Skip to content

Pagination & sync

Listing endpoints in the MendriX REST API use cursor-based pagination and support incremental sync with a since= parameter. Both are described in the API reference; this page explains the pattern.

ParameterMeaningDefaultMax
limitPage size1001000
cursorOpaque pointer to the next page——
sortSort field; prefix with - for descendingendpoint default—

A few endpoints cap limit lower because their responses carry more per item — purchase line items defaults to 10 and caps at 100. The per-endpoint values are in the reference; a larger limit is accepted but clamped.

To page through a listing, request the first page, then keep following the cursor returned with each response until no further cursor is given.

Terminal window
# First page
curl "https://<your-client-name>.api.mendrix.cloud/api/client/clients/?limit=100" \
-H "Authorization: Bearer ACCESS_TOKEN"
# Next page — pass the cursor returned by the previous response
curl "https://<your-client-name>.api.mendrix.cloud/api/client/clients/?limit=100&cursor=NEXT_CURSOR" \
-H "Authorization: Bearer ACCESS_TOKEN"
const BASE = 'https://<your-client-name>.api.mendrix.cloud/api';
const TOKEN = process.env.MENDRIX_ACCESS_TOKEN; // from login-api-token
// Yields every item; stamps the cycle's maxSequence on `state` for the next sync.
async function* listAll(path, params = {}, state = {}) {
let cursor = '';
let hasMore = true;
while (hasMore) {
const qs = new URLSearchParams({ limit: '1000', ...params });
if (cursor) qs.set('cursor', cursor);
const res = await fetch(`${BASE}${path}?${qs}`, {
headers: { Authorization: `Bearer ${TOKEN}`, Accept: 'application/json' },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { data } = await res.json();
for (const item of data.items ?? []) yield item;
// Follow the cursor until the API says there is no more.
cursor = data.nextCursor;
hasMore = data.hasMore;
state.maxSequence = data.maxSequence;
}
}
for await (const client of listAll('/client/clients/')) {
console.log(client.id);
}

For synchronisation, use since= to fetch only records changed since your last run, instead of re-reading everything each time.

  • since= is a sequence number, not a date: pass back the maxSequence the API returned in the envelope of your previous cycle. It is exclusive — you get the records whose sequence is greater than the value you pass.
  • Only a request carrying since= returns a watermark you can persist. With since=, maxSequence is a snapshot frozen at the start of the cycle, identical on every page. Without it, maxSequence is just the highest sequence among the items on the page you happen to have fetched (and is omitted when the page carries none) — informational, not a high-water mark.
  • Bootstrap the first cycle with since=1: it returns everything and hands you a real snapshot to store.
  • maxSequence is constant across a cycle, so persist it once, after the last page (hasMore is false). Tracking a per-page maximum is unnecessary.
  • since= requires the endpoint’s default sort (by id). Combining it with a custom sort returns 400.
GET /client/clients/?since=41207&limit=1000
GET /client/clients/?since=1&limit=1000 # first cycle: everything, plus a watermark
// Pseudocode for a recurring sync job.
const since = loadWatermark() || 1; // sequence number; 1 on the first run ever
const state = {};
for await (const record of listAll('/client/clients/', { since }, state)) {
upsertIntoLocalStore(record); // your side
}
// Trustworthy because this walk passed since=; a walk without it reports only
// the last page's maximum.
if (state.maxSequence) saveWatermark(state.maxSequence);

Tips

  • Run on a schedule (e.g. every few minutes) rather than continuously.
  • Always use the largest sensible limit (up to 1000) to reduce round trips.
  • Make your upsert idempotent — a record may be seen again if it changes on a boundary, and some endpoints re-deliver a whole group when one member changes.
  • Deletions need care. Hard-deleted rows are not surfaced by since=. Where an endpoint supports ?status=deleted, soft-deletes do bump the sequence and show up in the next sync; otherwise, run a periodic full re-read to notice removals.
  • For near-real-time updates, combine polling with webhooks: react to webhook events immediately and use the since= sweep as a safety net.