Pagination

Walk through any list with a cursor. Pages that stay correct while data changes, cost the same at any depth, and a copy-paste loop.

Every list endpoint is cursor-paginated the same way. A cursor marks a position in the list's order, so pages stay correct while data changes between your requests, and a page costs the same however deep it is.

Parameters

ParameterTypeDefaultDescription
limitinteger50Page size, from 1 to 100.
cursorstringnoneThe meta.nextCursor of the previous page. Leave it out for the first page.

The meta object

{ "limit": 100, "hasMore": true, "nextCursor": "ZTU2M2ZlYzMtM2Ix…" }
FieldDescription
limitThe page size that was applied.
hasMoretrue if at least one more item exists after this page.
nextCursorPass it as cursor to get the next page. null on the last page.

How to page

  1. Request the first page without cursor.
  2. While meta.hasMore is true, request again with cursor=<meta.nextCursor>.
  3. Stop when hasMore is false.

Keep every other parameter identical across pages. Changing a filter between pages with the same cursor is not supported.

# First page
curl "https://api-integrations-next.vitruve.fit/v1/workouts?userId=ATHLETE_ID&limit=100" \
  -H "X-API-Key: CUSTOMER_INTEGRATION_KEY" -H "X-Partner-Secret: YOUR_PARTNER_SECRET"

# Next page: same filters, plus the cursor from meta.nextCursor
curl "https://api-integrations-next.vitruve.fit/v1/workouts?userId=ATHLETE_ID&limit=100&cursor=NEXT_CURSOR" \
  -H "X-API-Key: CUSTOMER_INTEGRATION_KEY" -H "X-Partner-Secret: YOUR_PARTNER_SECRET"

How a cursor works

A cursor holds the sort value and the id of the last row of a page: a position in the list's order, not a row. So:

  • The next page stays correct when that last row is changed, filtered out or deleted before you ask for it.
  • New rows don't shift later pages. Rows added before the position never push rows you haven't read yet onto a page you already read.
  • A row whose sort date changes moves. A pending workout completed while you page moves to its new place in the order, so you may see it twice, or not in this pass. Store rows by id.
  • Depth is free. The thousandth page costs the same as the first.

Rules worth knowing

  • Cursors are opaque. Do not parse, build or edit them.
  • A cursor belongs to one customer and one list. A cursor served with another customer's key, or for another list (an endpoint sorted another way), answers 400.
  • Cursors issued before 2026-10 answer 400. Start again from the first page.
  • Do not stop on a short page. Use hasMore. A page can be shorter than limit only on the last page, but hasMore is the contract.
  • Use limit=100 for exports. It needs the fewest requests against each customer's rate limit.
  • Order is fixed per endpoint and always ends in id, so equal sort values are never ambiguous. The default order of each endpoint is stated in the reference.

Errors

StatusWhen
400limit outside 1–100 or not an integer, or the cursor is invalid: edited, served with another customer's key or for another list, or issued before 2026-10.

On this page