DevelopmentDecision Matrix12 min readPublished June 1, 2026

Four strategies, one decision · 26% of teams use SemVer · 410 Gone at sunset

API Versioning in 2026: The Engineering Decision Matrix

URL path, header, query parameter, and date-based versioning each fit a different context. The deciding factor is rarely aesthetic — it is CDN caching behavior, SDK coupling, and the cost of moving consumers off a deprecated version. This matrix pulls the trade-offs and real vendor timelines from primary docs.

DA
Digital Applied Team
Senior engineers · Published June 1, 2026
PublishedJune 1, 2026
Read time12 min
Sources20 primary docs
Teams using SemVer
26%
of all API teams
Postman 2025
Teams versioning APIs
60%
at all
Postman 2025
GitHub support window
24mo
minimum, per version
Contract testing
17%
of teams adopt it
Postman 2025

API versioning strategies are not a matter of taste — they are an architecture decision with downstream consequences for CDN caching, SDK design, observability, and the cost of migrating consumers years later. URL path, header, query parameter, and date-based versioning each solve a real problem and each carry a real tax.

The deciding factor is rarely how clean the URL looks. It is whether your CDN can cache the response without a misconfiguration that serves the wrong version, whether your SDKs stay pinned correctly, and whether you can sunset an old version without a costly scramble. Postman's 2025 State of the API Report found that 60% of teams version their APIs, yet only 26% use semantic versioning and just 17% run contract testing — the gap between intent and discipline is where production incidents live.

This guide compares the four mainstream strategies, treats GraphQL and gRPC as a legitimate fifth "no-version" category, puts real vendor deprecation timelines side by side from primary docs, and ends with a checklist for sunsetting a version cleanly. Every number and version string below is sourced to a vendor doc, an Internet standard, or the Postman report.

Key takeaways
  1. 01
    No single strategy wins outright.URL path, header, query parameter, and date-based versioning each fit different contexts. The right choice depends on your consumers, your CDN, and how aggressively you expect to break compatibility.
  2. 02
    The CDN tax is the hidden cost.URL path versioning caches cleanly at the edge — /v1 and /v2 are separate cache keys by default. Header versioning breaks standard HTTP caching unless you set Vary, the most common production mistake with header-based schemes.
  3. 03
    Date-based versioning is the platform default.GitHub uses YYYY-MM-DD date headers; Stripe uses YYYY-MM-DD.release_name pinned per account. Both decouple API behavior changes from SDK release cadence — a model worth studying before you copy SemVer wholesale.
  4. 04
    Only 26% of teams use semantic versioning.Per Postman's 2025 State of the API Report, SemVer adoption lags far behind the 60% who version at all — largely because SemVer conflates API-contract change with library-release change, which are different concerns.
  5. 05
    Clean sunsets need a checklist, not a memo.Deprecation and Sunset HTTP headers, a machine-readable deprecated flag in your OpenAPI spec, a migration guide, and a final 410 Gone are what separate an orderly retirement from a support fire.

01The Four StrategiesFour ways to put a version on a request.

REST APIs encode a version in one of four places: the URL path, an HTTP header, a query parameter, or a date. Each places the version somewhere different in the request, and that placement is what drives the trade-offs in everything from caching to client migration.

Most common
URL path
GET /v1/users

The version lives in the URI. Responses are uniquely cacheable at the CDN with no extra configuration — /v1/users and /v2/users are separate cache keys by default. Best for public APIs with diverse consumers where discoverability matters.

Kong: most common REST approach
Cleanest URL
HTTP header
X-API-Version: 2

Keeps the URL stable across versions. The catch: without a Vary: X-API-Version response header, CDNs and reverse proxies may serve the wrong version from cache — the most common production mistake with header versioning.

Accept: …;version=2 also valid
Simplest
Query parameter
GET /users?api-version=v1

Trivial to implement and easy to default. Caching is inconsistent: most caches treat distinct query strings as distinct keys, but some CDNs ignore query params in cache-key computation by default. Microsoft recommends it only when URL path stability is guaranteed.

Behavior varies by CDN
Platform-grade
Date-based
X-GitHub-Api-Version: 2026-03-10

A YYYY-MM-DD date, usually via header, sometimes with a named release. Used by GitHub and Stripe. Decouples behavior changes from SDK cadence and gives every change a precise, sortable identity.

GitHub & Stripe both use it

URL path versioning is the most common REST approach, per Kong's API versioning guidelines, precisely because it is the least surprising: the version is visible, the request is self-describing, and the CDN behavior is predictable. Header and query-parameter schemes optimize for URL stability, which is valuable when your URLs are themselves part of your contract — but they shift complexity onto your caching layer. Date-based versioning is a category of its own, covered in Section 03.

02The CDN TaxThe caching trade-off most comparisons skip.

Most versioning comparisons treat URL versus header as an aesthetic choice. The real difference shows up at the edge. With URL path versioning, the path alone differentiates versions, which allows immutable artifacts with long time-to-live values — you can warm the cache per version and set version-scoped TTLs without thinking about it. The version is part of the cache key automatically.

Header versioning breaks standard HTTP caching unless you do something about it. A CDN keys its cache on the URL by default; if two requests to /users differ only by an X-API-Version header, the cache cannot tell them apart. Without a Vary: X-API-Version response header to tell the cache that the header matters, a proxy can serve a v2 response to a v1 client. This is the cache-poisoning failure mode that catches teams who adopt header versioning for clean URLs and forget the Vary configuration.

The caching implications are often the deciding factor. With path versioning, the path alone differentiates versions, allowing immutable artifacts with long TTLs.— Nerd Level Tech, Mastering API Versioning

Query-parameter versioning sits awkwardly in the middle. Most HTTP caches treat different query strings as distinct cache keys, so ?api-version=v1 and ?api-version=v2often cache separately — but the behavior is not guaranteed. Some CDN providers strip or ignore query parameters in cache-key computation by default, which collapses your versions into one cache entry. The safe move is to verify your specific provider's cache-key rules rather than assume.

The practical rule
If your API sits behind a CDN and you cannot guarantee every team will remember to set Vary correctly, URL path versioning removes an entire class of cache-poisoning incidents by construction. Reach for header or query versioning only when URL stability is a hard requirement and you control the caching layer end-to-end.

03Date-Based VersioningWhat GitHub and Stripe do differently.

Two of the most-consumed APIs on the internet converged on the same idea: version by date, not by integer. The reason is that an integer version like v2 tells a consumer nothing about what changed or when, while a date is precise, sortable, and maps cleanly to a changelog entry.

GitHub: date headers with a long support floor

GitHub's REST API uses date-based versioning in YYYY-MM-DD format, specified via the X-GitHub-Api-Version header. As of June 1, 2026 the latest version is 2026-03-10 with no sunset scheduled, and the legacy 2022-11-28 remains supported until March 10, 2028. Requests without the header default to 2022-11-28, and requests for outdated versions return 410 Gone. GitHub's stated policy is that any version is supported for at least 24 months after a newer version is released.

Stripe: date plus a named release, pinned per account

Stripe uses date-based versioning with named major releases — the current version as of June 1, 2026 is 2026-05-27.dahlia, in the format YYYY-MM-DD.release_name. Major releases such as Dahlia or Acacia bundle non-backward-compatible changes; the monthly releases within a major are backward-compatible and can be adopted without code changes. Each account is pinned to the API version active when it was created, so existing integrations never break silently when Stripe ships a new version.

GitHub support floor
Minimum per version
24mo

GitHub supports each API version for at least 24 months after a newer version ships. The legacy 2022-11-28 version remains live until March 10, 2028 — years of runway for consumers to migrate.

Outdated → 410 Gone
Stripe current
2026-05-27.dahlia
Dahlia

Date plus named major release. Monthly releases inside a major are backward-compatible; only the named major bundles breaking changes. Accounts pin to the version active at creation.

Format: YYYY-MM-DD.name
Stripe rollback
Window after major upgrade
72h

Stripe documents a 72-hour window to roll back to the previous version after a major upgrade, giving teams a safety net to validate behavior changes against production traffic before committing.

Per Stripe upgrade docs

Stripe's deeper engineering choice is what makes its model durable. Responses are generated against current resource definitions, then the system walks backward through time applying version-change modules until the response matches the version the account is pinned to. Old versions become a fixed maintenance cost rather than dead branches scattered through the core code paths — which is how Stripe keeps very old API versions alive for years without the codebase rotting.

Like a connected power grid or water supply, after hooking it up, an API should run without interruption for as long as possible.— Brandur Leach, Stripe Engineering

04Decision MatrixThe strategy decision matrix.

The table below puts the five approaches against the dimensions that actually decide the choice in practice: how they behave at the CDN, how well tooling supports them, and what they suit. Cells are synthesized from the primary sources cited throughout this guide.

Strategy
/v1/users
CDN / caching
Clean — separate cache keys by default, version-scoped TTLs
Suited for
Public APIs with diverse consumers; teams that want predictable caching with zero CDN config. The most common REST approach.
Strategy
X-API-Version: 2
CDN / caching
Breaks caching without Vary: X-API-Version — cache-poisoning risk
Suited for
Stable-URL contracts where you control the caching layer end-to-end and can guarantee correct Vary configuration.
Strategy
?api-version=v1
CDN / caching
Inconsistent — varies by CDN; some ignore query params in cache keys
Suited for
Simple internal services, or public APIs that can guarantee URL path stability (Microsoft's recommended condition).
Strategy
2026-03-10
CDN / caching
Header-based — same Vary caveat as header versioning
Suited for
Platform APIs with continuous backward-incompatible evolution; precise per-change identity. GitHub and Stripe both use it.
Strategy
GraphQL / additive
CDN / caching
Single endpoint — caching handled per-query, not per-version
Suited for
Schema-evolution APIs where additive change and @deprecated cover most needs and clients ignore fields they do not request.

Reading the matrix top to bottom: URL path is the safe default for a public API. Header versioning is the right call only when URL stability is non-negotiable and you own the caching path. Query parameters are best kept to internal or guaranteed-stable services. Date-based versioning is what you graduate to when you ship breaking changes often enough that an integer version stops being descriptive. GraphQL's additive model is a real fifth option, not an absence of strategy — covered in Section 07.

05The SemVer GapWhy only 26% of teams use semantic versioning.

Semantic versioning is the stated best practice nearly everywhere, yet Postman's 2025 State of the API Report found that only 26% of teams actually implement it — against 60% who version their APIs at all and 57% who use Git for change tracking. That is a wide gap between what teams say they should do and what they do.

The gap is not laziness. SemVer was designed for libraries, where a single version number signals the severity of a release to a dependency manager. An API contract is a different object: a change can be additive at the wire level (a new optional field) while still being semantically significant to one consumer and invisible to another. Compressing "what changed in the contract" and "how severe is this release" into one MAJOR.MINOR.PATCH number forces a judgment call that often does not map cleanly onto how consumers experience the change.

API versioning & governance adoption · Postman 2025

Source: Postman 2025 State of the API Report
Version their APIs at allAny versioning scheme in place
60%
Use Git for change trackingVersion control on API definitions
57%
Implement semantic versioningMAJOR.MINOR.PATCH discipline
26%
Run contract testingAutomated consumer-contract validation
17%
Report citation
The 26% semantic-versioning figure, the 60% versioning rate, and the 17% contract-testing figure are all drawn from the Postman 2025 State of the API Report. The same report notes 93% of API teams face collaboration challenges and 55% struggle with inconsistent or missing documentation — context that explains why disciplined versioning is hard to sustain in practice.

A pragmatic resolution many platforms land on is a hybrid: use semantic versioning for the SDKs your consumers install, and a date-based or named-major scheme for the API behavior itself. That keeps the dependency-manager semantics SemVer is good at, while giving API behavior changes the precise, sortable identity a date provides. It is no accident that Stripe pins SDK releases to an API version — the two concerns are coupled but not identical, and treating them as one number is what drives the SemVer adoption gap.

06Backwards CompatibilityGoogle's three compatibility layers.

The whole point of a versioning strategy is to manage breaking changes — so it helps to define "breaking" precisely. Google's API Improvement Proposals are the most rigorous public treatment of this. AIP-180 defines three compatibility layers that must all be preserved within a version: source compatibility (existing client code still compiles), wire compatibility (clients and servers still communicate), and semantic compatibility (behavior still matches expectations).

Under that framework, the safe changes are additive: adding new fields, new methods, and new enum values. The breaking changes are removals and mutations: removing or renaming fields, changing a field's type, and moving fields between proto files — the last of which silently breaks C++ and Python imports and Go stubs even when the wire format is unchanged. Resource names, per AIP-180, must never change, even across a major version bump.

Google's numbering rule is equally strict. AIP-185 mandates major-version-only numbering: APIs use v1, never v1.0, v1.1, or v1.4.2. The major version is encoded in both the protobuf package and the REST URI path. Stability is signaled by suffix — GA is v1, beta is v1beta1, alpha is v1alpha — rather than by minor or patch numbers, which are explicitly prohibited.

AIP-180
Compatibility layers
3

Source (code compiles), wire (communication works), and semantic (behavior matches). All three must hold within a version. Resource names must never change, even across majors.

Google API guidance
AIP-185
Major-only numbering
v1

No v1.0, v1.1, or v1.4.2. The major version lives in the protobuf package and the URI path. Stability is a suffix — v1beta1, v1alpha — not a minor number.

Minor/patch prohibited
Postman 2025
Face collaboration friction
93%

Nearly all API teams report collaboration challenges, and 55% struggle with inconsistent or missing documentation — the practical reason compatibility discipline is hard to hold over time.

State of the API

07The No-Version CategoryGraphQL and gRPC evolve instead of versioning.

Most versioning guides ignore GraphQL and gRPC entirely, which leaves out a legitimate fifth strategy: evolve the schema additively and never cut a version at all. It is not that these systems forbid versioning — they make additive change cheap enough that explicit versions become the exception.

GraphQL: additive evolution plus @deprecated

In GraphQL, adding fields never breaks existing queries because clients only receive what they explicitly request — a new field is invisible to a query that does not ask for it. Fields slated for removal are marked with the @deprecated directive, which surfaces warnings in GraphiQL, Apollo Studio, and similar tooling. The trade-off is real: genuinely breaking changes still need coordination through a schema registry or gateway-level versioning, and field-level sunset is harder to enforce than the endpoint-level 410 Gone a REST API gets for free. Additive evolution is the recommended pattern, not a prohibition on versioning.

gRPC and protobuf: rules instead of version numbers

gRPC inherits protobuf's backward-compatibility rules, which let services evolve without explicit version bumps. Safe changes: add optional fields, add new RPC methods, add enum values. Breaking changes: renaming or removing fields even if unused, reusing a field number, and changing a field's type. The cardinal rule belongs to the protobuf specification, not to gRPC specifically — never reuse a field number, and use reserved to prevent a future collision. Follow those rules and a gRPC service can evolve for years on a single nominal version.

REST, public
URL path on a CDN

Diverse external consumers, edge caching, discoverability. /v1 caches cleanly with no Vary configuration to forget. The lowest-surprise default for a public REST API.

Pick URL path
Platform, evolving
Date-based headers

Frequent backward-incompatible change, an account-pinning model, a need to keep old behavior alive for years. Study GitHub and Stripe before copying SemVer into this slot.

Pick date-based
Internal services
Query param or none

Tightly-coupled internal callers you deploy together. Query-param versioning is trivial; for gRPC services, protobuf's additive rules may remove the need for an explicit version at all.

Pick simplest that works
Schema-first
GraphQL additive evolution

Client-driven field selection, a schema registry, tooling that surfaces @deprecated warnings. Treat field-level sunset as the hard part and plan for it explicitly.

Pick additive + @deprecated

08Deprecation & SunsetRetiring a version cleanly.

Choosing a versioning strategy is the easy half. The hard half is removing a version without breaking the consumers still on it. Internet standards exist for the runtime signals: the Sunset HTTP header field, defined in RFC 8594 (2019), carries a timestamp specifying when a resource will become unresponsive. The companion Deprecation header field, standardized as RFC 9745 (Standards Track, published March 2025 and grown out of the draft-ietf-httpapi-deprecation-headerlineage), signals that a resource will be or has been deprecated and can carry a date. Both GitHub and Zalando's public guidelines require these headers.

GitHub puts both into practice: as a version approaches closure it returns a Deprecation header with the date the version closes and a Sunset header with the date requests will start returning 410 Gone. Beyond the wire signals, the machine-readable layer matters too — OpenAPI 3.x supports deprecated: true on individual operations or fields, which drives documentation generators, SDK generators, and linters so that deprecation propagates into the tooling consumers actually use.

How long should you give consumers? The honest answer is that vendors disagree, and the right window depends on your consumer base. The table below puts real, primary-sourced timelines side by side so you can benchmark a policy of your own.

Vendor
GitHub
Support / notice window
≥ 24 months per version after a newer one ships
Sunset mechanics
Deprecation + Sunset headers as a version nears closure; outdated versions return 410 Gone.
Vendor
Twilio
Support / notice window
12 months prior-version support, then 12 months to EOL
Sunset mechanics
Four-stage lifecycle — Latest → Support (bug/security only) → Deprecated → End of Life. A full year's notice before any deprecation.
Vendor
SailPoint
Support / notice window
3 years active support + 2-year transition
Sunset mechanics
Annual release cadence; deprecated APIs stay functional for 2 years, marked Deprecated in the spec with deprecation response headers.
Vendor
Stripe
Support / notice window
Old versions kept alive long-term via per-account pinning
Sunset mechanics
Version transformation walks responses backward through change modules; a 72-hour rollback window follows a major upgrade.
Vendor
Common practice
Support / notice window
6–12 months public deprecation notice
Sunset mechanics
Deprecation + Sunset headers, deprecated: true in the OpenAPI spec, a migration guide, then 410 Gone at the sunset date.
The sunset checklist
A clean retirement has five moving parts: announce with a Deprecation header and a dated Sunset header; mark the affected operations deprecated: true in your OpenAPI spec so tooling picks it up; publish a migration guide with concrete before/after examples; give consumers a window that matches your base (commonly 6 to 12 months); and return 410 Gone at the sunset date so callers fail loudly rather than silently.
At Twilio, we give users a full year's notice before deprecating any API version.— Evan Cummack, Chief Product Officer, Twilio

One layer above the wire signals sits Consumer-Driven Contracts, the service-evolution pattern Ian Robinson described on martinfowler.com in 2006. Instead of a provider declaring its contract unilaterally, each consumer records what it actually uses in a contract file, and the provider validates every consumer contract before deploying a change. The pattern predates today's tooling — Pact, PactFlow, and Spring Cloud Contract now implement it — and it answers the question versioning alone cannot: not "did we change the contract" but "did we break anyone." That only 17% of teams run contract testing, per Postman, is the single biggest gap between versioning hygiene and versioning safety.

09ChoosingPicking the right strategy for your API.

The decision is contextual, but it is not arbitrary. Start from your consumers and your infrastructure, not from the strategy you find most elegant. A public REST API behind a CDN with consumers you do not control wants URL path versioning — it caches cleanly and removes the Vary footgun before anyone can step on it. A fast-moving platform that ships breaking changes regularly and needs to keep old behavior alive for years should study the date-based, account-pinned model GitHub and Stripe use rather than reach for SemVer reflexively.

Whatever you pick, the discipline matters more than the scheme. Most of the failure modes in this guide are not strategy choices — they are missing Vary headers, absent deprecation signals, no migration guide, and no contract testing to catch a break before it ships. Versioning decisions are made at design time, which is why they belong inside an API-first development approach rather than bolted on after the first breaking change. The same instinct that makes you reach for feature flags to roll out changes safely should make you treat a version sunset as a staged rollout, not a flip of a switch.

If your API also has rate-limiting and infrastructure decisions to make alongside versioning, the same decision-matrix lens applies to API rate limiting and to performance considerations at the infrastructure layer. When the decision spans architecture, tooling, and team process at once, our web development and platform engineering work starts with exactly this kind of trade-off analysis on your real consumers and traffic.

10ConclusionVersioning is a caching and migration decision.

The shape of API versioning, mid-2026

The strategy is the easy part — the discipline is what ships.

There is no universally correct API versioning strategy, and any comparison that crowns one is selling something. URL path versioning is the safe public default because it caches cleanly. Header and query schemes buy URL stability at the price of a caching tax most teams underestimate. Date-based versioning is what the platforms with the most consumers — GitHub and Stripe — converged on, because a date is more honest than an integer about what changed and when.

The numbers tell the real story. Sixty percent of teams version their APIs, but only 26% reach for semantic versioning and only 17% run contract testing. The gap is not ignorance; it is that SemVer was built for libraries and a contract change is a different object. The teams that get this right tend to separate the two concerns — SemVer for the SDKs people install, dates or named majors for the behavior of the API itself.

The part worth internalizing is that versioning is not mostly about how you stamp a request. It is about whether you can retire a version without a fire — Deprecation and Sunset headers, a deprecated: true flag your tooling reads, a migration guide, a window that respects your consumers, and a final 410 Gone. Pick the scheme that fits your consumers and your CDN; spend the real effort on the sunset discipline. That is the difference between an API that runs like infrastructure and one that breaks every time you try to improve it.

Design an API that evolves without breaking

Build an API that runs like infrastructure and sunsets without a fire.

Our team designs API versioning and deprecation strategies that fit your consumers and your infrastructure — caching-aware, tooling-driven, and built to sunset cleanly, delivered in weeks not quarters.

Free consultationExpert guidanceTailored solutions
What we work on

API & platform engineering

  • Versioning strategy chosen for your consumers and CDN
  • Deprecation & sunset policy with Deprecation/Sunset headers
  • Consumer-driven contract testing to catch breaks pre-deploy
  • OpenAPI-driven docs, SDKs, and linting pipelines
  • Migration playbooks for moving consumers off old versions
FAQ · API versioning

The questions we get every week.

There is no single best strategy — the right choice depends on your consumers, your caching layer, and how often you ship breaking changes. URL path versioning (e.g., /v1/users) is the safest default for public REST APIs because it caches cleanly at the CDN with no extra configuration. Header versioning keeps URLs stable but breaks standard HTTP caching unless you set a Vary header. Date-based versioning, used by GitHub and Stripe, suits fast-evolving platform APIs. Treat the choice as an engineering decision matrix across CDN behavior, tooling support, and client migration cost rather than an aesthetic preference.