Skip to main content

API Versioning Strategies: URL vs Header vs Query Parameter

·APIScout Team
api versioningapi designrest apibest practicesapi architecture

API Versioning Strategies: URL vs Header vs Query Parameter

API versioning is inevitable. Your API will change — new fields, renamed endpoints, breaking schema changes. How you version determines whether those changes break clients or coexist gracefully. There are four main strategies, each with tradeoffs.

The Four Strategies

1. URL Path Versioning

Format: /api/v1/users, /api/v2/users

The most common approach. Version is visible in the URL, easy to understand, easy to route. Used by GitHub (/v3), Stripe (/v1), Twitter (/2), and Google APIs.

Pros:

  • Obvious — developers see the version immediately
  • Easy to route (nginx, load balancers, API gateways handle path routing natively)
  • Easy to cache (different URLs = different cache entries)
  • Simple to document (each version has its own OpenAPI spec)
  • Easy to deprecate (shut down a path)

Cons:

  • URL pollution — the resource identity includes a version (philosophically wrong for REST purists)
  • Multiple versions = multiple codebases/routes to maintain
  • Clients must update URLs to upgrade
  • Breaking change to move between versions

Best when: Public APIs, APIs consumed by external developers, when simplicity > purity.

2. Header Versioning

Format: Accept: application/vnd.api+json;version=2 or X-API-Version: 2

Version is in request headers, keeping URLs clean. Used by GitHub (Accept header with preview features) and some enterprise APIs.

Pros:

  • Clean URLs — resource identity isn't polluted with versions
  • RESTful — follows content negotiation principles
  • Can version individual resources independently
  • Supports gradual migration (default to latest, opt-in to old)

Cons:

  • Invisible — developers can't see the version in the URL
  • Harder to test (can't just paste a URL in a browser)
  • Cache configuration is more complex (Vary header required)
  • Documentation is harder (need to explain header usage)
  • API gateways may not route on custom headers easily

Best when: Internal APIs, when REST purity matters, when different resources version at different rates.

3. Query Parameter Versioning

Format: /api/users?version=2

Version is a query parameter. Simple to implement, visible in the URL, but feels like a workaround.

Pros:

  • Easy to implement (just read a query parameter)
  • Visible in the URL
  • Optional — can default to latest if omitted
  • Easy to test (add parameter to any request)

Cons:

  • Pollutes the query string (version isn't a filter, it's a contract)
  • Caching can be tricky (parameter ordering, optional parameters)
  • Doesn't feel intentional — looks like an afterthought
  • Harder to enforce in routing layers

Best when: Quick versioning for internal APIs, migration periods, or when you need a simple escape hatch.

4. Content Negotiation (Accept Header)

Format: Accept: application/vnd.company.resource.v2+json

The most RESTful approach. Version is embedded in the media type. Different versions return different representations of the same resource.

Pros:

  • Most RESTful — versions are representations, not resources
  • Clean URLs
  • Can version individual resources independently
  • Supports multiple formats per version (JSON, XML, etc.)

Cons:

  • Most complex to implement
  • Hardest to understand for API consumers
  • Debugging is difficult (can't see version in URL)
  • Poor tooling support (most API tools don't handle custom media types well)
  • Documentation burden is highest

Best when: Internal APIs with sophisticated consumers, when following REST principles is a priority.


Comparison Table

CriteriaURL PathHeaderQuery ParamContent Negotiation
Visibility✅ Obvious❌ Hidden✅ Visible❌ Hidden
Simplicity✅ Simple⚠️ Medium✅ Simple❌ Complex
RESTfulness⚠️ Not pure✅ Clean⚠️ Hacky✅ Most pure
Caching✅ Easy⚠️ Vary header⚠️ Tricky⚠️ Vary header
Routing✅ Native⚠️ Custom⚠️ Custom❌ Complex
Testability✅ Browser❌ Needs tool✅ Browser❌ Needs tool
Adoption🏆 Most usedMediumLowLow

Real-World Examples

CompanyStrategyFormat
StripeURL path/v1/customers
GitHubURL path + header/v3, Accept: application/vnd.github.v3+json
TwilioURL path/2010-04-01/ (date-based!)
SlackURL path/api/conversations.list (method-based, not versioned)
GoogleURL path/v1/projects
Microsoft GraphURL path/v1.0/me, /beta/me

Best Practices

  1. Start with URL path versioning for public APIs — it's the most understood and tooling-friendly approach.
  2. Use date-based versions if you release frequently (Twilio's approach: /2010-04-01/).
  3. Default to the latest version if no version is specified — don't break existing clients by requiring version.
  4. Deprecate, don't delete — announce deprecation 12+ months before removing old versions.
  5. Version sparingly — most changes can be additive (new fields, new endpoints) without a new version.
  6. Document your versioning policy — how long are versions supported? What constitutes a breaking change?

Building APIs? Explore API design patterns and best practices on APIScout — architecture guides, comparisons, and developer resources.

Comments