API Pagination
Returning large collections in manageable pages using offset or cursor strategies
Overview
When a collection has thousands of items, returning them all at once is slow and wasteful. Pagination breaks results into smaller pages so clients fetch only what they need. The two common approaches are offset-based (page numbers) and cursor-based (a pointer to the next item), each with trade-offs.
Syntax / Usage
Offset pagination uses limit and offset (or page). Cursor pagination returns a token pointing to the next page.
# Offset-based
GET /products?limit=20&offset=40
# Page-based (a friendlier offset)
GET /products?page=3&limit=20
# Cursor-based
GET /products?limit=20&cursor=eyJpZCI6MTAwfQ
Examples
An offset response includes metadata so clients know the total and current page:
{
"data": [{ "id": 41, "name": "Item" }],
"pagination": { "limit": 20, "offset": 40, "total": 137 }
}
A cursor response returns an opaque token for the next request:
{
"data": [{ "id": 100, "name": "Item" }],
"pagination": { "nextCursor": "eyJpZCI6MTIwfQ", "hasMore": true }
}
Common Mistakes
- Returning entire collections with no pagination, causing slow responses
- Using large offsets on big tables, which get slower the deeper you page
- Skipping or duplicating rows when data changes during offset paging
- Not returning
totalorhasMore, so clients can't build navigation - Letting clients request unbounded
limitvalues with no maximum
See Also
api-design-rest-basics api-design-versioning api-design-http-methods