Designing predictable REST API pagination
Pagination is part of an API contract. Offset and limit are simple, but concurrent inserts and deletions can cause clients to miss or repeat records unless ordering and consistency are defined.
Define a deterministic order
Select an immutable or consistently ordered key and add a unique tie-breaker. Do not paginate a collection whose database order is unspecified, even when current results appear stable.
Choose offset or cursor by workload
Offset pagination supports direct page numbers and modest administrative datasets. Cursor pagination is better for large or changing feeds because the next position is based on the final returned key rather than rows counted again.
Make cursors opaque
Encode the required ordering values and query context, protect them from tampering where necessary and allow clients to return the token unchanged. Do not promise that its internal representation remains stable.
Return navigational metadata
Include the applied page size and links or tokens for available movement. Total count can be expensive or rapidly stale; provide it only where the product requires and document its consistency.
Test concurrent change
Insert, update and delete records between page requests and verify documented behaviour. Apply maximum page sizes, validate filters and ensure permission changes cannot expose records through a reused cursor.
Pagination order, filter and authorisation form one query state; a cursor from one state must not be accepted silently in another.