Skip to content
Backend

The REST API Checklist I Wish Every Integration Had

From both sides of hundreds of integrations: honest status codes, RFC 7807 errors with trace IDs, cursor pagination, idempotency and versioning posture, and the DX multipliers that halve support tickets.

5 min read Updated Sep 4, 2026
The REST API Checklist I Wish Every Integration Had

I've integrated against hundreds of REST APIs — payment processors, e-invoice providers, logistics platforms, one memorable API that returned HTTP 200 for every error with "success": "false" (a string!) in the body — and built my share for others to suffer or enjoy. This is the checklist distilled from both seats: not REST theology, just the decisions that determine whether integrators curse your name in their commit messages.

Resources, status codes, pagination and RFC 7807 errors — the pillars of a humane API

1. Resources and verbs: the boring part, done boringly

Nouns for resources, plural, shallow nesting (one level max — /orders/42/items yes, /merchants/7/orders/42/items/3/discounts no; the item has an ID, address it directly). Actions that don't map to CRUD get a sub-resource verb and nobody gets hurt: POST /orders/42/cancellation beats both DELETE-with-side-effects and RPC-style /cancelOrder?id=42. Use PATCH for partial updates and mean it; a PUT that secretly merges is a data-loss generator with a spec citation.

2. Status codes: the contract machines read first

The subset that covers 99% of an API, used consistently: 200/201/204 (and 201 carries a Location header), 400 malformed, 401 who are you, 403 I know who you are and no, 404 (also for resources hidden by tenancy — a 403 on someone else's order ID confirms it exists, which is an information leak), 409 state conflict, 422 understood-but-invalid, 429 with Retry-After (see the rate-limiting post), and 5xx meaning we broke, retry welcome. The one unforgivable sin: errors behind 200. Every retry library, monitor and circuit breaker on Earth keys off status codes; lie to them and your integrators must hand-roll error detection per endpoint, forever.

3. Errors: RFC 7807, plus the field level

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "2 fields failed validation.",
  "instance": "/v1/orders",
  "trace_id": "req_8f3ka92...",
  "errors": [
    { "field": "email", "code": "invalid_format", "message": "Not a valid e-mail." },
    { "field": "items", "code": "min_items",      "message": "At least 1 item required." }
  ]
}

The load-bearing parts: machine-readable code per error (integrators branch on codes, humans read messages — never make code parse prose), and a trace_id echoed from your tracing — the single field that turns support tickets from "it doesn't work" into a searchable incident. Cost: one exception handler. Value: every debugging session anyone ever has with your API.

4. Pagination: cursors, and the fields around them

Offset pagination (?page=52) degrades linearly and skips or duplicates rows when data changes between pages — fine for small admin lists, wrong for anything synced or crawled. Cursor pagination is stable and index-friendly:

GET /v1/orders?limit=50&cursor=eyJpZCI6NDIxMX0

{ "data": [ ... ],
  "pagination": { "next_cursor": "eyJpZCI6NDI2MX0", "has_more": true } }

Make cursors opaque (encode them; integrators will construct any URL whose shape they can guess), cap limit hard, and default sort by a stable unique key. While you're at the collection endpoint: filtering via query params with documented operators beats inventing a query language, and every list an integrator might sync deserves an updated_since filter — it's the difference between polite nightly syncs and full-table crawls against your API.

5. The reliability layer: what separates professional APIs

  • Idempotency keys on every mutating endpoint that matters. Payments obviously; but any POST an integrator would retry after a timeout — which is all of them.
  • Explicit timeouts + honest 5xx semantics, so clients can implement retry-with-backoff without guessing which failures are safe to repeat.
  • Webhooks done right if you push events — signed, retried, with a delivery log integrators can see.
  • Versioning posture declared on day one: URL version, additive-change rules, and the "clients must ignore unknown fields" sentence in the docs before the first client exists.

6. The developer experience multipliers

An OpenAPI spec that's generated or validated from code (a hand-maintained spec is a slowly diverging work of fiction — contract tests in CI keep it honest); a sandbox with deterministic test data and documented magic values ("card ending 0002 always declines" — every good payments API does this because it works); curl examples for every endpoint, because the first integration step is always a terminal; and consistent field conventions throughout — one casing, ISO 8601 with timezone for every timestamp, money as integer minor units + currency code (floats for money is how you end up explaining rounding to an accountant), and null vs absent meaning something deliberate.

The checklist, compressed for review day

□ Nouns, shallow nesting, sub-resource verbs for actions
□ Status codes honest; 404 for cross-tenant; 429 + Retry-After
□ RFC 7807 errors with per-field codes and trace_id
□ Cursor pagination, opaque cursors, capped limits, updated_since
□ Idempotency keys on mutations; documented retry semantics
□ Versioning + tolerance rules declared before client #1
□ Generated OpenAPI, sandbox with magic values, curl-able docs
□ ISO dates, minor-unit money, one casing to rule them all

Designing an API for external integrators is a different sport from internal endpoints — and a design review before the first integration costs one afternoon against years of being the API people warn each other about.

Keep reading

Related articles

Backend 5 min read

The Caching Stack: From Browser to Buffer Pool

Five layers walked top to bottom — browser headers, CDN edge with its famous incident, Redis with a job description, database-adjacent options — plus the staleness grid that makes TTLs a product decision.

Backend 4 min read

GraphQL: An Honest Take After the Hype Cycle

The specific problem it solves brilliantly, the five bills itemized — resolver N+1s, forfeited HTTP caching, query-surface DoS, field-level auth, the toolchain — and the BFF alternative most teams actually need.