Pagination & filtering
Cursor pagination
/v1/catalog is the one paginated endpoint here — the
complete filtered set. It uses opaque cursors, not page numbers or offsets:
| Parameter | Default | Max | Notes |
|---|---|---|---|
limit |
100 | 500 | Rows per page |
cursor |
none | — | Opaque — from a previous response's pagination.next_cursor |
Take next_cursor exactly as returned and send it back exactly as received.
Never construct one by hand, decode it, or edit it — its internal shape is
not part of the contract and can change without notice.
has_more
pagination.has_more tells you whether another page exists. When it's
false, next_cursor is null and you're done.
Stable ordering
Rows are ordered by sku within the filters you
requested, and that ordering doesn't shift under you mid-walk — a page can't be reshuffled by
data arriving between your requests.
Filters
The same six filters apply identically on /v1/sample, /v1/catalog, and
/v1/coverage — all optional:
| Parameter | Format |
|---|---|
retailer |
retailer slug |
category |
category slug |
department |
department slug |
rcd |
one or more retailer:category:department triples, comma-separated — shorthand for the three above |
start_date |
ISO 8601 (YYYY-MM-DD) |
end_date |
ISO 8601 (YYYY-MM-DD) |
start_date/end_date bound crawl_date, not the retailer's own change
date — see The Product object. A wrong retailer,
category, or department value fails with valid_options naming the real
ones — see Error handling.
Invalid cursor
A cursor that's expired, malformed, or wasn't issued by this API returns
invalid_cursor (400, not
retryable). There's exactly one recovery: restart
the walk from page one, without a cursor parameter. Don't try to repair a
bad cursor — it isn't decodable in a way that's meaningful to you.
A two-request walk
curl "https://api.stylumia.com/v1/catalog?retailer=home_depot&category=power_tools&limit=2" \
-H "Authorization: Bearer $STYLUMIA_KEY"
# Never commit your key — see /docs/key-security
{
"data": [
{ "product_id": "prd_5741354fa5d111f0a1ce7bcc", "sku": "1003097550", "retailer": "home_depot", "category": "power_tools", "...": "..." },
{ "product_id": "prd_9a3fddc7f5ec69b5913b5614", "sku": "1003097557", "retailer": "home_depot", "category": "power_tools", "...": "..." }
],
"pagination": { "limit": 2, "total_matching": 6, "next_cursor": "eyJvIjogMn0=", "has_more": true },
"meta": {
"request_id": "req_1a2b3c9f7e0d452186af",
"resolved": { "retailer": "home_depot", "category": "power_tools", "department": null, "rcd": null, "start_date": null, "end_date": null },
"coverage": { "combinations": 1, "matching_rows": 6, "corpus_rows": 2361, "earliest_date": "2026-06-14", "latest_date": "2026-08-14" }
}
}
pagination.limit and pagination.total_matching only appear on
/v1/catalog — total_matching is the size of the whole filtered set, not
just this page. Take next_cursor and send it back — with the same filters,
not just the cursor:
curl "https://api.stylumia.com/v1/catalog?retailer=home_depot&category=power_tools&limit=2&cursor=eyJvIjogMn0=" \
-H "Authorization: Bearer $STYLUMIA_KEY"
# Never commit your key — see /docs/key-security
The cursor only tracks your position in the result set — it decodes to an
offset, nothing more — it doesn't remember which filters produced it. Drop
or change a filter between calls and you'll walk a different set of rows
from wherever the cursor left off, silently, not as an error. Keep
retailer, category, department, rcd, start_date, and end_date
identical across a whole walk.