High-Traffic API Design: Pagination, Idempotency, Versioning
High-traffic API design: cursor-based pagination, idempotency keys, versioning strategy, contract-first development, HTTP cache headers and bulk endpoints.
What separates a high-traffic API from an ordinary one?
An ordinary API gives the right answer; a high-traffic API gives the right answer at predictable cost, safely under repeated calls, and in a form that can evolve for years. The difference lives in three contracts: the pagination contract (cost), the idempotency contract (repeat safety) and the versioning contract (evolution). This article builds all three to production standard.
Pagination: offset's hidden bill
Offset pagination (page=500&size=20) is innocent on small data; on a large table, every page also scans the rows being skipped — query cost grows with page depth, and interleaved writes shift records to show duplicates or gaps. The high-traffic standard is cursor (keyset) pagination: the client receives an opaque cursor (the last record's sort key, encoded), and the next page is fetched by index as 'after this key' — cost pinned regardless of depth, drift gone. The rules: the cursor stays opaque and encoded/signed (clients must not poke inside), the sort key must be unique (created_at + id), and total-count needs move to a separate, cacheable endpoint — 'COUNT(*) on every page' is a classic self-inflicted wound.
Idempotency keys: single effect in a repeating world
Our earlier rule returns here as an API contract: networks, retrying clients and double-clicks deliver the same request more than once. For write endpoints the pattern is crisp: the client generates an Idempotency-Key header (UUID); the server stores the key with the result on first processing (with a TTL); when the same key arrives again, the operation is not re-executed — the stored result returns; a concurrent second request on an in-flight key receives a conflict (409/425). For payments, orders and every endpoint whose 'double effect costs money', this is not a preference but an obligation. The subtlety: key + request body are validated together — the same key with a different body returns an error, never a silent accept.
Versioning: evolving without breaking
Approach | Example | Pro | Con |
|---|---|---|---|
URL version | /v1/orders | Visible, easy to route | Coarse; every change is 'a new world' |
Header version | Accept: ...;v=2 | Stable URLs; fine-grained | Low visibility; needs tooling |
Additive evolution | New fields added, old preserved | Most changes ship without a version | Requires discipline and tolerant readers |
The practical recipe is a blend: additive evolution as the main line (adding fields is free; removal/meaning changes are forbidden) + URL versions for unavoidable breaks. Both sides bind to the rule: the server is free to send unknown fields, the client ignores what it does not know (tolerant reader). Deprecation is a contract too: Deprecation/Sunset headers, usage metrics and a scheduled shutdown — 'who is still on v1?' must be answerable from a dashboard.
Contract-first: the OpenAPI production line
In a high-traffic API the contract is not documentation derived from code; it is the source code derives from: the OpenAPI definition is written first, server skeletons and client SDKs (including TypeScript types) are generated from it, and CI validates example requests/responses. The payoff chain is clear: frontend and backend work in parallel, breaking changes are caught in CI (schema diff), and SDKs handed to external consumers are never handwritten. This is the general-API form of the screen-contract discipline from our BFF article.
Cost-cutting details: cache headers and bulk endpoints
HTTP's built-in tools earn real money at scale: on stable resources, ETag/If-None-Match makes 304 responses that zero the body cost; on catalog-like content, Cache-Control feeds CDN absorption (connecting to the layer map in our caching article). The second item is the API face of the N+1 pattern: if a client makes 50 calls for 50 products, the fault is in the contract, not the client — bulk endpoints (multi-read by ids=1,2,3, batch writes) and field selection (fields=id,name,price) cut call count and body size together. Rate-limit headers (X-RateLimit-*, Retry-After) are the client-visible face of the contract from our rate-limiting article.
Business impact: the API contract is a capacity and revenue instrument
Cursor pagination + ETag + bulk endpoints deliver the same functionality with markedly less database load and bandwidth — that is directly the infrastructure bill. Idempotency eliminates double-charge/double-order incidents and their customer-service cost. Version discipline lets external integrations (marketplaces, partners) connect with confidence — if your API is a product, contract quality is your sales argument.
Frequently asked questions
Doesn't GraphQL solve these problems?
It moves some at a different cost: field selection is built in, but query cost control, N+1 resolvers and caching become your problem. REST + a good contract is more predictable in most enterprise scenarios.
How long should idempotency keys be stored?
As long as the client's realistic retry window plus margin: 24-72 hours is common in practice. The storage TTL must be stated explicitly in the contract.
How do I 'jump to page 7' with cursors?
You don't — that is offset's job and carries offset's cost. The real need is usually filtering/search, not jumping; for the rare screens that truly need page numbers, bounded-depth offset is acceptable.
Is this discipline required for internal APIs too?
Increasingly yes as traffic and team count grow: internal consumers also retry, internal contracts also break. At minimum, idempotency + tolerant readers + schema-diff CI should be standard internally.
API design checklist
List endpoints use cursor pagination; cursors opaque, sort keys unique
All critical write endpoints support Idempotency-Key
Evolution rule written: additive free, breaking → version; tolerant reader mandatory
OpenAPI is the source; SDKs/types generated; schema diff in CI
ETag/Cache-Control strategy defined per endpoint
Bulk endpoints and field selection enforce a call/body diet
Deprecation/Sunset process and version-usage dashboard in place
SSH Yazılım builds contract design and OpenAPI production lines for high-traffic APIs end to end. Let us prepare your API for scale together.