API Versioning Strategies

Learn how to manage breaking API updates using URI paths, query params, headers, and media types, while coordinating schema translation and deprecations.

BeginnerAPI DesignChapter: API Design10 min read

The Concept

Software changes. As features are added, field schemas expand, database models are normalized, and endpoint interactions are updated. However, while a developer can redeploy a web frontend instantly, they cannot force external integration clients, mobile applications, or SDKs to update simultaneously.

API Versioning is the structural practice of running multiple concurrent representation interfaces of a single API. This allows developers to deliver breaking updates and modern schemas to new integrations while preserving the behavior and structural expectations of legacy clients.

API Version Routing: URI Path vs Accept HeaderClientApp IntegrationGET /v1/usersAccept: v2+jsonAPI Gateway RouterURI Path RuleHeader NegotiatorLegacy ServiceHandles v1 representationModern ServiceHandles v2 representation


Practical Analogy

Think of API Versioning as the power adapter sockets in a hotel:

  • No Versioning is like a hotel changing all physical wall outlets overnight to a new configuration. Suddenly, guests with older phone chargers cannot plug in their devices, breaking utility access.
  • URI Path Versioning is like the hotel installing two physical sockets side-by-side: a legacy three-pin plug socket and a modern USB-C outlet. The guest looks at the wall, identifies the socket that fits their cord, and connects.
  • Header Versioning is like a single universal socket. The socket detects the type of plug inserted and adjusts its internal electrical output accordingly, negotiating compatibility behind the scenes without cluttering the wall space.

Versioning Strategies Compared

APIs utilize four primary strategies to determine which representation version to process:

1. URI Path Versioning

The version prefix is hardcoded directly into the URL path structure.

  • Example: https://api.example.com/v1/users
  • Pros: Simple to route at the network layer, highly readable, works out-of-the-box with simple web browsers.
  • Cons: Violates pure REST design principles: a resource should have a single unique URL, rather than switching paths based on the requested model structure.

2. Query Parameter Versioning

The requested version is passed as a URL query parameter string.

  • Example: https://api.example.com/users?version=2
  • Pros: Easy to implement, defaults to a standard version if the query parameter is omitted.
  • Cons: Query parameters complicate URL parsing and are easily stripped or modified by analytics, tracking, or proxy systems.

3. Custom Header Versioning

Clients request specific interfaces by including a proprietary header in the transaction request.

  • Example: X-API-Version: 2
  • Pros: Preserves clean, resource-centric URLs.
  • Cons: Requires clients to configure custom request engines, preventing simple browser link testing.

4. Content Negotiation (Media Type)

Clients request versions by specifying custom media types in the standard HTTP Accept header.

  • Example: Accept: application/vnd.company.v2+json
  • Pros: The most semantically correct REST approach. The URL represents the resource, while the Accept header negotiates the requested format (representation).
  • Cons: Highly complex routing logic, difficult for novice API consumers to understand, and complicates HTTP proxy behavior.

Routing and Caching Impacts

API versioning selection dramatically affects Content Delivery Networks (CDN) and caching layers:

  • URI Path Caching: CDNs cache requests based on the URL string. When versions are isolated in the URI path (e.g. /v1/users vs /v2/users), each version naturally receives its own cache key. This is highly efficient and safe.
  • Header-Based Caching: When versioning relies on headers (like Accept or X-API-Version), the cache key is identical if it only reads the URL path. If a CDN returns a cached v1 representation to a modern client requesting v2, the client application breaks.
  • The Vary Header: To prevent cache poisoning in header-based architectures, the origin server must return a Vary: Accept or Vary: X-API-Version header. This instructs CDNs to split the cache entry into multiple sub-keys based on the header value, increasing cache management complexity.

API Gateway Integration and Routing

In distributed systems, versioning is often managed at the API Gateway layer rather than inside individual application codebases.

An API Gateway intercepts requests, parses the version indicator (URI prefix or header parameter), and proxies the traffic to distinct backend microservice clusters. For instance, /v1/users can be routed to a legacy service cluster running an older container build, while /v2/users is directed to a modern cluster. This architecture prevents developers from bloating a single codebase with legacy controller routes, allowing teams to build, deploy, and scale versions independently.


Schema Translation and Controller Layout

If version routing must occur inside a single application server, the system should avoid mixing version translation logic with core business logic. Software designs use two patterns:

The Controller Routing Split

Create separate directory structures and classes for each version controller (e.g. controllers/v1/user.go and controllers/v2/user.go). The HTTP router forwards requests to the appropriate class.

The Dynamic Schema Translation Layer

The controller always reads and writes the latest internal database schema. If a client requests an older version, the request passes through a translation middleware. This middleware maps the database entity back to the legacy schema structure before serializing the output (e.g., merging split name fields or restoring deprecated attributes).


Deprecation and Sunset Strategies

No API version can run indefinitely. Maintaining multiple versions increases support costs, security auditing overhead, and database migration complexity.

To safely decommission legacy endpoints, APIs communicate lifetimes using standardized HTTP headers:

  • Deprecation: Signals that the endpoint is deprecated and should not be used in new integrations (e.g. Deprecation: @1718222400 indicating date or epoch).
  • Sunset: Announces the timestamp when the endpoint will be turned off and deleted (e.g. Sunset: Fri, 31 Dec 2027 23:59:59 GMT).

These headers allow automated client systems or monitoring tools to alert integration engineers to upgrade their API calls before the legacy endpoint is turned off.


Further Reading

Prerequisites

Code Examples

Core Literature References

Versioning Web APIs

by Erik Wilde — API Evolution and Version Negotiation Mechanics

View source