Pagination, Filtering, Sorting and Versioning

4.Bookmarks and New Editions

A

In this chapter

We'll learn how good APIs return large amounts of data in pages, let callers filter and sort, and change over time without breaking anyone — pagination, filtering, sorting and versioning — explained as a library catalogue, a bookmark and a new edition of a book.

12–14 min

The Problem in Real Life

Riverside Arena's system calls GET /v1/orders once a night to copy sales into their accounting. When they started, they had 300 orders. Now they have 48,000, and BlueTicket's API tries to send all of them in one response: 60 MB of JSON, a minute of database work, and frequent timeouts.

Their developer also asks politely whether BlueTicket could rename a confusing field. Liam is about to just rename it. John stops him: "Twelve venues' systems read that field every night. Rename it, and twelve accounting systems break tomorrow morning."

J

An API is a promise. You can add to it. You can't take things away without warning.

John

"Here's Everything, Forever Unchanged" vs. Pages, Questions and Versions

Too much at once

Returning every record in one response is slow, fragile and gets worse every day.

Callers want specific things

"Only paid orders from last night, newest first" — not all 48,000.

APIs need to change

New needs arrive, but other companies' code depends on today's exact shape.

Pagination, Filtering, Sorting and API Versioning

The library analogy: a library catalogue doesn't hand you a list of all two million books. It shows one page of results at a time (pagination), lets you narrow the search — "only novels, only in English" (filtering) — and order them — "newest first" (sorting). And when a textbook changes a lot, the publisher prints a new edition while the old one stays on the shelf for a while (versioning).

  • Pagination — one page at a time: the API returns a limited number of items per response (say 100) plus a way to get the next page. Responses stay small and fast, no matter how much data exists.
  • Offset pagination — "page 3, please": GET /v1/orders?limit=100&offset=200 means "skip 200, give me the next 100". Simple and lets you jump to any page. But it gets slower deep into big tables (the database still has to count past the skipped rows), and if new orders arrive while you're paging, items can shift between pages — you might see one twice or miss one.
  • Cursor pagination — a bookmark: instead of a page number, each response includes a cursor: a bookmark pointing to exactly where you stopped. GET /v1/orders?limit=100&after=ord_9F2a. It stays fast on huge tables and doesn't skip or repeat items when new data arrives. You can't jump to "page 37", but for "go through everything" — like a nightly sync — it's the better choice.
  • Filtering — narrowing the search: query parameters let callers ask only for what they need: ?status=paid&createdAfter=2026-11-20T00:00:00Z. Less data, less work, fewer timeouts. (The server still validates every parameter — Act 22.)
  • Sorting — choosing the order: ?sort=createdAt (oldest first) or ?sort=-createdAt (newest first — the minus means descending). Pagination needs a stable order, or pages can overlap.
  • Versioning — new editions: a breaking change is anything that could break an existing caller: renaming or removing a field, changing a type, making an optional field required. Non-breaking changes — adding a new optional field or a new endpoint — are safe. For breaking changes, publish a new version (/v2/orders) and keep /v1 running for a while. Announce a deprecation date and give consumers time — often months — to move.
Table — Offset vs. cursor pagination
FeatureOffset (page numbers)Cursor (bookmark)
Request?limit=100&offset=200?limit=100&after=ord_9F2a
Jump to page 37YesNo
Speed on huge tablesGets slower deeper inStays fast
New data while pagingItems can shift, repeat or be missedConsistent
Good forSmall lists, page numbers in a UISyncing everything, infinite scroll
Table — Breaking or not?
ChangeBreaking?What to do
Add a new optional field totalAmountNoJust ship it
Add a new endpoint /v1/refundsNoJust ship it
Rename amt to totalAmountYesAdd the new one; remove the old in /v2
Change total from number to stringYesNew version
Make a filter parameter requiredYesNew version
A paginated, filtered, sorted request and response
GET /v1/orders?status=paid&createdAfter=2026-11-20T00:00:00Z&sort=createdAt&limit=100
{
"data": [
{ "orderId": "ord_1042", "status": "paid", "amt": 178.00, "totalAmount": 178.00, "createdAt": "2026-11-20T19:42:10Z" }
// ... up to 100 orders
],
"nextCursor": "ord_9F2a", // pass as ?after=ord_9F2a for the next page
"hasMore": true
}

amt stays for existing venues; totalAmount is added beside it. Removing amt waits for /v2.

BlueTicket's fixes: GET /v1/orders now returns at most 100 orders per page with a cursor, supports status, createdAfter and createdBefore filters, and sort. Riverside's nightly sync becomes "orders created since last night, 100 at a time" — about 400 orders in five small requests, two seconds in total. The confusing field (amt) stays in v1; a clearer name (totalAmount) is added alongside it — a non-breaking change. In v2, planned for next year, amt will be removed, with six months' notice to all venues.

Key Takeaway

Large collections are returned in pages: offset pagination ("skip 200, give 100") is simple but slows down and can skip or repeat items; cursor pagination uses a bookmark and stays fast and consistent. Filtering and sorting via query parameters let callers ask only for what they need. Adding fields is safe; renaming or removing them is a breaking change that needs a new version and a deprecation period, because other people's code depends on the contract.

Why This Matters

Every API that returns lists needs pagination, and every API used by others will need to change. Getting these right keeps APIs fast as data grows and keeps your partners' systems working — and "how would you paginate this?" and "how do you version an API?" are classic interview questions.

Back to the main problem: the payment provider's callback. Anna now knows the rules it broke. Time to understand callbacks properly — and why BlueTicket should never again depend on one of them arriving.

Next