ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

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.

APIs are contracts. Changing the response shape, renaming fields, or removing endpoints breaks every client that depends on the current behavior. API versioning is how you evolve the contract without violating existing agreements.


Versioning Approaches

URI Path Versioning

GET /api/v1/orders/456
GET /api/v2/orders/456

Pros: Obvious, easy to route, easy to test, cacheable. Cons: URL proliferation, harder to deprecate gradually.

This is the most common approach and the right default for most teams.

Header Versioning

GET /api/orders/456
Accept: application/vnd.example.v2+json

Pros: Clean URLs, same resource at different versions. Cons: Harder to test (need custom headers), harder to cache, invisible in logs.

Query Parameter Versioning

GET /api/orders/456?version=2

Pros: Simple. Cons: Mixes versioning with business parameters, cache-key pollution.


What Triggers a New Version

Not every change needs a version bump:

ChangeBreaking?Version Bump?
Add a new field to responseNoNo
Add a new optional parameterNoNo
Remove a field from responseYesYes
Rename a fieldYesYes
Change field type (string → int)YesYes
Change error formatYesYes
Add a new endpointNoNo
Remove an endpointYesYes

Rule: Additive changes are non-breaking. Subtractive or structural changes are breaking.


Running Multiple Versions

Routing Layer

# API router that dispatches by version
@app.route('/api/v1/orders/<order_id>')
def get_order_v1(order_id):
    order = OrderService.get(order_id)
    return OrderSerializerV1(order).to_json()

@app.route('/api/v2/orders/<order_id>')
def get_order_v2(order_id):
    order = OrderService.get(order_id)
    return OrderSerializerV2(order).to_json()

The business logic (OrderService) is shared. Only the serialization layer differs between versions.

Adapter Pattern

class OrderSerializerV1:
    def serialize(self, order):
        return {
            "id": order.id,
            "total": order.total,           # float
            "customer_name": order.customer.name
        }

class OrderSerializerV2:
    def serialize(self, order):
        return {
            "id": order.id,
            "total": {                      # structured object
                "amount": order.total,
                "currency": order.currency
            },
            "customer": {                   # nested object
                "id": order.customer.id,
                "name": order.customer.name
            }
        }

Deprecation and Sunset

Sunset Header

HTTP/1.1 200 OK
Sunset: Sat, 01 Jun 2027 00:00:00 GMT
Deprecation: true
Link: <https://api.example.com/v3/docs>; rel="successor-version"

Deprecation Policy

Phase 1: Announce deprecation (6 months before sunset)
  - Add Sunset header to all v1 responses
  - Email API consumers
  - Update documentation

Phase 2: Warning period (3 months before sunset)
  - Log all v1 usage with consumer identification
  - Send targeted migration reminders to active consumers

Phase 3: Sunset
  - Return 410 Gone for v1 endpoints
  - Include migration guide URL in error response

Migration Tooling

Help consumers migrate with codemods, compatibility layers, or dual-write periods:

# Provide a compatibility shim
curl https://api.example.com/v1/orders/456
# Returns v1 format with deprecation warning header

curl https://api.example.com/v2/orders/456
# Returns v2 format

Anti-Patterns

Anti-PatternConsequenceFix
No versioning at allEvery change risks breaking clientsVersion from day one
Too many active versionsMaintenance burden, bug surfaceMax 2-3 active versions
Breaking changes without version bumpSilent client failuresAutomated contract testing
Infinite version supportLegacy code never diesEnforce sunset policy
Versioning internal APIsUnnecessary overheadOnly version external/public APIs

API versioning is about respecting your consumers’ investment in your API while preserving your ability to evolve. Get the strategy right early — retrofitting versioning onto an established API is painful for everyone.

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →