Pagination

Cursor pagination and incremental sync crawls.

The lists that can grow large use cursor pagination (products, listings, orders, events, operations, actions, ignored listings, companies, storefront orders):

curl "https://api.nembol.com/rest/v1/products?limit=50" \
  -H "Authorization: Bearer $NEMBOL_API_KEY"
{
  "data": [ { "id": "…" } ],
  "has_more": true,
  "next_cursor": "eyJ1IjoiMjAyNi0wNy0yNFQx…"
}

Pass next_cursor back as ?cursor= to get the next page. When has_more is false, you're done. limit is 1–100 (default 25). Cursors are opaque. Don't parse or store them long-term. A cursor Nembol didn't give (truncated, edited, empty) answers 422 validation_failed with param: cursor: it is never read as the first page.

Lists without a cursor

A few lists are small by nature, or capped, and come back in one response. They have no limit / cursor; where has_more is present it is always false.

ListHow it pages
GET /customersPage-based: page (from 1), limit 1–100, total in the answer
GET /promotionsThe 200 most recent. Older ones are still reachable by id (GET /promotions/{id})
GET /notificationsThe 200 most recent
GET /storefront/discounts, GET /storefront/bundlesAll of them, in one response
GET /channel_accounts, GET /webhook_endpoints, GET /redirect_urls, GET /error_fixesAll of them, in one response (a store has a handful)
GET /sales, GET /catalog/diagnosticsReports: one object, no list

Ordering guarantee

Lists are ordered newest first with a stable id tiebreak: products and listings by updated_at, orders by created_at (the time the order was placed), events by created_at. This order is part of the contract. Pages never skip or repeat items within a single crawl.

Incremental sync

To keep an external system in sync without re-reading everything, crawl with updated_after:

curl "https://api.nembol.com/rest/v1/products?updated_after=2026-07-24T00:00:00Z&limit=100" \
  -H "Authorization: Bearer $NEMBOL_API_KEY"

Record the timestamp you started the crawl at, page through to the end, then use that timestamp as the next crawl's updated_after. Deleted and merged products leave the default list: to catch them in a crawl, also read status=archived&updated_after=… (or use the product.deleted webhook). (For push-based sync, use webhooks instead. Crawling is the reconciliation fallback.)

Orders that changed. GET /orders?updated_after=… returns the orders placed or changed (status, fulfillment, tracking: e.g. cancelled or refunded on the channel) after that time; each order's updated_at says when it last changed. Poll with the time of your previous poll. For push, use the order.created and order.status_changed webhooks.

Last updated on October 18, 2026

Need help?

Email us and a human replies, usually within one business day.

On this page