Pagination Patterns for GET Requests
Explore common pagination strategies for REST API GET requests including offset, cursor, and keyset pagination with proper HTTP headers.
Detailed Explanation
Why Paginate?
Returning thousands of records in a single GET response is slow and memory-intensive. Pagination splits large collections into manageable pages.
Offset-Based Pagination
The simplest approach uses page and limit (or offset and limit) query parameters:
GET /api/users?page=3&limit=20 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json
X-Total-Count: 542
{
"data": [ ... 20 users ... ],
"meta": {
"page": 3,
"limit": 20,
"totalPages": 28,
"totalCount": 542
}
}
Pros: Simple, supports jumping to any page Cons: Inconsistent with concurrent inserts/deletes, slow on large offsets (database scans)
Cursor-Based Pagination
Uses an opaque cursor (usually a base64-encoded ID or timestamp) instead of a page number:
GET /api/users?limit=20&after=eyJpZCI6NDB9 HTTP/1.1
HTTP/1.1 200 OK
{
"data": [ ... 20 users ... ],
"cursors": {
"after": "eyJpZCI6NjB9",
"before": "eyJpZCI6NDF9",
"hasNext": true,
"hasPrevious": true
}
}
Pros: Consistent even with concurrent changes, fast regardless of position Cons: Cannot jump to a specific page, clients must traverse sequentially
Link Header (RFC 8288)
The standard way to communicate pagination links:
HTTP/1.1 200 OK
Link: </api/users?page=4&limit=20>; rel="next",
</api/users?page=2&limit=20>; rel="prev",
</api/users?page=1&limit=20>; rel="first",
</api/users?page=28&limit=20>; rel="last"
Choosing a Strategy
| Factor | Offset | Cursor | Keyset |
|---|---|---|---|
| Jump to page N | Yes | No | No |
| Consistent results | No | Yes | Yes |
| Performance at scale | Slow | Fast | Fast |
| Implementation | Simple | Moderate | Moderate |
| Best for | Small datasets, admin UIs | Feeds, timelines | Large sorted datasets |
Keyset Pagination
A variation of cursor-based that uses actual column values:
GET /api/users?limit=20&created_after=2026-01-15T10:00:00Z&id_after=42 HTTP/1.1
This translates directly to an efficient SQL query:
SELECT * FROM users
WHERE (created_at, id) > ('2026-01-15T10:00:00Z', 42)
ORDER BY created_at, id
LIMIT 20
Use Case
A social media feed uses cursor-based pagination so that new posts do not cause items to shift between pages as users scroll. An admin dashboard uses offset pagination so that administrators can jump directly to page 15 of the user list.