Pagination
Page through lists with page and perPage, read links and meta, use cursors on merchant lists, and sync CRM records incrementally with filter[updated_since].
Lists that can grow come back one page at a time. Storefront lists use numbered pages. Merchant lists, such as CRM contacts and orders, use cursors, which stay correct while records are being added and changed.
Numbered pages
Storefront lists take two query parameters:
| Parameter | Default | Range |
|---|---|---|
perPage | 24 | 1 to 100. Larger values are capped at 100. |
page | 1 | 1 and up |
They apply to GET /v1/products, GET /v1/collections, GET /v1/collections/{slug}/products, GET /v1/search and a signed-in customer's GET /v1/orders. Note the camel case: perPage, not per_page.
curl -G https://api.elaraone.app/api/v1/products \
-H "X-Tenant-Key: $ELARAONE_TENANT_KEY" \
-H "X-Tenant-Secret: $ELARAONE_TENANT_SECRET" \
--data-urlencode "perPage=48" \
--data-urlencode "page=2"The response carries links and meta next to data:
{
"data": [ … ],
"links": {
"first": "https://api.elaraone.app/api/v1/products?perPage=48&page=1",
"last": "https://api.elaraone.app/api/v1/products?perPage=48&page=3",
"prev": "https://api.elaraone.app/api/v1/products?perPage=48&page=1",
"next": "https://api.elaraone.app/api/v1/products?perPage=48&page=3"
},
"meta": {
"current_page": 2,
"from": 49,
"last_page": 3,
"per_page": 48,
"to": 96,
"total": 131,
"path": "https://api.elaraone.app/api/v1/products"
}
}links.nextisnullon the last page, andlinks.prevon the first.meta.totalandmeta.last_pageare what you need for page numbers.- The links keep your other query parameters, such as filters and sorts.
- A page past the end returns an empty
datalist, not an error.
Lists that are always short are not paginated: GET /v1/promotions, GET /v1/countries, GET /v1/cart/shipping-options and GET /v1/account/addresses return everything at once.
Cursors on merchant lists
The merchant CRM lists (contacts, companies, tasks and their related records) and the merchant orders list use cursor pagination:
| Parameter | Meaning |
|---|---|
perPage | 1 to 100, default 24. A value outside the range returns 422. |
cursor | meta.next_cursor or meta.prev_cursor from the previous page |
sort | A sort the list allows, with - for descending |
{
"data": [ … ],
"links": { "first": null, "last": null, "prev": null, "next": "https://api.elaraone.app/api/v1/merchant/crm/contacts?cursor=eyJ…" },
"meta": {
"path": "https://api.elaraone.app/api/v1/merchant/crm/contacts",
"per_page": 24,
"next_cursor": "eyJ…",
"prev_cursor": null
}
}Keep requesting with cursor set to meta.next_cursor until it is null. Treat cursors as opaque: pass them back exactly as you received them. Cursor lists have no total.
An unknown filter[…] or sort on these lists returns 400; a malformed value, such as a date that cannot be read, returns 422 VALIDATION_FAILED.
Incremental sync with filter[updated_since]
To keep another system in step with the store's CRM, do not re-read everything on every run. The CRM contacts, companies and tasks lists take filter[updated_since], an ISO 8601 time, and return only records changed at or after it.
Run a full sync once
Page through the whole list with sort=updated_at and store each record. Note the time you started.
Then fetch only what changed
On each later run, ask for records changed since the start of the previous run, and page through them the same way.
Move the mark forward
When the run finishes, save its start time as the next run's updated_since.
curl -G https://api.elaraone.app/api/v1/merchant/crm/contacts \
-H "X-Tenant-Key: $ELARAONE_TENANT_KEY" \
-H "X-Tenant-Secret: $ELARAONE_TENANT_SECRET" \
-H "Authorization: Bearer $ELARAONE_MERCHANT_API_KEY" \
--data-urlencode "filter[updated_since]=2026-10-23T02:00:00Z" \
--data-urlencode "sort=updated_at" \
--data-urlencode "perPage=100"- Use the start of the previous run, not its end, so a record changed while the run was in progress is picked up next time. You may see a few records twice; make your writes idempotent by record id.
- A tag change counts as a change to the contact.
- Pair the sync with CRM change webhooks if you want to hear about changes as they happen, and keep the sync as a safety net.
- Stay inside the store's API budget: see rate limits.
Responses and errors
The success and error envelopes, every error code with its HTTP status and what to do, the shape of validation errors, and which errors to retry.
Idempotency keys
Retry requests that change things without doing them twice, which routes accept an Idempotency-Key, and how to generate keys and retry safely.