PRICIUM
All posts
nextjsapi integrationproduct datadeveloper guideapp router

The Complete Guide to Integrating Product Data APIs into Next.js Applications

A step-by-step guide to integrating a real-time product data API into a Next.js 16 App Router application - from API routes to ISR caching to UI components.

Aman Patel

Aman Patel

Founder & CEO

2026-03-28 10 min read

Why Next.js Is the Right Framework for E-commerce Data Apps

Next.js 16 with the App Router is an excellent choice for building e-commerce data applications. Its Server Components, API Routes, and built-in ISR (Incremental Static Regeneration) give you:

  • Server-side data fetching without exposing your API keys to the client
  • Caching to reduce API calls and improve performance
  • Static page generation for SEO-critical product pages
  • Edge runtime support for ultra-low-latency responses globally

This guide walks through a complete integration of the Pricium product data API into a Next.js 16 application.

Project Setup

npx create-next-app@latest ecommerce-app --typescript --app --src-dir
cd ecommerce-app
npm install axios zod

Add your API key to .env.local:

PRICIUM_API_KEY=pk_live_...

Step 1: Create a Type-Safe API Client

Create src/lib/pricium.ts:

import { z } from 'zod';

const VariationSchema = z.object({
  size: z.string().optional(),
  color: z.string().optional(),
  config: z.string().optional(),
  price: z.number(),
  currency: z.string(),
  available: z.boolean(),
  rating: z.number().optional(),
  review_count: z.number().optional(),
});

const ProductDataSchema = z.object({
  product_title: z.string(),
  source_url: z.string(),
  variations: z.array(VariationSchema),
  geo_pricing: z.record(z.object({
    price: z.number(),
    currency: z.string(),
  })).optional(),
  scraped_at: z.string(),
});

export type ProductData = z.infer<typeof ProductDataSchema>;
export type Variation = z.infer<typeof VariationSchema>;

export async function fetchProductData(
  url: string,
  location: string = 'US'
): Promise<ProductData> {
  const response = await fetch('https://api.pricium.store/product-detail', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${process.env.PRICIUM_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ url, location }),
    // Next.js 16 cache control
    next: { revalidate: 300 }, // Cache for 5 minutes (ISR)
  });

  if (!response.ok) {
    throw new Error(`Pricium API error: ${response.status}`);
  }

  const data = await response.json();
  return ProductDataSchema.parse(data);
}

Step 2: API Route for Client-Side Fetching

Create src/app/api/product/route.ts:

import { NextRequest, NextResponse } from 'next/server';
import { fetchProductData } from '@/lib/pricium';

export async function POST(req: NextRequest) {
  try {
    const { url, location } = await req.json();

    if (!url || typeof url !== 'string') {
      return NextResponse.json({ error: 'Valid URL is required' }, { status: 400 });
    }

    const data = await fetchProductData(url, location || 'US');
    return NextResponse.json(data);
  } catch (error) {
    console.error('Product fetch error:', error);
    return NextResponse.json({ error: 'Failed to fetch product data' }, { status: 500 });
  }
}

Step 3: Server Component - Static Product Page

Create src/app/product/page.tsx for a Server Component that renders with ISR:

import { fetchProductData } from '@/lib/pricium';
import { VariationsTable } from '@/components/VariationsTable';

interface Props {
  searchParams: { url?: string; location?: string };
}

export default async function ProductPage({ searchParams }: Props) {
  const { url, location = 'US' } = searchParams;

  if (!url) {
    return <div className="p-8 text-center">Paste a product URL to get started.</div>;
  }

  const product = await fetchProductData(url, location);

  return (
    <main className="max-w-5xl mx-auto p-8">
      <h1 className="text-3xl font-bold mb-2">{product.product_title}</h1>
      <p className="text-gray-500 mb-6 text-sm">
        Data as of {new Date(product.scraped_at).toLocaleString()} ·{' '}
        <a href={product.source_url} target="_blank" rel="noopener" className="underline">
          View on source
        </a>
      </p>
      <VariationsTable variations={product.variations} />
    </main>
  );
}

Step 4: Variations Table Component

Create src/components/VariationsTable.tsx:

import { Variation } from '@/lib/pricium';

interface Props {
  variations: Variation[];
}

export function VariationsTable({ variations }: Props) {
  const sorted = [...variations].sort((a, b) => a.price - b.price);

  return (
    <div className="overflow-x-auto">
      <table className="w-full text-sm border-collapse">
        <thead>
          <tr className="bg-gray-50 text-left">
            <th className="p-3 font-semibold border-b">Size</th>
            <th className="p-3 font-semibold border-b">Color</th>
            <th className="p-3 font-semibold border-b text-right">Price</th>
            <th className="p-3 font-semibold border-b text-center">Stock</th>
            <th className="p-3 font-semibold border-b text-center">Rating</th>
          </tr>
        </thead>
        <tbody>
          {sorted.map((v, i) => (
            <tr
              key={i}
              className={`border-b transition-colors ${
                !v.available ? 'opacity-40' : 'hover:bg-blue-50'
              }`}
            >
              <td className="p-3">{v.size || '-'}</td>
              <td className="p-3">{v.color || '-'}</td>
              <td className="p-3 text-right font-medium">
                {v.currency} {v.price.toFixed(2)}
              </td>
              <td className="p-3 text-center">
                {v.available ? (
                  <span className="text-green-600 font-medium">In Stock</span>
                ) : (
                  <span className="text-red-500">Out of Stock</span>
                )}
              </td>
              <td className="p-3 text-center">{v.rating ?? '-'}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Step 5: Environment Variables for Production

For Vercel deployment:

vercel env add PRICIUM_API_KEY

The next: { revalidate: 300 } option in the fetch call automatically enables ISR - pages are cached for 5 minutes and then regenerated on the next request.

What You've Built

A Next.js app that:

  • Fetches variation-level product data server-side (no client API key exposure)
  • Caches results with ISR to minimize API calls
  • Renders a clean, sortable variations table
  • Is ready to deploy on Vercel in minutes

Get your Pricium API key and start building →

Aman Patel

Written by Aman Patel

Founder & CEO at Pricium