API Versioning
API versioning lets you evolve an API without breaking existing clients. Three strategies: URL path prefix (/v1/), Accept header versioning, and query parameter versioning. URL prefix is the most widely adopted (Stripe, GitHub, Twilio all use it). Never make breaking changes to an existing version.
Details
API versioning is how you manage change without breaking clients. Without versioning, any breaking change breaks all clients immediately.
URL path prefix (most common): /v1/users, /v2/users Visible, cacheable, easy to test in a browser. Clients explicitly opt into new versions. Used by Stripe, GitHub, Twilio, Shopify. Downside: multiple versions require running multiple route sets in parallel.
Accept header versioning: Accept: application/vnd.api+json;version=2 Theoretically more RESTful (URL identifies the resource, header identifies representation). Harder to test in browser. Less commonly adopted.
Query parameter versioning: GET /users?api_version=2026-01-15 Convenient for testing. Breaks caching (version in query string). Not recommended for production APIs.
Date-based versioning (Stripe/Anthropic pattern): GET /users (with Stripe-Version: 2024-09-30 header) Anthropic-Version: 2023-06-01 Each version is a date snapshot. Backward-compatible changes within a version; breaking changes require a new date version. Clients opt in to new versions explicitly.
Breaking vs non-breaking changes: Breaking: removing a field, changing a field type, changing URL structure, changing status codes. Non-breaking: adding new optional fields, adding new endpoints, adding new optional query params. Never make breaking changes to a published version. Create a new version instead.
URL Examples
| Pattern | Description |
|---|---|
| /api/v1/users | URL path prefix (most common) |
| /api/v2/users | New version at new prefix |
| Accept: application/vnd.example+json;version=2 | Header versioning |
| Stripe-Version: 2024-09-30 | Date-based header (Stripe pattern) |
Do
- +Pick one versioning strategy and apply it consistently from day one
- +Maintain previous versions for at least 12–18 months after a new version launches
- +Communicate deprecation timelines clearly in response headers: Deprecation: version=v1, Sunset: Sat, 1 Jan 2027 00:00:00 GMT
- +Document what changed between versions in a changelog
- +Consider date-based versioning (Stripe pattern) for mature APIs with many clients
Don't
- !Never make breaking changes to a published API version
- !Never use version numbers beyond v3 without questioning whether it's the right path
- !Never use /v1.1/ or /v1.2/ – use /v1/ and /v2/ only
- !Never mix versioning strategies across the same API
Examples
# v1 endpoint – still supported
GET /api/v1/customers/cus_123
→ { "id": "cus_123", "name": "Alice", "card_last4": "4242" }
# v2 endpoint – new field names, removed card_last4
GET /api/v2/customers/cus_123
→ { "id": "cus_123", "name": "Alice", "paymentMethods": [...] }
# Deprecation header on v1 responses
Deprecation: version=v1
Sunset: Sat, 1 Jan 2027 00:00:00 GMT
Link: </api/v2/customers>; rel="successor-version"