API Versioning Strategies
How to change an API's shape over time without breaking every client that already depends on it.
2 min read
The moment an API has more than one consumer, you lose the ability to freely change it. A field rename, a removed endpoint, or a stricter validation rule that seems harmless from the backend can silently break a mobile app that's already in users' hands and can't be force-updated instantly. Versioning is how backends change without breaking what's already deployed.
What counts as a breaking change
- Removing or renaming a field or endpoint
- Changing a field's type (a string
idbecoming a number) - Adding a new required field to a request
- Changing the meaning of an existing field or status code
Additive changes — a new optional field, a new endpoint, a new optional query parameter — are generally safe, because well-written clients ignore fields they don't recognize.
Common versioning strategies
URL path versioning — the most common and most visible approach:
GET /v1/users/42
GET /v2/users/42
Simple to understand and easy to route, but it means maintaining parallel implementations (or at least parallel response-shaping logic) for each supported version.
Header versioning — the version is specified in a request header instead of the URL:
GET /users/42 HTTP/1.1
Accept: application/vnd.example.v2+jsonKeeps URLs stable (arguably more "RESTful," since a URL is supposed to identify a resource, not a version of an API), but is less discoverable — you can't just look at a URL in a browser and know which version you're hitting.
No versioning, additive-only evolution — never remove or change fields, only add new ones, and deprecate old fields with warnings before eventually retiring them on a long timeline. This is how many GraphQL APIs operate, and some REST APIs adopt the same discipline.
Deprecation, not deletion
Whatever strategy you use, the practical pattern is the same: ship the new version alongside the old one, mark the old one deprecated (often via a Deprecation or Sunset header), give consumers a real migration window — weeks or months, not days — and monitor actual traffic to the old version before removing it. Removing a version that's technically deprecated but still receiving real production traffic is still a breaking change to whoever's still calling it.
HTTP/1.1 200 OK
Deprecation: true
Sunset: Wed, 01 Jan 2027 00:00:00 GMT
Link: <https://api.example.com/v2/users/42>; rel="successor-version"Picking a strategy
There's no universally correct answer — URL versioning is the pragmatic default for public APIs with external, less-coordinated consumers, since it's the most explicit and cache-friendly. Internal APIs, where the backend team controls all the consumers, can often get away with additive-only evolution and skip formal versioning almost entirely.
Once an API has multiple resources returning lists, the next practical question is how a client asks for a slice of that list rather than the whole thing — which is exactly what pagination and filtering solve.