API Versioning Strategies

Choose and implement an API versioning strategy that lets you evolve your API without breaking existing clients. Covers URI versioning, header versioning, content negotiation, sunset policies, and migration tooling.

API versioning strategies are the means by which a backend service evolves its interface over time while maintaining compatibility with clients that depend on prior versions. This matters when the service must introduce breaking changes—adding, removing, or modifying fields in request or response bodies, altering semantics, or changing endpoints—without disrupting existing clients. Versioning ensures that clients using old interfaces continue to work, even as new versions are released and adopted.

Versioning via URL Path

When clients need to explicitly target a version, embedding the version in the URL path is the most direct approach. The version is part of the endpoint path, typically at the root or after the base path.

GET /api/v1/users/123 HTTP/1.1
Host: api.example.com
Accept: application/json

The server routes the request based on the v1 segment. The version may be a simple string, such as v1, v2, or v3, or a date-based version like v2024-03-15.

A common failure occurs when the version is not normalized across the service. Clients may send v1, V1, v1.0, or even api/v1, but the server treats them as distinct versions. To avoid this, the routing logic must be case-insensitive and support optional trailing slashes.

Clients can discover the current version by making a GET /api/version request:

{
  "current": "v2",
  "supported": [
    "v1",
    "v2"
  ],
  "latest": "v2"
}

Clients may also use Accept headers to request a version:

GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/vnd.example.users.v2+json

The server parses the Accept header and selects the appropriate version. This pattern is especially useful in microservices where different services use different versioning schemes.

Path Versioning Pitfalls

Versioning via Query Parameters

Query parameters allow clients to specify versioning in the query string. This is especially useful for clients that cannot control the full URL path, such as mobile apps or legacy systems.

GET /api/users/123?version=v1 HTTP/1.1
Host: api.example.com

The server reads the version query parameter and selects the appropriate handler. The version key may be named differently: ver, api_version, or v.

The server must handle multiple versions with a single endpoint. For example, a GET /api/users endpoint may serve different data structures based on the version parameter.

// GET /api/users?version=v1
{
  "users": [
    {
      "id": 123,
      "name": "Alice",
      "email": "[email protected]"
    }
  ]
}
// GET /api/users?version=v2
{
  "users": [
    {
      "id": 123,
      "fullName": "Alice Johnson",
      "email": "[email protected]",
      "createdAt": "2024-01-01T00:00:00Z"
    }
  ],
  "pagination": {
    "currentPage": 1,
    "total": 42
  }
}

The server may also support Accept header versioning in conjunction with query parameters.

Query Parameter Versioning Pitfalls

Versioning via Header

HTTP headers provide a clean, standardized way to specify the API version, especially for clients that send multiple requests to the same service.

GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
X-API-Version: v2

The server reads the X-API-Version header and selects the appropriate version. The header name may vary: API-Version, Accept-Version, Version, or X-Api-Version.

The server may also return the selected version in the response header:

HTTP/1.1 200 OK
Content-Type: application/json
X-API-Version: v2
Content-Length: 142

{
  "id": 123,
  "name": "Alice",
  "email": "[email protected]"
}

Clients can use this to verify that they are interacting with the expected version.

Header Versioning Pitfalls

Versioning via Media Types

Media types (MIME types) allow versioning through the Accept header and the Content-Type header, using a structured format to encode version information.

GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/vnd.example.users.v2+json

The server matches the Accept header to a known media type and selects the appropriate version. The media type may include parameters:

Accept: application/vnd.example.users.v2+json; charset=utf-8; profile=full

The server returns the response with the same media type:

HTTP/1.1 200 OK
Content-Type: application/vnd.example.users.v2+json
Content-Length: 142

{
  "id": 123,
  "fullName": "Alice Johnson",
  "email": "[email protected]"
}

The media type may include version-specific features. For example, v2 may introduce createdAt, while v1 does not.

Media Type Versioning Pitfalls

Versioning via Content Negotiation

Content negotiation combines multiple versioning mechanisms. The server selects the version based on a combination of Accept, X-API-Version, and version query parameters.

GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/vnd.example.users.v2+json
X-API-Version: v1
version: v3

The server applies a priority order to determine the version:

  1. Accept header (highest priority)
  2. X-API-Version header
  3. version query parameter (lowest priority)

The server uses the highest-priority version and returns it in the X-API-Version header.

Content Negotiation Pitfalls

Versioning with Backward and Forward Compatibility

A well-designed versioning strategy includes backward and forward compatibility to reduce the burden of version upgrades.

Backward Compatibility

The server must support both the current version and previous versions. When a new version is introduced, the server must handle requests for old versions.

// GET /api/users/123?version=v1
{
  "id": 123,
  "name": "Alice",
  "email": "[email protected]"
}

The server may return v1 data even when the Accept header specifies v2.

Forward Compatibility

Clients may use a new version before the server supports it. The server must handle unknown fields and additional data.

// GET /api/users/123?version=v2
{
  "id": 123,
  "fullName": "Alice Johnson",
  "email": "[email protected]",
  "createdAt": "2024-01-01T00:00:00Z",
  "preferences": {
    "theme": "dark",
    "notifications": true
  },
  "unknownField": "value"  // not defined in v2 schema
}

The server may ignore unknown fields and return them in the response.

Forward Compatibility Pitfalls

Versioning with Deprecation and Sunset

A versioning strategy must include deprecation and sunset policies.

HTTP/1.1 200 OK
Content-Type: application/json
X-API-Version: v1
Deprecation: true
Deprecation-Until: 2025-06-30
Deprecation-Link: <https://docs.example.com/deprecations/v1>; rel="documentation"
HTTP/1.1 410 Gone
Content-Type: application/json
X-API-Version: v1
Sunset: 2025-06-30

The client may use the Sunset header to inform users that a version is no longer supported.

Deprecation and Sunset Pitfalls

Versioning with Discovery and Metadata

Clients may discover available versions through a dedicated endpoint.

GET /api/version HTTP/1.1
Host: api.example.com
Accept: application/json
{
  "current": "v2",
  "supported": [
    "v1",
    "v2"
  ],
  "latest": "v2",
  "deprecated": [
    "v1"
  ],
  "sunsetting": [
    {
      "version": "v1",
      "until": "2025-06-30"
    }
  ],
  "links": {
    "self": "/api/version",
    "documentation": "/docs/api",
    "changelog": "/changelog"
  }
}

Clients may use this endpoint to select the appropriate version and to inform users about deprecation and sunset.

Discovery and Metadata Pitfalls

Versioning with Multiple Versions in One Service

A single service may support multiple versions simultaneously. The server must route requests based on the version, and the service must maintain backward and forward compatibility.

// GET /api/users/123?version=v1
{
  "id": 123,
  "name": "Alice",
  "email": "[email protected]"
}
// GET /api/users/123?version=v2
{
  "id": 123,
  "fullName": "Alice Johnson",
  "email": "[email protected]",
  "createdAt": "2024-01-01T00:00:00Z"
}

The server may also support v3 with new features, while maintaining v1 and v2.

Multiple Versions Pitfalls

Versioning with Client-Side Routing

Clients may route requests to different services based on the version. The client may maintain a routing table that maps versions to endpoints.

{
  "v1": {
    "url": "https://api.example.com/v1",
    "version": "v1"
  },
  "v2": {
    "url": "https://api.example.com/v2",
    "version": "v2"
  }
}

The client may use this table to select the appropriate endpoint for each request.

Client-Side Routing Pitfalls

Versioning with Error Responses

Error responses must include version information to help clients debug version-related issues.

{
  "error": "NotFound",
  "message": "User not found",
  "details": {
    "userId": 123,
    "version": "v2"
  },
  "links": [
    {
      "rel": "documentation",
      "href": "https://docs.example.com/errors/NotFound"
    }
  ]
}

The server may return 404 Not Found with the X-API-Version header to indicate the version of the error.

Error Responses Pitfalls

Versioning with Testing and Validation

Clients may validate their versioning strategy through automated tests.

curl -H "Accept: application/vnd.example.users.v2+json" \
     -H "X-API-Version: v1" \
     -v \
     https://api.example.com/api/users/123

The client may expect the response to include fullName and createdAt.

Testing and Validation Pitfalls

Versioning with Deployment and Rollout

A versioning strategy must include deployment and rollout policies.

Deployment and Rollout Pitfalls

Versioning with Monitoring and Observability

A versioning strategy must include monitoring and observability.

Monitoring and Observability Pitfalls

Versioning with Documentation and Examples

A versioning strategy must include documentation and examples.

Documentation and Examples Pitfalls

This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.