API Style & Pattern Comparisons

Side-by-side trade-offs. Neither column wins — the consumer environment, the data shape and the operational budget decide.

Offset pagination vs Cursor pagination

page/limit arithmetic that any developer understands in seconds vs an opaque continuation token that stays correct and fast while the data moves underneath it.

Offset paginationOpen lesson →
Use when

Small, slow-changing datasets where users genuinely need page numbers and jump-to-page — admin tables, reports.

Avoid when

The collection takes concurrent writes — rows shift between requests, so pages duplicate and skip items.

Strengths

Trivial to implement, stateless, maps directly to numbered-page UI, random access to any page.

Fails when

OFFSET 100000 makes the database scan and discard 100k rows; deep pages get slower page by page.

Operational cost

Deep-offset queries degrade the database for everyone; drift complaints surface as support tickets.

Cursor paginationOpen lesson →
Use when

Large or live collections: feeds, logs, event lists — anything scrolled while rows are inserted and deleted.

Avoid when

The UI is built on numbered pages and "jump to page 47" — a cursor can't take you there.

Strengths

Stable under inserts and deletes, O(1)-ish continuation via the index at any depth, hides internals behind an opaque token.

Fails when

The sort key isn't unique or stable — the cursor lands ambiguously and rows repeat or vanish.

Operational cost

Cursor encoding/versioning, a documented "cursor expired" error path, and a tiebreaker column in every sort.

DimensionOffset paginationCursor pagination
Random accessAny page, directlyNext (and maybe previous) only
Cost at depthGrows linearly — scan and discardFlat — index seek from the cursor key
Under concurrent writesDuplicates and missing rows between pagesStable continuation from a fixed position
Client contractTransparent page/limit numbers clients computeOpaque token clients must echo back unchanged
Retrofit costEasy to ship first, painful to outgrowMore upfront design, no forced migration later