API Versioning: Why It Matters and How to Get It Right
Every API evolves. Fields get renamed, endpoints get restructured, authentication methods change. Without a versioning strategy, every change risks breaking your consumers' integrations. Versioning is your contract with clients: "this version will keep working the way you expect."
There are three primary versioning strategies in widespread use. Each has genuine advantages and real trade-offs.
Strategy 1: URI Path Versioning
The version number is embedded directly in the URL path:
GET /v1/users/123
GET /v2/users/123
Pros:
- Immediately visible — the version is obvious from the URL alone
- Easy to test in a browser or with curl
- Simple to route at the gateway or reverse proxy level
- Can be cached by CDNs and proxies without configuration
Cons:
- URLs are technically supposed to identify resources, not representations — purists argue this violates REST principles
- Can result in URL sprawl as versions multiply
Used by: Stripe (/v1/), Twilio, GitHub, most major public APIs. This is the de facto standard for a reason — simplicity wins.
Strategy 2: HTTP Header Versioning
The version is specified in a custom request header, keeping URLs clean:
GET /users/123
Accept-Version: 2
# or
API-Version: 2025-01-01
Pros:
- URLs remain stable and "pure" — the resource URL doesn't change
- Aligns with HTTP content negotiation semantics
- Supports date-based versioning (e.g., Stripe uses this for their API version dates)
Cons:
- Harder to test — you can't just paste a URL in a browser
- CDN caching is more complex (requires Vary headers)
- Less discoverable for new API consumers
Used by: Stripe's supplemental versioning (date-based), some enterprise APIs. Often combined with URI versioning.
Strategy 3: Query Parameter Versioning
The version is passed as a query string parameter:
GET /users/123?version=2
GET /users/123?api-version=2024-01
Pros:
- No URL structure changes
- Easy to add to existing clients
- Optional — can fall back to a default version if omitted
Cons:
- Can clutter URLs, especially when combined with other query params
- Caching behavior varies — query strings may or may not be cached by proxies
- Less explicit than URI versioning
Used by: Microsoft Azure REST APIs, some Google APIs. Common in enterprise settings.
Comparison at a Glance
| Strategy | Discoverability | Caching | REST Purity | Ease of Use |
|---|---|---|---|---|
| URI Path | High | Excellent | Moderate | High |
| HTTP Header | Low | Complex | High | Moderate |
| Query Param | Medium | Variable | Moderate | High |
Best Practices Regardless of Strategy
- Define your deprecation policy upfront. Tell consumers how long old versions will be supported before sunset.
- Use semantic versioning or date-based versioning consistently. Don't mix
v2and2024-01-15in the same API. - Make breaking vs. non-breaking changes explicit. Adding optional fields is non-breaking; removing required fields is breaking.
- Communicate changes proactively. Changelogs, email notifications, and Sunset headers (
Sunset: Sat, 31 Dec 2025 23:59:59 GMT) are all useful signals. - Version from day one. Even if you start with
v1and never bump it, it sets the expectation that versions exist.
For most APIs, URI path versioning is the pragmatic choice — it's clear, cacheable, and what developers expect. Save header-based versioning for situations where URL stability is a hard requirement.