Currently Available: Need a skilled Software Developer for your next project?
Categories
APIs

Documenting Pagination in API Contracts

When a client reads a large API collection, the server returns the records in portions called pages. The pagination contract tells the client how to request each portion, how to continue, and how to recognize the end. Clear rules matter because a client that guesses at ordering, changes a continuation value, or treats a short page as the last page can miss or repeat records.

No single protocol defines every part of pagination. Standards such as RFC 8288 define how HTTP links work. API specifications and provider guidelines describe different ways to divide results into pages. An integrator should follow the specific API’s contract for result order, continuation values, page size, links, and errors. The server’s internal pagination method does not tell the client how to use the response.

Define the order before the page boundary

The contract should specify the default sort order and the sort parameters the API supports. The sort order determines which records appear on each page. If several records have the same sort value, the API should use a tie-breaker that gives each record a unique position, such as a record ID. The JSON:API Cursor Pagination profile describes this approach.

A stable order does not keep the collection unchanged while the client reads it. Microsoft’s Graph collections guidance warns that adding or removing records during this time can still cause the client to miss or repeat records. The contract should say whether the API promises only a stable order or also gives the client a consistent snapshot, a fixed view of the collection while it reads the pages.

When integrating with the API, check how it resolves ties and whether the requested sort remains in effect across pages. Do not assume that a cursor prevents every duplicate or omission when records change.

Explain what the client sends to continue

A cursor or page token is a value that tells the server where to resume. APIs represent continuation in different ways: a token, an existing record’s identifier, or a complete URL. The contract should identify which form the API uses and tell the client whether to send it back unchanged.

Google’s AIP-158 requires page tokens in its scope to be opaque, meaning clients should not parse or interpret them. Microsoft Graph similarly instructs clients to use the full @odata.nextLink URL without changing its query options. Stripe documents a different provider-specific pattern: clients pass an existing object ID through starting_after or ending_before.

Unless the API explicitly documents another rule, treat the continuation value as opaque. Do not decode it, construct a replacement, or alter query parameters embedded in a returned URL. A contract should also state whether the token expires and what the client should do if the server rejects it. Token lifetime and recovery behavior are API-specific.

State how page size works

The page-size parameter asks the server for up to a certain number of records. The server does not have to return exactly that number. The contract should give the default and maximum page sizes, explain what happens when the client asks for too many records, and say whether the server can return fewer records than requested.

GitHub says that most REST endpoints cap per_page at 100 and reduce larger requests to that limit; the endpoint documentation supplies the applicable default and maximum. By contrast, the JSON:API Cursor Pagination profile specifies a 400 Bad Request response for a size above the server-defined maximum. Clients should not infer rejection or clamping from a parameter name such as limit or page_size.

A short page does not always mean the collection has ended. Under AIP-158, a service may return fewer records than requested, including zero, while more results remain. The client should use the documented continuation signal rather than count the records in the current response.

Tell clients where to find the next page

RFC 8288 defines Web Linking, including relation types, labels such as next that describe what a link points to. It does not prescribe one format for pagination responses. The contract must say where the link or token appears and how the client uses it.

For example, GitHub can return pagination URLs in the HTTP Link header, with relations such as next and prev. JSON:API places pagination links in a response-body links object. Microsoft Graph returns a body property named @odata.nextLink, while Google AIP-158 uses a next_page_token field. These formats are alternatives, not interchangeable conventions.

The contract should say how the client knows there are no more results. The format might signal the end by omitting the next link or returning an empty token. It might use another documented signal instead. GitHub also notes that some relations, such as last, might be missing when the server cannot calculate them. Clients should follow the links the server returns instead of building the next request themselves.

Document invalid requests and end conditions

Pagination errors have no universal status code or recovery rule. The contract should say what happens when a client gives an invalid page size, asks to sort by a field the API does not support, changes query arguments between pages, or sends a cursor the server cannot use.

For APIs following AIP-158, a negative page size and changes to non-page-size request arguments between pages produce INVALID_ARGUMENT. The JSON:API Cursor Pagination profile specifies 400 responses for invalid page sizes, including sizes above the maximum. It also specifies them for unsupported sorts. Microsoft’s Graph collections guidance says the server must return an error if it cannot honor $top or $skip, rather than silently ignoring those options. GitHub’s documented behavior for an excessive per_page value is to reduce it to the maximum.

The contract should distinguish an empty result from an error. Microsoft’s guidance treats an empty filtered collection as a valid 200 response. It should also explain what clients should do when a continuation token expires or becomes invalid; the response code and restart procedure depend on the API.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *