API versioning is one of those decisions that feels premature on day one and catastrophically important on day three hundred. Breaking changes to a public API cascade into client outages, angry partners, and emergency hotfixes. The right versioning strategy depends on your API's audience, release cadence, and operations model — and the wrong one means you are maintaining five parallel codebases forever.
What changed in 2026
- API gateways automated version routing. Kong, AWS API Gateway, and Apigee can now route by version with minimal config, making the operational cost of multiple versions much lower.
- OpenAPI 3.1 is the de facto contract format. Versioned OpenAPI specs generate client SDKs automatically, reducing the migration burden on consumers.
- GraphQL and tRPC pushed "versionless" APIs — schema evolution without versioning works well for internal APIs but still needs a deprecation story.
- The
Deprecation and Sunset HTTP headers (RFC 8594) became widely adopted, enabling machine-readable deprecation notices rather than relying on docs.
Versioning strategies compared
| Strategy |
Example |
Pros |
Cons |
| URL path |
/v1/users |
Explicit, cacheable, easy to route |
Pollutes URL space |
| Query param |
/users?version=1 |
No URL change |
Breaks HTTP caching |
| Header |
API-Version: 2026-05-01 |
Clean URLs |
Harder to test in browser/curl |
| Content-Type |
Accept: application/vnd.api+json; version=2 |
REST-pure |
Complex client code |
| Date-based |
API-Version: 2026-01-01 |
Self-documenting timeline |
Confusing for semver thinkers |
URL versioning (recommended default)
Simple to implement, easy to test, and cache-friendly:
GET /v1/users/123 HTTP/1.1
GET /v2/users/123 HTTP/1.1
Route at the API gateway level — no application code changes needed to add a version:
# Kong route config
- name: users-v1
paths: ["/v1/users"]
service: users-service-v1
- name: users-v2
paths: ["/v2/users"]
service: users-service-v2
Keep versions as separate router branches or apps. Do not layer version conditionals through your business logic.
Header versioning
Cleaner URLs, but requires discipline in docs and tooling:
GET /users/123 HTTP/1.1
Host: api.example.com
API-Version: 2026-05-01
Stripe uses this pattern effectively with date-based versions, and it works well for platforms with long-lived integrations. The trade-off: you lose HTTP cache differentiation unless you Vary: API-Version on responses.
What constitutes a breaking change
Breaking (requires a new version):
- Removing a field from a response
- Renaming a field
- Changing a field type (string → number)
- Changing authentication scheme
- Removing an endpoint
- Making an optional parameter required
Non-breaking (safe to add to current version):
- Adding a new optional field to a response
- Adding a new optional request parameter
- Adding a new endpoint
- Adding a new enum value (with caveats — clients that switch on enum must handle unknowns)
Deprecation workflow
# Response headers on a deprecated endpoint
Deprecation: true
Sunset: Sat, 01 Aug 2026 00:00:00 GMT
Link: <https://docs.example.com/migration/v2>; rel="deprecation"
Steps for a clean deprecation:
- Announce the new version and a sunset date (minimum 6 months for public APIs, 3 months for internal).
- Add
Deprecation and Sunset headers to all deprecated endpoints.
- Log clients still hitting deprecated routes so you can contact them.
- Monitor traffic to v1 — do not sunset while there is still significant usage.
- Return a
410 Gone (not 404) after sunset so clients get a clear signal.
How to pick a versioning strategy
- Public-facing REST API? URL versioning — explicit, cacheable, and clients can see the version in logs and URLs.
- Internal API within a single company? Header or date-based versioning — cleaner URLs, you can mandate client updates.
- GraphQL? Avoid versioning; use
@deprecated on fields and schema evolution. Only reach for versioning if field removal is truly necessary.
- Event / webhook payloads? Include a
version field in the payload body itself — "version": "2" — and use content negotiation or separate topics.
- SDK-based API (client libraries control the contract)? Semver the SDK and manage the protocol change inside the library.
Common mistakes
Releasing v2 for additive changes. Adding a new optional nickname field to a user object is backwards-compatible. It does not need a new version.
Coupling version logic deep in business code. Version routing should live at the gateway or router layer. Business logic should not contain if version == 2 branches.
No sunset policy. Without a committed sunset date, deprecated versions run forever and the maintenance burden compounds.
Forgetting to version webhooks. Outbound events have consumers you cannot force to update — version your webhook payloads just like your REST endpoints.
Inconsistent versioning. Some endpoints on v2, others still on v1 with no clear rule. Document which resources are on which version.
What to skip
- Query-parameter versioning — breaks proxy caches and is easy to omit accidentally.
- Implicit versioning (different behaviour based on
User-Agent) — opaque and impossible to document or test.
- Keeping more than 2 live versions unless your API has enterprise SLAs that demand it — the maintenance overhead is brutal.
FAQ
How do I handle versioning in OpenAPI?
Generate a separate spec file per version (openapi-v1.yaml, openapi-v2.yaml) and serve them at /v1/openapi.json and /v2/openapi.json. Keep them in the same repo and diff them to catch breaking changes in CI.
Should I use semver for APIs?
Semver (major.minor.patch) works well conceptually, but in practice most REST APIs only expose a "major" version publicly. Minor and patch versions are internal — clients do not need to know about them.
How long should I maintain old versions?
For public APIs: 12 months minimum after the successor launches, or until traffic drops below ~1%. For internal APIs: 3–6 months with a clear migration guide.
What if I need to change authentication?
Authentication changes are always breaking. Version the entire API, not just the affected endpoints, and give clients a long runway.
Where to go next
See How to document an API in 2026, How to paginate an API in 2026, and REST vs GraphQL in 2026.