API Versioning Strategies

Choose the right versioning approach to evolve your API while maintaining backward compatibility

← Back to REST Guide

Why Version Your API?

API versioning is essential for maintaining backward compatibility while allowing your API to evolve. Without proper versioning, changes to your API can break existing clients and integrations.

  • Introduce breaking changes safely
  • Support multiple API versions simultaneously
  • Give clients time to migrate
  • Document API evolution clearly
  • Maintain stable contracts with consumers
v1 /api/v1/users → Stable
v2 /api/v2/users → Current
v3 /api/v3/users → Beta

Versioning Strategies

🔗

URL Path Versioning

The most common and straightforward approach. The version number is embedded directly in the URL path, making it immediately visible and easy to understand.

# Version in the URL path GET /api/v1/users GET /api/v1/users/123 POST /api/v1/orders # Upgrading to a new version GET /api/v2/users GET /api/v2/users/123 POST /api/v2/orders

✅ Advantages

  • Explicit and highly visible
  • Easy to understand and implement
  • Simple to route in web servers
  • Easy to cache at CDN level
  • Works with any HTTP client
  • Simple to test in browser

❌ Disadvantages

  • Technically violates REST (URL = resource)
  • Clutters the URL structure
  • Requires URL changes for version upgrades
  • Can lead to URL proliferation

Query Parameter Versioning

The version is passed as a query parameter. This keeps the base URL clean and makes versioning optional—clients can omit the parameter to use the default version.

# Version as query parameter GET /api/users?version=1 GET /api/users/123?version=1 GET /api/users?version=2 # Default version (when omitted) GET /api/users # Uses latest or default version # Alternative formats GET /api/users?v=1 GET /api/users?api-version=2024-01-15

✅ Advantages

  • Clean, resource-focused URLs
  • Optional—can default to latest
  • Easy backward compatibility
  • Simple to add to existing APIs
  • Flexible parameter naming

❌ Disadvantages

  • Caching issues (query strings often not cached)
  • Can be overlooked or forgotten
  • Mixes versioning with other parameters
  • Some proxies strip query parameters
📋

Header Versioning

Version information is passed through HTTP headers, keeping URLs completely clean. This is considered more "RESTful" as URLs remain purely resource identifiers.

# Using Accept header with version parameter GET /api/users Accept: application/vnd.api+json;version=1 # Using custom version header GET /api/users X-API-Version: 1 # Alternative custom headers GET /api/users API-Version: 2024-01-15 # Microsoft style GET /api/users api-version: 2.0

✅ Advantages

  • Clean, semantic URLs
  • Follows REST principles
  • Version metadata separate from resource
  • Good for API-savvy consumers
  • Can be set globally in HTTP clients

❌ Disadvantages

  • Harder to test in browser
  • Not visible in URL sharing
  • Requires HTTP client configuration
  • Can be accidentally omitted
  • Some tools don't support custom headers easily
🤝

Content Negotiation (Media Type Versioning)

Uses custom media types in the Accept header to specify both the format and version. This is the most "RESTful" approach, treating different versions as different representations of the same resource.

# Vendor-specific media type with version GET /api/users Accept: application/vnd.company.api.v1+json # Version 2 of the same resource GET /api/users Accept: application/vnd.company.api.v2+json # Resource-specific versioning GET /api/users Accept: application/vnd.company.user.v1+json # Date-based versioning GET /api/users Accept: application/vnd.company.api.2024-01+json # Response includes Content-Type HTTP/1.1 200 OK Content-Type: application/vnd.company.api.v2+json

✅ Advantages

  • Most RESTful approach
  • URLs are pure resource identifiers
  • Allows resource-level versioning
  • Self-documenting media types
  • Supports hypermedia patterns

❌ Disadvantages

  • Complex to implement and test
  • Not intuitive for newcomers
  • Caching configuration more complex
  • Limited tooling support
  • Steeper learning curve

Deprecation Strategies

Properly deprecating API versions is crucial for a good developer experience. Give your consumers time and tools to migrate smoothly.

📢 1. Announce Deprecation

Communicate through documentation, changelog, emails, and the API itself. Give at least 6-12 months notice for major versions.

Deprecation: version="1.0", date="2024-06-01"
Link: <https://api.example.com/docs/migration>; rel="deprecation"

⏰ 2. Add Sunset Header

Use the standard Sunset HTTP header (RFC 8594) to indicate when an API version will be removed.

Sunset: Sat, 01 Jun 2024 00:00:00 GMT
Link: <https://api.example.com/v2/users>; rel="successor-version"

⚠️ 3. Deprecation Warnings in Response

Include deprecation warnings in API responses to alert developers during development and testing.

{ "data": { ... }, "_warnings": [ { "type": "deprecation", "message": "API v1 is deprecated. Please migrate to v2.", "sunset": "2024-06-01", "migration_guide": "https://docs.example.com/migrate-v1-to-v2" } ] }

📚 4. Provide Migration Guides

Create comprehensive migration documentation with code examples, breaking changes list, and step-by-step upgrade instructions.

🔄 5. Parallel Running Period

Run both versions simultaneously for an extended period. Monitor usage of deprecated version and reach out to heavy users directly.

🚫 6. Graceful Shutdown

When the sunset date arrives, return 410 Gone with a helpful message pointing to the new version.

HTTP/1.1 410 Gone Content-Type: application/json { "error": "api_version_retired", "message": "API v1 has been retired as of 2024-06-01", "upgrade_url": "https://api.example.com/v2/", "documentation": "https://docs.example.com/v2/" }

Comparison Table

Choose the right versioning strategy based on your API's needs and your consumers' expectations.

Aspect URL Path Query Param Header Media Type
Visibility ⭐⭐⭐ High ⭐⭐ Medium ⭐ Low ⭐ Low
RESTfulness ⭐ Low ⭐⭐ Medium ⭐⭐⭐ High ⭐⭐⭐ High
Implementation Easy Easy Medium Complex
Caching ⭐⭐⭐ Excellent ⭐ Poor ⭐⭐ Good ⭐⭐ Good
Testing Easy Easy Medium Complex
Browser Support ⭐⭐⭐ Native ⭐⭐⭐ Native ⭐ Limited ⭐ Limited
Used By Twitter, Stripe, Google AWS, Google Microsoft, GitHub GitHub (also)

When to Use Each Method

🔗 URL Path Versioning

Best for public APIs with diverse consumers, when discoverability matters, and when you want simple CDN caching. Most common choice for REST APIs.

❓ Query Parameters

Good for internal APIs, when backward compatibility is paramount, or when adding versioning to existing unversioned APIs.

📋 Header Versioning

Ideal for APIs consumed by technical teams with proper HTTP clients, when URL cleanliness is a priority, or enterprise integrations.

🤝 Media Type

Perfect for mature APIs following strict REST/HATEOAS principles, when different versions need different representations, or complex enterprise systems.

Versioning Best Practices

📝 Use Semantic Versioning

✅ Good v1, v2, v3 (major versions only) 2024-01-15 (date-based)
❌ Bad v1.2.3 (too granular for APIs) latest, stable (ambiguous)

🎯 Version at API Level

✅ Good /api/v2/users /api/v2/orders
❌ Bad /api/v1/users /api/v2/orders (mixed versions)

📖 Document All Versions

✅ Good Separate docs per version
Clear changelog between versions
❌ Bad Single documentation for all
No migration guides

⏰ Set Clear Timelines

✅ Good 12-month deprecation notice
6-month parallel operation
Sunset headers
❌ Bad Surprise deprecations
Immediate shutdowns
No advance warning

🔄 Support N-1 Rule

✅ Good Support current + previous version
e.g., v3 (current) + v2 (supported)
❌ Bad Supporting too many versions
Only supporting latest

🏷️ Default Version Strategy

✅ Good Require explicit version
400 if version missing
❌ Bad Silently use latest version
Breaking changes without notice