Skip to main content

API Versioning

API versioning lets an API make breaking changes without breaking existing clients via URI, header, or media-type versions. Version only on breaking changes, keep active versions few, and pair with a clear deprecation policy.

Type
API
When to Use
Breaking Contract Changes, Support Multiple Clients, Long Lived Public APIS, Phased Deprecation

API versioning is the discipline of evolving an API's contract while keeping existing clients working. Because published APIs have consumers you do not control, a breaking change — renaming a field, removing an endpoint, changing semantics — must be introduced in a way that lets old and new clients coexist during a migration window. Versioning provides that coexistence.

How It Works

Several mechanisms exist. URI versioning puts the version in the path (/v1/orders, /v2/orders); it is explicit, cache-friendly, and the most common, at the cost of arguably violating the idea that a URI identifies one resource. Header versioning sends the version in a custom header (X-API-Version: 2), keeping URLs stable but being less visible and harder to test by hand. Media-type (content) versioning uses the Accept header (application/vnd.example.v2+json), the most RESTful approach but the most complex. Query-parameter versioning (?version=2) is simple but easy to omit.

Good practice is to version only on breaking changes and to add new optional fields without bumping the version (tolerant readers). A clear deprecation policy — sunset dates, Deprecation/Sunset headers, and communication — is as important as the mechanism. GraphQL takes a different route, favoring continuous evolution with field deprecation over discrete versions.

When to Use It

Version any long-lived, externally consumed API: public APIs, partner integrations, and internal APIs with many independent consumers. Introduce a new version when you must make a backward-incompatible change. For purely additive changes, evolve in place rather than version, to avoid proliferating versions you must maintain.

Trade-offs

Every active version is code you must run, test, and secure, so versions are a maintenance liability — keep the number small and retire old ones. URI versioning leaks version into resource identity; header/media-type versioning is cleaner but less discoverable and harder to cache and debug. Without a real deprecation process, old versions live forever. Over-versioning (bumping for trivial changes) is a common mistake.

Related Patterns

HATEOAS aims to reduce versioning needs by letting clients follow links instead of hard-coding structure. A backend-for-frontend can absorb version differences per client. An anti-corruption layer can translate between versions internally. Pagination contracts are a frequent subject of versioning, and REST→GraphQL migrations replace versioning with field-level deprecation.

Example

# URI versioning — explicit and cache-friendly
GET /v2/orders/1042

# Media-type versioning — stable URL, version negotiated
GET /orders/1042
Accept: application/vnd.example.v2+json

# Signalling deprecation of v1
HTTP/1.1 200 OK
Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT

Clients on v1 keep working while being told, via standard headers, when the version will be withdrawn.