API Versioning Strategies (URI vs Header vs Media Type)

Versioning is unavoidable once multiple clients depend on your API. A durable strategy must preserve backward compatibility, enable safe rollouts, and support client-specific migrations without breaki

API Versioning Strategies (URI vs Header vs Media Type)#

Versioning is unavoidable once multiple clients depend on your API. A durable strategy must preserve backward compatibility, enable safe rollouts, and support client-specific migrations without breaking caches or observability.

Goals of a Versioning Strategy#

A strong versioning strategy should:

  • Make breaking changes explicit and discoverable.
  • Allow old and new clients to coexist.
  • Keep documentation and SDKs in sync.
  • Avoid routing ambiguity and cache pollution.

URI Versioning#

Example: /v1/orders and /v2/orders.

Pros:

  • Simple to route and cache.
  • Easy to observe in logs and metrics.
  • Works well with API gateways and CDN caching.

Cons:

  • URL changes propagate everywhere.
  • Harder to do per-field deprecation within the same version.
  • May encourage coarse-grained version bumps.

Header Versioning#

Example: X-Api-Version: 2.

Pros:

  • URLs stay stable.
  • Supports granular or per-client versioning.
  • Easier to keep links stable in documentation.

Cons:

  • Harder to cache and debug.
  • Non-standard headers can be blocked by proxies.
  • Requires more attention in client SDKs.

Media Type (Content Negotiation) Versioning#

Example: Accept: application/vnd.example.orders+json;version=2.

Pros:

  • Aligns with HTTP content negotiation semantics.
  • Allows multiple representations of the same resource.
  • Works well for evolutionary schema changes.

Cons:

  • Complex to implement correctly.
  • Harder for humans to inspect and debug.
  • Requires strict documentation discipline.

Recommendation for Advanced Systems#

Use URI versioning for major breaking changes and media type versioning for gradual, representation-level changes. This gives a clear upgrade path while enabling more granular control inside a version.

Spring Boot Routing Examples#

1
2
3
4
5
6
7
8
@RestController
@RequestMapping("/v1/orders")
class OrderV1Controller {
    @GetMapping
    public List<OrderV1> listOrders() {
        return List.of();
    }
}
1
2
3
4
5
6
7
8
@RestController
@RequestMapping("/orders")
class OrderHeaderVersionController {
    @GetMapping(headers = "X-Api-Version=2")
    public List<OrderV2> listOrdersV2() {
        return List.of();
    }
}
1
2
3
4
5
6
7
8
@RestController
@RequestMapping("/orders")
class OrderMediaTypeVersionController {
    @GetMapping(produces = "application/vnd.example.orders+json;version=2")
    public List<OrderV2> listOrdersV2() {
        return List.of();
    }
}

Version Lifecycle Management#

A predictable lifecycle reduces operational risk:

  • Deprecation headers: add Deprecation and Sunset headers.
  • Dual-write or transform: write data in a backward-compatible shape during migration.
  • Traffic shaping: use the API gateway to gradually migrate clients.

Observability and Compatibility Testing#

Implement contract tests that run against both versions. Track version usage in metrics to know when it is safe to retire older versions.

Summary#

URI versioning is operationally simplest, header versioning is client-friendly, and media type versioning is semantically correct but more complex. Mature APIs often mix these strategies to balance compatibility with operational clarity.

Contents