API ReferencePagination

Pagination

How list endpoints page, sort, and filter results.

Envelope

Every list endpoint returns the same envelope:

{
  "data": [
    /* items */
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 1247,
    "totalPages": 63
  }
}
  • data — array of resource objects, never null
  • meta.page — 1-indexed current page
  • meta.limit — page size actually applied, which may be lower than what you asked for (see the cap below)
  • meta.total — total count across all pages
  • meta.totalPagesMath.ceil(total / limit)

Query params

GET /v1/products?page=2&limit=100&search=shirt&sortBy=createdAt&sortOrder=desc
ParamTypeDefaultNotes
pagenumber11-indexed
limitnumber20Hard cap 100. A larger value is silently clamped, not rejected
searchstringSubstring match across name + key text fields
sortBystringper-endpointAllowlisted per endpoint — an unrecognised value is ignored, not rejected
sortOrderstringper-endpointasc or desc

The cap is 100 and it does not error. ?limit=200 returns 100 rows with meta.limit: 100. Nothing in the response says you asked for more. Any loop that decides it has reached the end by comparing data.length against the limit it requested will stop after the first page — see the iteration example below.

Sorting

Each endpoint allows a specific set of sortBy values. Products, for example, accepts name, createdAt, updatedAt, menuOrder, status, sku, slug, basePrice (plus the aliases pricebasePrice), defaults to menuOrder, and defaults sortOrder to asc.

An unsupported sortBy value is not an error. There is no 400 and no error code — the server falls back to its own default ordering (createdAt on Products) and returns 200. If your integration depends on a particular order, assert on the data you get back rather than trusting that a typo'd sort field would have failed loudly.

Iterating through all results

For backfills and exports, drive the loop from meta.totalPages only:

let page = 1;
while (true) {
  const res = await client.getProducts({ page, limit: 100 }); // 100 is the cap
  for (const p of res.data) await handle(p);
  if (page >= res.meta.totalPages) break;
  page++;
}

Do not add res.data.length < limit as a break condition. If you requested more than 100 it is true on page 1 and the backfill silently skips the rest of the catalog.

There is no iterate() / async-generator helper in the SDK today — write the loop above.

No cursor pagination

Every list endpoint is page-based. There is no cursor query parameter and no meta.nextCursor field anywhere on the public API — if you have seen those in an older version of this page, they were never implemented.

The practical consequence: paging is not stable under concurrent writes. Rows inserted or deleted while you iterate can shift items across page boundaries, so a long backfill can repeat or miss a row. Two mitigations that do work today:

  • Sort by an immutable field (createdAt) rather than one that changes (updatedAt, menuOrder).
  • Make the consumer idempotent — key on the resource id and upsert, so a repeated row is harmless.

Filtering

Filters are endpoint-specific — see each endpoint's docs page. Common patterns:

  • ?status=active — exact match
  • ?categories=cat_a,cat_b — comma-separated OR
  • ?minPrice=10&maxPrice=50 — numeric ranges
  • ?search=shirt — substring match