Pagination
Pagination is essential for working with larger data sets, where it is not feasible to fetch all data in a single call.
All top-level API resources that return a list of items always include pagination information in the response. Whether an endpoint also accepts pagination parameters on the request is documented per endpoint. If you paginate past the available items, the API returns an empty list.
The Storefront API offers three ways to paginate:
- Page-based Pagination - the default; ideal for numbered navigation and jumping to a specific page.
- Offset-based Pagination - for sequential reads where you want to vary the batch size between calls.
- Product-ID-based Pagination - for exporting or iterating over large product sets on
/v1/products.
Choosing a method
| Method | Best for | Can jump to arbitrary page? | Subject to the 500,000-item window? |
|---|---|---|---|
| Page-based | UI pagination, "page X of Y" navigation | Yes | Yes |
| Offset-based | Sequential reads with a changing batch size | No (sequential) | Yes |
| Product-ID-based | Full exports / iterating over all products at scale | No (sequential) | No |
Maximum result window. For page-based and offset-based pagination, you cannot reach beyond the first 500,000 items: page * perPage and offset + limit must each stay at or below 500000. To extract product sets larger than this - or simply for better performance on deep pagination - use product-ID-based pagination, which is not bound by this limit.
Page-based (page, perPage) and offset-based (offset, limit) parameters are mutually exclusive. Combining them in a single request returns a 400 validation error. See Error handling.
Page-based Pagination
All endpoints use page-based pagination by default when you don't specify otherwise.
| Parameter | Details |
|---|---|
page | The page you want to retrieve items for. |
perPage | How many items you want to retrieve per page. Type: Integer
|
Every paginated response includes a pagination object:
| Response Field | Details |
|---|---|
current | The number of items returned on the current page. This equals perPage unless there are fewer items left. For example, if you request the last page and only 10 items remain, current is 10.Type: Integer |
total | The total number of items available for this resource, regardless of pagination. Type: Integer |
perPage | The perPage value that was applied to the request.Type: Integer |
page | The page that was applied to the request.Type: Integer |
first | The first page. Always 1. Type: Integer |
prev | The previous page you can request. If you are on the first page, this is 1. Type: Integer |
next | The next page you can request. If you are on the last page, this equals the value in page.Type: Integer |
last | The last page you can request, derived from total and perPage. When there are no items, this is 1.Type: Integer |
Offset-based Pagination
Offset-based pagination gives you more flexibility, because the number of items you retrieve per call can vary.
Set offset to the number of items you have already retrieved, and adjust limit as needed while iterating.
| Parameter | Details |
|---|---|
limit | The maximum number of items to return. |
offset | The position from which to start returning items. Set it to the total number of items retrieved so far. |
Offset-based pagination is a REST API feature. The typed @scayle/storefront-api client always paginates by page (pagination: { page, perPage }), so use a direct REST request for offset/limit, or use page-based or product-ID-based pagination with the SDK.
Offset-based responses return only the total count:
| Response Field | Details |
|---|---|
total | The total number of items for this resource. |
Product-ID-based Pagination
Product-ID-based pagination is only available on the /v1/products endpoint. Use it whenever you iterate over large amounts of products. For example, an export, or a script that processes every product in a shop.
Instead of requesting pages, you use the ID of the last product you received as a cursor for the next request. This is far more performant than page-based or offset-based pagination for large data sets, and it is not bound by the 500,000-item window that otherwise limits /v1/products.
| Parameter | Details |
|---|---|
minProductId | Only products with an ID greater than or equal to this value are returned. |
page | The page you want to retrieve items for. |
perPage | How many items you want to retrieve per page. Type: Integer
|
To reliably iterate over every product, sort by product ID ascending so the cursor advances predictably:
- Start with an initial request using
minProductId=0,sort=id, andsortDir=asc. - Take the ID of the last product in the response and use that ID + 1 as the
minProductIdof the next request. - Repeat until a request returns an empty list. You have now retrieved every product in the shop.
Product ID is the Storefront API's default sort field, so the TypeScript example only needs to set the direction. sort: { direction: 'asc' } gives you ID-ascending order out of the box, fully typed and with no extra configuration. Just keep the request free of a search term, as that switches the default to relevance sorting.
Because it walks the data by ID rather than by position, product-ID-based pagination stays consistent even if products are added or removed while you iterate, unlike page/offset pagination, where inserts and deletes can shift items between pages.
Error handling
Invalid pagination parameters return a 400 response with a details array describing each violation:
Common causes:
| Situation | Result |
|---|---|
Mixing page/perPage with offset/limit in the same request | 400 "Can't use both offset-based and page-based pagination in the same request" |
perPage outside 1–1000, or not an integer | 400 "perPage must be between 1 and 1000", or "Unable to parse integer from perPage" |
limit outside 1–1000 | 400 "The limit parameter must be an integer between 1 and 1000" |
page * perPage exceeds 500000, or page not an integer | 400 "page must be between 1 and {n}", where {n} is 500000 / perPage rounded down (e.g. "...and 5000" at the default perPage=100); or "Unable to parse integer from page" |
offset + limit exceeds 500000 | 400 "The offset parameter must be an integer between 0 and {n}", where {n} is 500000 - limit (e.g. "...and 499900" at limit=100) |
| Paginating past the available items (but within the window) | 200 with an empty list, not an error |