Skip to main content
List routes return { data, hasMore, nextCursor } and you page by passing nextCursor back unmodified. Cursors are keyset, not offset. A row inserted while you are paging cannot shift the window and make you skip or repeat an item, which is the failure offset pagination has and this does not.

The envelope

There is no total count. Counting every matching row on every request is a cost you would pay on every page for a number almost no caller uses.

Parameters

pageSize clamping is worth noting: asking for 500 gives you 100 and a 200, not a 422. Read hasMore rather than inferring the end of the list from the number of rows you received.

Paging through a list

Cursors are opaque

A cursor encodes the position of the last row you were served. Treat it as a token: pass it back exactly as received, do not decode it, do not construct one, and do not carry one between routes. Any tampering fails with 400 invalid_cursor: bad base64, bad JSON, a missing key, or a timestamp that does not parse.

Which routes paginate

Two lists deliberately do not paginate. GET /v1/import is a fixed window of the 20 most recent imports, and GET /v1/keys returns the caller’s keys in full.

Recall pagination is opt-in

GET /v1/recall/{recall_type} serves four different shapes from one route, and only two of them are lists.
  • insights and temporal accept cursor and pageSize.
  • identity and graph are composite documents. Sending either parameter returns 400 pagination_unsupported, with the supported views named in suggestedAction.
Send neither parameter and the response keeps its original, unpaginated shape. That is why this is described as opt-in: pagination changes the response, so it only happens when you ask for it.

Next

Recall

The four recall views and what each one returns.

Errors

The problem envelope and the full code catalogue.