Skip to content

Pagination & fields

Every endpoint that returns a collection uses one shape, and every one of them supports trimming the payload down to the fields you actually render.

The list shape

json
{
  "status": true,
  "status_code": 200,
  "message": "",
  "data": {
    "items": [ /* … */ ],
    "next_cursor": "eyJ2IjoxLCJzIjoibmV3ZXN0Iiwi…",
    "has_more": true
  }
}

Some endpoints add a sibling next to items — product reviews include summary, and category products include category — but items, next_cursor and has_more are always present.

Endpoints that return a small, complete set (categories, payment methods, curated lists like top products) use the same shape with next_cursor: null and has_more: false, so one code path handles every list.

Walking pages

Send next_cursor back as cursor, and stop when has_more is false:

js
async function allProducts(baseUrl, key) {
  const out = [];
  let cursor = null;

  do {
    const url = new URL(`${baseUrl}/api/v1/business/website/products/`);
    url.searchParams.set("limit", "50");
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await fetch(url, { headers: { "X-Periscale-Key": key } });
    if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);

    const { data } = await res.json();
    out.push(...data.items);
    cursor = data.has_more ? data.next_cursor : null;
  } while (cursor);

  return out;
}

Why cursors instead of page numbers

Pagination is keyset-based, not offset-based. Pages stay correct while the catalog changes underneath you: with ?page=2, a product added during the crawl shifts everything down and you silently get a duplicate or skip an item. A cursor points at a position in the sort order, so that cannot happen.

Two rules:

  • A cursor belongs to its sort. It encodes the position within a specific ordering, so changing sort mid-walk is rejected — start again from page one.
  • Cursors are opaque. Treat them as strings. Do not parse, build, or persist them long-term; the encoding may change.

An invalid or tampered cursor returns 400 with error_code: validation_failed.

Limits

Endpoint groupDefaultMax
Products, category products, flash deals, bundles24100
Reviews2050
Blog posts20100

Values above the maximum are clamped rather than rejected.

Selecting fields

Data endpoints accept fields — a comma-separated list of top-level fields:

GET /api/v1/business/website/products/?fields=id,name,price,images
json
{ "data": { "items": [
  { "id": 148, "name": "Sweet Orange Marmalade", "price": "1700.00", "images": [] }
], "next_cursor": null, "has_more": false } }
  • id is always included, whether or not you ask for it.
  • Unknown field names are ignored, so a typo yields fewer fields rather than an error.
  • Omit fields to get the full object.

This is not only a smaller response. Asking for fewer fields also skips the work that would have populated the omitted ones — a product list without variants or reviews avoids those lookups entirely, which is a real speed-up on a large catalog. Listing pages that only need a card's worth of data benefit most.

Combine with pagination

?fields=id,slug,name,price&limit=100 is the fastest way to walk a full catalog — for example to build a sitemap or a search index.

© Periscale