API Versioning: How to Evolve an API Without Breaking Clients
URL path versioning, header versioning, query params, semantic versioning, and the Sunset header — what each approach costs and when to use it.
API versioning is a promise about how long your API will remain stable. Every versioning strategy is a trade-off between how easy it is for you to change things and how easy it is for clients to consume them. Getting this wrong means either a fragile API that breaks clients or a maintenance burden that never goes away.
Why You Need Versioning
Without versioning, any breaking change to your API breaks every client simultaneously. "Breaking change" includes:
- Removing or renaming a field
- Changing a field's type
- Changing an endpoint's URL
- Changing required vs optional parameters
- Changing error response shapes
Additive changes — new optional fields, new endpoints, new optional query params — are generally non-breaking. Design your API to be additive by default.
URL Path Versioning (/v1/, /v2/)
The most common approach: the version is part of the URL.
GET /api/v1/users
GET /api/v2/users
Pros: Explicit. URLs are bookmarkable and loggable. Easy to route by version in a proxy or Next.js middleware. Immediately visible in network logs — you know exactly which version a request hit.
Cons: Version in the URL violates REST purists' sense of "a URL should identify a resource, not a version of that resource." More practically: it creates two different URLs for the same resource, complicating caching.
In Next.js App Router, implement with route groups:
app/
api/
v1/
users/route.ts
v2/
users/route.ts
Header Versioning
The version is specified in a request header, leaving the URL clean:
GET /api/users
API-Version: 2026-04-01Stripe uses date-based versioning in this style. The advantage: clients pin to a specific version date and see a stable API. Each "version" is a snapshot of the API on that date, and breaking changes are introduced as new dates.
Cons: Harder to test in a browser. Requires clients to know to set the header. Caches need to vary on the version header.
Query Parameter Versioning
GET /api/users?version=2
Easiest for clients to use (visible, easy to change). Works naturally in browser address bars. The main downside: caches may not vary on query params correctly without explicit Vary configuration.
Semantic Versioning and Breaking Changes
For libraries and SDKs, SemVer (MAJOR.MINOR.PATCH) signals breaking changes clearly: increment MAJOR for breaking changes, MINOR for new features, PATCH for fixes.
For HTTP APIs, SemVer maps less cleanly because clients can't "upgrade" a server the way they upgrade a library. Version numbers in API URLs tend to be integers (v1, v2) that increment on major breaking changes, not SemVer.
The Sunset Header
When you deprecate a version, tell clients:
HTTP/1.1 200 OK
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Deprecation: Wed, 01 Jan 2026 00:00:00 GMT
Link: <https://api.example.com/v2/users>; rel="successor-version"Sunset is an IETF standard (RFC 8594). It tells clients: this endpoint will stop working on this date. Good API clients log or alert on this header so their operators know a migration is needed.
Version Overlap Periods
When releasing a new version, run old and new simultaneously for a migration window. The length depends on your client base:
- Internal APIs: 2–4 weeks (you control the clients)
- Partner APIs: 3–6 months (they need planning time)
- Public APIs: 6–12 months (unknown clients, conservative)
After the sunset date, return 410 Gone rather than continuing to serve the deprecated endpoint. This makes the retirement explicit — a 404 suggests the URL never existed; a 410 says it used to and is now intentionally gone.
What Not to Version
Don't version things that don't need it. If an API change is backward-compatible (new optional field, new endpoint), there's no need to bump the version. Over-versioning creates unnecessary maintenance burden — every version in active use is a code path you maintain.
ApiShield's HAR parser and live probe features were added without versioning because they extended the input format detection non-breakingly. Only changes to the normalized spec format or the report structure would require versioning the tool's API surface.