DevelopmentIndustry Guide12 min readPublished June 13, 2026

One reference · errors, pagination, idempotency, versioning · production patterns from Stripe, GitHub & Google

REST API Design in 2026: A Full Engineering Reference

The REST consensus has quietly shifted. RFC 9457 standardised machine-readable errors back in 2023, cursor pagination is the answer at scale, idempotency keys are table stakes, and the spec is now the contract. This is the engineering reference that pulls the current state of the art into one place — grounded in how Stripe, GitHub, Google, and Microsoft actually ship.

DA
Digital Applied Team
Senior engineers · Published Jun 13, 2026
PublishedJun 13, 2026
Read time12 min
SourcesIETF RFCs + vendor docs
Problem Details RFC
9457
supersedes RFC 7807
since 2023
Idempotency key length
255ch
Stripe max (vendor-stated)
OpenAPI line
3.2
released Sept 2025
back-compatible 3.1
Deprecation notice
6–12mo
Zalando guideline

REST API design in 2026 is no longer a matter of taste — much of what used to be argued in style guides now has a published standard, a production reference implementation, or both. The hard part is that the consensus moved while a lot of shipped code stayed put. This reference pulls the current state of the art into one place.

Three things are true at once. The patterns that matter — structured errors, cursor pagination, idempotency keys, header-based versioning, machine-readable deprecation — are well-specified and battle-tested at companies like Stripe, GitHub, and Google. Most real-world APIs still don’t use them. And the gap between the two is exactly where developer experience, reliability, and integration cost quietly leak away.

What follows is organised the way an engineering team actually makes decisions: resource modelling, error format, pagination, idempotency, versioning, rate limiting, deprecation, and the OpenAPI-first workflow that ties it together. Every fact below is sourced from the relevant IETF RFC or the public engineering documentation of the provider it describes.

Key takeaways
  1. 01
    RFC 9457 is the standard for API errors — and it’s 2023.Problem Details for HTTP APIs defines a machine-readable JSON error format (media type application/problem+json) with five base fields. It superseded RFC 7807 in July 2023, yet many APIs still return ad-hoc strings.
  2. 02
    Offset pagination collapses at scale; cursor doesn’t.OFFSET 100,000 forces the database to scan and discard the first 100,000 rows on every request. Cursor pagination keys off a stable identifier instead — the trade-off is losing random-access page jumps.
  3. 03
    Idempotency keys make retries safe — with one trap.Stripe’s spec uses an Idempotency-Key header (V4 UUID recommended). Results are cached only after endpoint execution begins, so a failed validation is NOT served from cache — a detail that breaks naive retry logic.
  4. 04
    Versioning is about isolation, not version numbers.Stripe pins date-based versions by default and transforms responses backwards; GitHub uses an X-GitHub-Api-Version header. The mechanism matters more than whether you write v1 or a date.
  5. 05
    The spec is the contract — write it first.OpenAPI 3.1 aligns fully with JSON Schema 2020-12; 3.2 (Sept 2025) adds structured tag navigation and streaming media types. Generating clients and stubs from the spec closes the drift gap that wrecks DX.

01Resources & NamingModel resources, not your database.

The first design decision is also the one teams most often get backwards: a REST resource is a noun in your domain model, not a row in your schema. Microsoft’s Azure REST API design guidance is explicit on this — use plural nouns for collection URIs, keep URI depth to a collection/item/collection maximum, and avoid APIs that mirror the internal database structure. Leaking your table names into the URL couples consumers to your storage layer and widens the attack surface for no benefit.

Google’s AIP-190 naming standard adds the conventions that keep a large API surface coherent: method names in VerbNoun UpperCamelCase, correct American English spelling, and the same term used for the same concept everywhere. It warns against overly generic words — instance, info, service — that create ambiguity. Google-style APIs also use a colon separator for custom actions on collections, making the action-versus-resource boundary explicit: POST /v1/videos:process rather than /processVideo. The standard methods stay List, Get, Create, Update, and Delete.

Collections
Plural nouns
GET /orders · POST /orders

Microsoft’s guide recommends plural nouns for collections and capping URI depth at collection/item/collection. Don’t mirror your database tables in the path.

Microsoft Azure guide
Custom actions
Colon separator
POST /v1/videos:process

Google-style REST keeps action-resource boundaries explicit with a colon — not a verb baked into the path like /processVideo. Standard methods stay List/Get/Create/Update/Delete.

Google AIP-190
Idempotency
Method semantics
GET · PUT · DELETE = idempotent

Per Microsoft’s guide, GET, PUT, and DELETE must be idempotent; POST and PATCH are not guaranteed to be. The same PUT submitted repeatedly must always produce the same result.

HTTP method contract
Naming discipline
Google AIP-190 is blunt about word choice — and the underlying point is consistency, not pedantry. Per the standard, names used in APIs should be in correct American English, and overly generic terms such as instance, info, and service create ambiguity that compounds across a large surface. Pick one term per concept and use it everywhere.

02Error BodiesRFC 9457: the error standard almost nobody uses.

Here is the adoption gap in one line: the IETF has had a machine-readable error standard since 2023, and most APIs still return {"error": "something went wrong"}. RFC 9457, “Problem Details for HTTP APIs,” was published in July 2023 and supersedes RFC 7807. It defines a standardised JSON (and XML) error format served as application/problem+json, built from five base fields: type, title, status, detail, and instance.

RFC 9457 added three improvements over 7807: a registry for commonly-used problem type URIs to enable interoperability, clarified handling of multiple simultaneous problems, and expanded guidance for non-dereferenceable type URIs. One subtlety catches teams off guard — the detail field is explicitly for helping the client resolve the issue, not for server debugging output. Extension members beyond the five standard fields are allowed, and consumers must ignore unrecognised extensions so the format stays forward-compatible.

RFC 9457 problem+json field reference: each of the five base fields plus extension members, with type, requirement, purpose, and an example value.
FieldTypeRequiredPurpose
typeURI referenceRecommendedIdentifies the problem type
titlestringOptionalHuman-readable summary of the problem type
statusintegerOptionalHTTP status code (useful in logging contexts)
detailstringOptionalOccurrence-specific explanation for the client
instanceURI referenceOptionalIdentifies this specific occurrence
Extension membersanyOptionalProblem-specific data; unknown members must be ignored

The practical migration is small and high-leverage. Swap a bare {"error": "not found"} for a problem+json body with a stable type URI and a client-actionable detail, set the Content-Type to application/problem+json, and your consumers get errors they can branch on programmatically instead of string-matching prose. It is one of the cheapest developer-experience upgrades an existing API can make.

03PaginationThe OFFSET lie at scale.

Offset pagination feels free because it is, until your dataset isn’t small. The mechanism is the problem: offset-based pagination forces the database to scan and discard the first N rows on every query, so OFFSET 100,000 requires the engine to read and throw away 100,000 rows before returning a single result. At scale that cost grows roughly with how deep into the dataset the request reaches.

Cursor-based pagination avoids the problem by keying off a stable identifier — a timestamp or an opaque token — so the database can seek directly to the slice it needs. The trade-off is real and worth stating plainly: API consumers can no longer jump to an arbitrary page, because random access is lost, and cursors can be invalidated if records are deleted, which means fresh cursors with each response. GitHub’s production answer is to expose pagination through Link headers — containing next, last, first, and prev references — rather than baking URLs into the JSON body, and to have clients follow those links rather than construct query strings by hand.

You should not try to manually construct pagination queries, but instead use the Link headers to determine what pages of results you can request.— GitHub Docs, REST API Best Practices
Small / bounded sets
Offset is fine

Admin tables, settings lists, anything where the total row count stays modest and users genuinely want to jump to page 7. Offset’s scan cost is negligible when N is small.

Offset pagination
Large / growing feeds
Cursor by default

Activity feeds, event logs, anything paginated deeply or under heavy load. Keying off a stable identifier avoids the read-and-discard cost that makes deep OFFSET exponentially expensive.

Cursor pagination
Public APIs
Link headers

Follow GitHub’s pattern: return next/prev/first/last in Link headers and instruct clients to follow them. Decouples your URL structure from consumer code so you can change it later.

Header-driven navigation

04IdempotencyIdempotency keys and the cache-timing trap.

Networks fail mid-request, and a client that doesn’t know whether its POST succeeded will retry. Idempotency keys make that retry safe. Stripe’s widely-copied spec uses an Idempotency-Key request header, recommends a V4 UUID, caps key length at 255 characters (vendor-stated), and retains keys for at least 24 hours (vendor-stated). It applies to all POST requests. Stripe saves the resulting status code and body of the first request regardless of whether it succeeded or failed — including 500 errors — and if the parameters on a retry don’t match the original request, the idempotency layer returns an error, preventing accidental reuse of a key for a different payload.

The edge case that breaks retry logic
Per Stripe’s documentation, results are cached only after endpoint execution begins — so validation errors and concurrent conflicts do not trigger caching. Teams that assume a failed validation request was cached will have their retry logic silently misfire: the second call with the same key isn’t served the first response, because there was never a cached one. Design your client to treat a key as “in flight,” not “answered,” until you’ve seen a stored result.

The reason this matters is fundamental to distributed systems, and Stripe’s own engineering writing puts it well — when a client sees a failure it can converge its state with the server’s by retrying until it verifiably succeeds, but only if the server makes that retry safe. Idempotency keys are how you keep the promise. If you’re building event-driven systems on top of this, the same discipline extends to idempotency patterns for webhooks and retries, where at-least-once delivery makes the property non-negotiable.

When a client sees a failure, it can ensure convergence of its own state with the server's by retrying, and continue to retry until it verifiably succeeds.— Stripe Engineering, Designing robust and predictable APIs

05VersioningVersioning is about isolation, not numbers.

The version-numbering debate misses the real engineering question: how do you isolate a breaking change from the clients that aren’t ready for it? Stripe’s answer is date-based API versioning — values like 2017-05-25 rather than integer v1/v2 — and the company reports having shipped approximately 100 backwards-incompatible versions since 2011. New accounts are pinned to the latest version automatically at their first API call. The architecture underneath is a transformation layer: a core that only speaks the current version, plus version-change modules that convert responses backwards for older clients, which eliminates the version-check conditionals that would otherwise sprawl across business logic.

GitHub takes the header route, using an X-GitHub-Api-Version request header rather than URL-path versioning. Both keep the base URL stable, which is what makes them “soft” isolation. The comparison below puts the common strategies side by side on the dimensions that actually drive the decision.

API versioning strategy comparison across five approaches: breaking-change isolation, client migration effort, discovery mechanism, and a real-world adopter for each.
StrategyIsolationClient effortUsed by
URL path/v1/users → /v2/usersHard — separate namespaceManual URL swapMany public APIs
HeaderX-GitHub-Api-VersionSoft — same base URLHeader updateGitHub REST API
Date-basedStripe-Version: 2024-06-20Soft + pinned by defaultDashboard upgradeStripe
Media typeAccept: …; version=2Soft — content negotiationAccept headerSome hypermedia APIs
Query param?version=2Soft — cacheability riskParam updateLess common / legacy
An API is a contract
Stripe’s engineering team frames the obligation in a single image worth keeping in mind when you weigh a breaking change. Per their writing, an API represents a contract for communication that can’t be changed without considerable cooperation and effort — like a connected power grid or water supply, once it’s hooked up it should run without interruption for as long as possible. For a deeper decision tool, see our API versioning strategies decision matrix.

06Rate Limits429 done right: backoff, jitter, and headers.

A 429 Too Many Requests response is only half a design. RFC 9110 (Section 10.2.3) and RFC 6585 both recommend including a Retry-After header with the 429, where the value can be either an HTTP date or a delay in seconds. Without it, every client has to guess when to come back. On the client side, exponential backoff with jitter is the standard for recovery — and the jitter is not optional. Without it, multiple clients that hit the limit at the same moment will retry simultaneously, re-synchronise their traffic, and destabilise the server they were trying to be gentle with.

Good APIs let clients avoid the 429 entirely. Most providers expose X-RateLimit-Remaining and X-RateLimit-Reset headers so a proactive client can self-throttle before it trips the limit, rather than reacting after the fact. GitHub adds a nice twist for conditional requests: it returns ETag and Last-Modified headers, and a conditional GET using if-none-match or if-modified-since that returns 304 Not Modified does not consume primary rate-limit quota. On the implementation side, Redis is the standard backend for real-time rate limiting thanks to its atomic increments and TTL support.

The four pieces of a complete rate-limit response

Source: RFC 9110, RFC 6585, GitHub & Zuplo documentation
Retry-After on 429RFC 9110 §10.2.3 + RFC 6585 — date or seconds
Required
Backoff + jitterClient-side standard — jitter prevents retry synchronisation
Standard
X-RateLimit-* headersRemaining + Reset → clients self-throttle pre-emptively
Proactive
Conditional GET (304)GitHub — if-none-match / if-modified-since skip quota
Free reads

Authentication sits alongside rate limiting as the other transport concern that shapes the whole API. OAuth 2.0 with PKCE (Proof Key for Code Exchange) is the standard auth flow for modern public clients; bearer tokens should expire in roughly 15 to 60 minutes with refresh token rotation, and every authentication pattern is exposed without HTTPS/TLS at the transport layer. For the algorithm-level view of how the limits themselves are computed, see our companion reference on rate limiting strategies for REST APIs.

07DeprecationSunsetting endpoints, machine-readably.

Retiring an endpoint is a contract event, and the IETF has given it two machine-readable signals. RFC 8594 defines the Sunset HTTP response header, indicating the date and time after which a URI is expected to become unresponsive. Its companion, RFC 9745 (“The Deprecation HTTP Response Header Field”), defines the Deprecation header, which signals that an endpoint is deprecated. Used together, they let consumers detect and monitor deprecations automatically rather than discovering them when something breaks.

Standards describe the mechanism; policy is what makes deprecation humane. The widely-referenced open-source Zalando RESTful API Guidelines specify that deprecated endpoints must carry both the Deprecation and Sunset headers, and that teams owe consumers six to twelve months of notice plus a migration guide before decommissioning. The headers tell a machine; the notice period and the guide tell the engineers on the other end.

Sunset header
When it goes away
8594RFC

RFC 8594 defines the Sunset HTTP response header — the date/time after which a URI is expected to become unresponsive. Lets clients schedule their migration against a real date.

IETF standard
Deprecation header
That it’s deprecated
9745RFC

RFC 9745 defines the Deprecation response header signalling an endpoint is deprecated. Pairs with Sunset for automated, machine-readable monitoring.

IETF RFC
Notice period
Plus a migration guide
6–12mo

The Zalando RESTful API Guidelines call for both headers on deprecated endpoints, and 6–12 months of notice plus a migration guide before decommissioning. Policy, not just protocol.

Zalando guideline

08OpenAPI-FirstThe spec is the contract.

Every pattern above is only as trustworthy as the document that describes it — and a spec written after the code drifts away from reality the moment the code changes. The design-first answer is to make the OpenAPI specification the contract, not the implementation. OpenAPI 3.1.0 aligns fully with JSON Schema Draft 2020-12, added webhooks as a top-level element, made path items optional for reusable component libraries, and allowed descriptions alongside $ref usage. The current 3.1-line schema is 3.1.2 (schema updated 2025-11-23).

OpenAPI 3.2.0, released in September 2025, added structured tag navigation, streaming-friendly media types, and new OAuth flows while remaining fully backwards-compatible with 3.1. With the spec as the source of truth, code generation closes the drift gap: the open-source OpenAPI Generator produces client SDKs and server stubs across 50+ languages from a single OpenAPI document and integrates into CI/CD pipelines so generated code stays in sync with spec changes. When the spec is the contract, the generated client is correct by construction.

One more design lever belongs in the same conversation. HATEOAS — Hypermedia as the Engine of Application State — enriches responses with discoverable links to reduce client-server coupling, commonly serialised as HAL or JSON:API. In 2025–2026 it earns its complexity more for intricate client-server ecosystems such as workflow APIs and multi-step state machines than for simple CRUD. If you’re weaving APIs together across services, our reference on API-first development and microservices architecture picks up where this leaves off, as does our guide to serverless deployment for API backends.

In 2025, HATEOAS isn't fashionable like GraphQL — yet it solves real problems for complex client-server ecosystems.— Pradeep Loganathan

09What To AvoidThe anti-patterns that quietly cost you.

Most API problems aren’t exotic; they’re the same handful of anti-patterns repeated across teams. Each one trades a small upfront convenience for a recurring tax on the consumers and on your future self. The grid below collects the ones worth auditing for first.

URIs
Verbs in the path
/createOrder → POST /orders

A verb in the URI duplicates what the HTTP method already says and forfeits method semantics. Using POST for everything is the same mistake at scale — it throws away GET/PUT/DELETE meaning entirely.

Use method semantics
Errors
200 OK on failure
{"error": "went wrong"}

Hiding a failure behind a 200 status, or returning a generic error string, forces clients to parse prose. RFC 9457 exists precisely to replace this — a stable type URI beats a free-text message.

RFC 9457 instead
Structure
Deep nesting & leaked schema
/customers/1/orders/99/products/5/reviews

URI nesting beyond ~3 levels is fragile, and using table names as resource names couples consumers to your storage and widens the attack surface. Keep depth shallow; model the domain, not the database.

≤3 levels
Lifecycle
Silent breaking changes
v3 · no Deprecation / Sunset

Integer bumps with no deprecation headers, no sunset date, and no migration path break consumers without warning. Offset pagination on large datasets is the same class of bug — an invisible cost.

Signal the change

10ConclusionClose the adoption gap.

The state of REST, June 2026

The patterns are standardised. The opportunity is in actually using them.

The striking thing about REST API design in 2026 is how little of it is still genuinely contested. Errors have a standard in RFC 9457. Pagination has a clear mechanism-driven answer. Idempotency, versioning isolation, machine-readable deprecation, and spec-first workflows all have well-documented production references. The disagreement has largely moved from what is correct to why so few APIs do it.

That gap is the opportunity. An existing API can adopt problem+json errors in an afternoon, add Retry-After and X-RateLimit-* headers in a sprint, and earn outsized developer-experience gains for the effort. The harder, more valuable moves — cursor pagination on hot paths, a transformation-layer versioning strategy, an OpenAPI document that is the contract rather than an afterthought — are the ones that compound as the API and its consumer base grow.

Looking forward, the direction of travel is clear: machine-readable everything. Errors a client can branch on, deprecations a tool can monitor, contracts a generator can turn into correct clients. The teams that treat the specification as the source of truth — and lean on the IETF and vendor patterns rather than re-litigating settled questions — will spend their design energy on the parts of their API that are genuinely unique to their domain. That is where it belongs.

Ship APIs your consumers trust

Make your API a contract consumers can build on.

Our engineers design and build production-grade REST APIs — RFC 9457 error contracts, cursor pagination, idempotency, versioning, and OpenAPI-first workflows — and integrate them across your stack, delivered in days not quarters.

Free consultationSenior engineersTailored solutions
What we work on

REST API engagements

  • Problem+json error contracts & API health audits
  • Cursor pagination & rate-limit design for scale
  • Idempotency keys & retry-safe write paths
  • Versioning strategy & machine-readable deprecation
  • OpenAPI-first specs with generated client SDKs
FAQ · REST API design

The questions engineers ask every week.

RFC 9457, “Problem Details for HTTP APIs,” is the IETF standard for machine-readable error responses, published in July 2023 as the successor to RFC 7807. It defines a JSON (and XML) error format served with the media type application/problem+json, built from five base fields: type, title, status, detail, and instance. The point is that clients can branch on a stable type URI programmatically instead of string-matching free-text error messages. RFC 9457 also added a registry for common problem type URIs, clarified handling of multiple simultaneous problems, and allows custom extension members that consumers must ignore if unrecognised, keeping the format forward-compatible. It matters because most APIs still return ad-hoc error strings, so adopting problem+json is a cheap, high-leverage developer-experience upgrade.