# API Versioning System Low-Level Design

Source: https://www.techinterview.org/post/3233467734/lld-api-versioning/
Updated: 2026-04-18 · techinterview.org

## Why API Versioning Matters

APIs are contracts between your service and its consumers. Once published, breaking changes — renaming fields, changing types, removing endpoints — break existing clients. API versioning lets you evolve your API without breaking existing integrations. Stripe's API versioning is the gold standard: clients pin to a version; breaking changes are released as new versions; old versions are supported for years. Understanding the trade-offs between URL versioning, header versioning, and date-based versioning is a common system design topic.

## Versioning Strategies

**URL versioning** — version in the path: /v1/users, /v2/users


```
GET /v1/users/123   → {"id": 123, "full_name": "John Doe"}
GET /v2/users/123   → {"id": "usr_123", "name": {"first": "John", "last": "Doe"}}
```

Pros: explicit, cacheable, easy to route. Cons: URL pollution; clients must update URLs when migrating.

**Header versioning**: Accept: application/vnd.myapi.v2+json or custom header X-API-Version: 2

Pros: clean URLs. Cons: harder to test in browser, not cached differently by CDN unless Vary header is set.

**Date-based versioning** (Stripe's approach): Stripe-Version: 2023-10-16


```
# Client sends their pinned version date
Stripe-Version: 2023-10-16
# Server applies the schema/behavior for that date
# New customers default to the latest version; existing customers keep their pinned version
```

Pros: granular; clients see exactly what they integrated against. Cons: complex to maintain many date-versions.

## Data Model for Version Management


```
ApiVersion(
    version     VARCHAR PRIMARY KEY,  -- 'v1', 'v2', '2023-10-16'
    status      ENUM(ACTIVE, DEPRECATED, SUNSET),
    released_at TIMESTAMPTZ,
    deprecated_at TIMESTAMPTZ,
    sunset_at   TIMESTAMPTZ,          -- date after which requests return 410 Gone
    changelog   TEXT
)

ClientApiVersion(
    client_id   UUID,
    api_key     VARCHAR,
    pinned_version VARCHAR,
    updated_at  TIMESTAMPTZ
)
```


## Request Routing by Version


```
# Middleware: extract version from URL or header, attach to request context
@app.middleware
def version_middleware(request, call_next):
    # URL versioning: /v2/users → version='v2'
    version = extract_from_path(request.url.path)

    # Header versioning fallback
    if not version:
        version = request.headers.get('X-API-Version')

    # Default to latest stable version
    if not version:
        version = get_latest_stable_version()

    # Check sunset
    api_version = db.get(ApiVersion, version)
    if api_version.status == 'SUNSET':
        return Response(410, 'This API version has been sunset. Please upgrade.')

    # Deprecation warning header
    if api_version.status == 'DEPRECATED':
        request.state.deprecation_warning = (
            f'API version {version} is deprecated and will be sunset on '
            f'{api_version.sunset_at}. Please upgrade.'
        )

    request.state.api_version = version
    response = call_next(request)

    if hasattr(request.state, 'deprecation_warning'):
        response.headers['Deprecation'] = request.state.deprecation_warning
        response.headers['Sunset'] = api_version.sunset_at.isoformat()

    return response
```


## Version-Specific Response Transformation


```
# Handler returns internal model; serializer applies version-specific transformation
def get_user(user_id, api_version):
    user = db.get_user(user_id)

    if api_version == 'v1':
        return {
            'id': user.id,          # integer in v1
            'full_name': user.name  # flat name in v1
        }
    elif api_version >= 'v2':
        return {
            'id': f'usr_{user.id}',    # prefixed string ID in v2
            'name': {                   # structured name in v2
                'first': user.first_name,
                'last': user.last_name
            },
            'created_at': user.created_at.isoformat()
        }
```


## Deprecation and Migration Strategy

  - Release v2 alongside v1 — both serve traffic simultaneously

  - Announce deprecation of v1: add Deprecation and Sunset response headers

  - Email clients using v1 with migration guide and sunset date (minimum 6-12 months)

  - Monitor v1 traffic — track which client_ids still use the old version

  - On sunset date: return 410 Gone with a message pointing to the migration guide

  - After sunset: remove v1 code after another 3-6 months (keep routing to return 410)

## Key Design Decisions

  - URL versioning for public APIs — explicit and easy to understand for third-party developers

  - Date-based for partner APIs — enables per-client pinned versions, fine-grained changelog

  - Sunset headers (RFC 8594) — machine-readable deprecation enables automated client alerts

  - Internal model separate from response schema — versioned serializers transform one internal model to multiple response shapes

  - Never break without versioning — backwards-compatible changes (adding fields) don't require a new version

API versioning and backward compatibility design is discussed in [Stripe system design interview questions](https://www.techinterview.org/post/3233460268/stripe-interview-guide-2026-process-bug-bash-round-and-payment-systems/).

API versioning and deprecation strategy is covered in [Shopify system design interview preparation](https://www.techinterview.org/post/3233459924/shopify-interview/).

API versioning and public API design is discussed in [Atlassian system design interview guide](https://www.techinterview.org/post/3233459919/atlassian-interview/).

See also: [Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence](https://www.techinterview.org/post/3233460274/netflix-interview-guide-2026-streaming-architecture-recommendation-systems-and-engineering-excellence/)

See also: [Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering](https://www.techinterview.org/post/3233460275/scale-ai-interview-guide-2026-data-infrastructure-rlhf-pipelines-and-ml-engineering/)
