mdashikjs/blog
All posts
API Versioning: URL Path vs Headers vs Content Negotiation
System Design

API Versioning: URL Path vs Headers vs Content Negotiation

System Design4 min

API Versioning: URL Path vs Headers vs Content Negotiation

Your API will change. The question is how to evolve it without breaking existing clients. I have used all three common strategies and each has a clear sweet spot.

API DesignRESTVersioningArchitecture
Share:

Strategy 1: URL path versioning

GET /api/v1/users
GET /api/v2/users

Pros: Obvious, easy to understand, easy to route. Clients know exactly which version they are using. Load balancers can route different versions to different servers.

Cons: URL pollution. Every resource gets duplicated. Internal links and documentation get messy.

Best for: Public APIs with long-lived consumers. If your API serves mobile apps that update infrequently, URL versioning gives them a stable target.

Strategy 2: Header versioning

GET /api/users
Accept-Version: 2

Pros: Clean URLs. The version is metadata about the request, not part of the resource identifier.

Cons: Harder to test (you need to set headers, not just change the URL). Some proxies and CDNs do not forward custom headers reliably.

Best for: Internal APIs where you control all clients and can enforce header discipline.

Strategy 3: Content negotiation

GET /api/users
Accept: application/vnd.myapp.v2+json

Pros: RESTful purists love this. The media type describes the representation, which is what HTTP content negotiation was designed for.

Cons: Practically nobody does this well. Client libraries make it awkward. Documentation tools do not support it cleanly.

Best for: Honestly, I have never seen this work well in practice.

My recommendation

For most teams: URL path versioning. It is boring, obvious, and every tool in the ecosystem supports it.

When to version

Not every change needs a new version. Breaking changes do. Non-breaking changes do not.

Breaking: Removing a field, changing a field type, changing authentication, changing error format.

Non-breaking: Adding a field, adding an endpoint, adding an optional parameter.

The migration plan

  1. Ship v2 alongside v1
  2. Add deprecation headers to v1 responses: Deprecation: true
  3. Monitor v1 traffic — do not remove it until traffic is near zero
  4. Give clients at least 6 months of overlap
  5. Remove v1 and redirect to v2 docs

The real answer

Design your v1 to be extensible. Use envelopes ({ data, meta }), make fields optional where possible, and use feature flags instead of version bumps for minor changes. The best API version is the one you never need to ship.

MA

Written by Md Ashik

Senior Software Engineer building reliable backends. I write about the practical tradeoffs behind shipping software that holds up in production.