This guide builds the read side of a storefront: product listing pages, product detail pages and content pages. The examples use the TypeScript SDK and work in any server runtime — Next.js, Astro, Remix, a build script. The full endpoint reference is at api.minipim.com/docs.

1. Get a key and a client

In the admin, open System → API keys, create a key, and store it as a server-side environment variable. It’s a secret: never ship it to the browser.

npm install @minipim/sdk
// lib/pim.ts
import { createMinipimClient } from '@minipim/sdk';

export const pim = createMinipimClient({
  baseUrl: 'https://api.minipim.com', // or your self-hosted API
  apiKey: process.env.MINIPIM_API_KEY!,
});

The key carries its organization, so there’s nothing else to configure.

2. Listing pages

const { data } = await pim.GET('/v1/products', {
  params: { query: { status: 'active', categoryId, includeDescendants: true, limit: 48, offset: 0 } },
});
// data = { data: Product[], limit, offset, hasMore }
  • Always pass status: 'active' — lists include drafts and archived products otherwise.
  • Pages use limit (up to 200) and offset; hasMore tells you whether there’s another page.
  • includeDescendants: true makes a parent category show everything beneath it.
  • Unknown query parameters are rejected with a 422, which catches typos early.

3. Product detail by slug

Product detail is fetched by id, so resolve the URL slug with the list filter first:

const { data: list } = await pim.GET('/v1/products', {
  params: { query: { slug: params.slug, status: 'active', limit: 1 } },
});
const id = list?.data[0]?.id;
if (!id) return notFound();

const { data: product } = await pim.GET('/v1/products/{id}', {
  params: { path: { id }, query: { locale: 'en_US', channel: 'web' } },
});

Passing locale and/or channel adds resolvedAttributes: one value per attribute, picked by the closest match — exact locale and channel, then locale only, then channel only, then the unscoped value. Render from resolvedAttributes and you don’t have to handle the stored per-locale shape yourself.

Money comes back as { amount_cents, currency } — format it with the SDK’s money helpers rather than dividing floats.

4. Variants

Variants are a separate call:

const { data: variants } = await pim.GET('/v1/products/{id}/variants', {
  params: { path: { id } },
});

Each variant has its SKU and only the values it overrides; anything missing inherits from the product.

5. Content pages

const { data: page } = await pim.GET('/v1/content/by-slug/{slug}', {
  params: { path: { slug }, query: { status: 'active' } },
});

Pass status: 'active' here too — the slug lookup returns drafts otherwise. Bodies are Markdown; render them with whatever Markdown component you use.

6. Static builds

For a full static build, the SDK’s paginate helper walks every page for you:

import { paginate } from '@minipim/sdk';

for await (const product of paginate(pim, '/v1/products', { query: { status: 'active' } })) {
  // generate a page
}

Next

Make the site update when the catalog changes: keep a storefront in sync with webhooks.