1. Why Version Your API?
Once an API is in production and clients are depending on it, you cannot simply change it. A client that calls /users/123 and expects a full_name field will break if you rename it to name. API versioning solves this by letting you introduce breaking changes in a new version while existing clients continue using the old version.
The key principle: you are not versioning your code, you are versioning the contract between your API and its clients. A version bump signals that the contract has changed in a way that requires client updates.
2. Breaking vs Non-Breaking Changes
| Change Type | Breaking? | Examples |
|---|---|---|
| Add optional field to response | No | Adding "profile_picture" to user object |
| Add new optional request parameter | No | Adding optional "include_deleted=true" param |
| Add new endpoint | No | New /v1/subscriptions endpoint |
| Remove field from response | Yes | Removing "full_name", client code breaks |
| Rename a field | Yes | "full_name" → "name" |
| Change field data type | Yes | amount: string → amount: integer |
| Remove an endpoint | Yes | DELETE /v1/legacy-reports |
| Change required→optional params | No | Making "email" optional |
| Change optional→required params | Yes | Making "phone" required |
3. Strategy 1 — URL Versioning
The version number is embedded in the URL path. This is the most common strategy and the easiest to implement, route, and document.
URL Versioning examples:
GET https://api.example.com/v1/users/123
POST https://api.example.com/v2/payments
GET https://api.example.com/v3/products?page=1
At the API Gateway / load balancer level:
/v1/* → service-v1 (legacy)
/v2/* → service-v2 (current)
/v3/* → service-v3 (beta)
Pros: explicit, easy to route, easy to cache, curl/browser friendly
Cons: clutters URLs, can lead to parallel codebases, versioned links
in bookmarks/docs go stale
4. Strategy 2 — Header Versioning
The version is specified in a custom HTTP request header. The URL stays clean, but the version is less discoverable and harder to test in a browser.
Stripe's Date-Based Versioning
Stripe uses date-based version strings (e.g. 2024-06-20) instead of v1/v2/v3. Each API key has a default version locked at the time it was created. Developers can test new versions by setting the Stripe-Version header, then upgrade their API key's default once they have validated compatibility. This approach avoids the "when does v3 become v4?" question — every change that needs a version gets a new date.
5. Strategy 3 — Query Parameter Versioning
The version is passed as a URL query parameter. Simple to implement but considered less clean than URL or header versioning.
6. Strategy 4 — Content Negotiation
The version is expressed through the Accept and Content-Type headers using media type versioning. This is the most REST-pure approach but the least practical for most teams.
7. Versioning Strategy Comparison
| Strategy | URL Cleanliness | Discoverability | Caching | Used By |
|---|---|---|---|---|
| URL versioning | Cluttered | Excellent | Native (URLs differ) | Stripe, Twilio, AWS, most APIs |
| Header versioning | Clean | Poor (hidden in header) | Requires Vary header | Stripe (Stripe-Version), custom APIs |
| Query parameter | OK | Good | Works (URL differs) | Google Maps, older APIs |
| Content negotiation | Very clean | Poor | Requires Vary header | GitHub v3 (Accept header) |
8. API Deprecation — Sunset Headers
When you decide to retire an old API version, you must communicate the timeline to clients. The Sunset HTTP header (RFC 8594) provides a machine-readable deprecation date:
Never Break Existing Versions Without Notice
Removing or changing an API version without adequate notice destroys developer trust. Follow industry standard: minimum 6–12 months notice for deprecation of publicly documented API versions. Even with notice, keep the old version available as long as traffic exists. Stripe has maintained some API versions for over a decade because enterprise clients cannot update quickly. Build your versioning infrastructure to support long-lived parallel versions.
9. API Gateway Versioning
In a microservices architecture, the API gateway (Kong, AWS API Gateway, Nginx) handles version routing so individual services do not need to be aware of versioning:
- Route
/v1/*to the v1 service cluster - Route
/v2/*to the v2 service cluster - Strip the version prefix before forwarding to the upstream service
- Add deprecation headers at the gateway level for old versions
- Rate-limit old versions more aggressively to encourage migration
This separation means you can run v1 and v2 of a service in parallel without changing the service code — the gateway handles the routing and clients are isolated from the underlying service topology.
How We Research and Update This Guide
We test the underlying formula or workflow, compare outputs with reliable references, and revise examples whenever the page content changes.
- The workflow or formula is tested directly in the tool and compared against independent reference examples.
- Examples are kept practical so readers can verify the result without hidden assumptions.
- Pages are revised whenever the interface, calculation flow, or surrounding guidance materially changes.
Frequently Asked Questions — API Versioning
API versioning allows you to evolve your API over time by introducing breaking changes without breaking existing clients. When you need to change a response structure, rename a field, or remove an endpoint, clients using the old version continue to work while new clients adopt the updated version. Without versioning, any breaking change immediately breaks all existing integrations.
A breaking change is any change that requires existing clients to update their code. Breaking changes include: removing a field from a response, renaming a field, changing a field's data type (e.g. string to integer), removing an endpoint, changing required parameters to required with different semantics, and changing HTTP status codes. Non-breaking (additive) changes include: adding a new optional field to a response, adding a new optional request parameter, and adding a new endpoint.
URL versioning (/v1/users, /v2/users) is the most widely used strategy because it is explicit, easy to route at the infrastructure level (load balancers, API gateways), easy to test in a browser or curl, and easy to document. The trade-off is that it clutters URLs and can lead to multiple parallel codebases. Stripe, Twilio, GitHub, and most major public APIs use URL versioning.
Stripe uses a date-based versioning scheme via the Stripe-Version HTTP header (e.g. Stripe-Version: 2024-06-20). Each API key has a default version set at creation time. Stripe maintains backward compatibility for each version indefinitely and sends advance notice before deprecating versions. This allows Stripe to evolve the API frequently without forcing clients to update their integrations.
The Sunset header (RFC 8594) is a standard HTTP response header that indicates when an API version or endpoint will be decommissioned. Example: Sunset: Sat, 31 Dec 2026 23:59:59 GMT. Clients that parse this header can proactively warn developers before an endpoint goes dark. Combine it with the Deprecation header (an IETF draft) to indicate that the current version is deprecated but not yet sunset.
Semantic versioning (MAJOR.MINOR.PATCH) works well for libraries and SDKs. For REST APIs, most teams only version on MAJOR (breaking) changes, incrementing v1 → v2 → v3. MINOR and PATCH changes are backward-compatible and do not require a version bump. Date-based versioning (like Stripe uses) is an alternative that communicates recency clearly. GraphQL APIs often avoid versioning altogether by using additive changes and deprecating individual fields.