The Day-One Requirement
Every list endpoint will eventually face a user with a million rows. Without pagination designed in, that first power user becomes your outage. Pagination is not a feature; it is load control for read paths.
UNPAGINATED PAGINATED
GET /trips → 4M records, 2GB GET /trips?limit=20 → 40KB
app server OOMs, DB drowns bounded memory, bounded latency,
predictable client rendering
rule: NO list endpoint ships without a limit + strategy
Two Families
OFFSET PAGINATION CURSOR PAGINATION
?limit=20&offset=100 ?limit=20&cursor=eyJpZCI6MTAwfQ==
"skip 100, give me 20" "continue after this opaque bookmark"
✓ random access to page N ✓ stable under inserts/deletes
✓ jump links, page numbers ✓ no skipped/duplicated items
✗ unstable under concurrent ✗ only forward/sequential walks
writes (rows shift) ✗ no "jump to page 37"
✗ OFFSET scans past rows ✓ constant cost at any depth
→ page 10,000 reads 200k rows
The mechanics of each family get their own lessons — this one is about choosing.
The Decision Table
| Requirement | Pick |
|---|---|
| Admin UI with numbered pages | Offset |
| Infinite scroll feeds | Cursor |
| Data changes during browsing (feeds, orders) | Cursor |
| Deep pagination expected (millions of pages) | Cursor |
| Simple internal tools on small tables | Offset |
Consumer-facing infinite-scroll surfaces — most modern products — are cursor territory by default.
API Shape Conventions
request:
GET /trips?limit=20&cursor=<opaque>
response envelope:
{
"data": [ ...up to 20 trips... ],
"pagination": { "next_cursor": "eyJ...", "has_more": true }
}
rules that make clients simple:
- cursor is OPAQUE base64 — never document its internals;
you'll want to change encoding later without breaking anyone
- has_more spares clients an extra empty fetch
- default limit exists (20); max limit enforced (100)
Defaults and Limits Are Load Control
unbounded limit=99999999 is a DoS vector:
one request → full table scan → serialized megabytes
enforce: default_limit = 20
hard_max = 100 (4xx beyond)
response includes actual count served
Interview Framing
“Design the feed API” questions test pagination instinct immediately. Score pattern: state the family choice with reason (“infinite scroll over mutable feed → cursor”), show the envelope once, mention limits enforcement as load protection. Candidates who say “we’d paginate” without picking a family get asked exactly this follow-up — have the decision table ready.
Premium Content
Unlock Pagination and all premium lessons with a subscription.
From ₹199.99/year — See plans