Quick answer
API versioning is how a backend evolves an API contract without unexpectedly breaking clients. The goal is not to create new versions for every change. The goal is to preserve compatibility while giving teams a controlled path for breaking changes.
Common strategies include URL versions such as /v1/users, header-based versions, and backward-compatible evolution inside the same version.
What counts as a breaking change
A breaking change is a change that can make an existing client fail or behave differently.
Common breaking changes include:
- Removing a field.
- Renaming a field.
- Changing a field type.
- Making an optional field required.
- Changing pagination semantics.
- Changing authentication requirements.
- Changing status codes that clients depend on.
- Removing an endpoint.
Adding a new optional response field is usually not breaking if clients ignore unknown fields. Changing the meaning of an existing field is more dangerous.
URL versioning
URL versioning puts the version in the path:
GET /v1/orders
GET /v2/orders
This is easy to see, easy to route, and easy to document. It is common for public REST APIs because clients can tell which contract they are using by looking at the URL.
The downside is that it can encourage large version jumps instead of steady compatibility work. Teams may also duplicate too much implementation if they treat each version as a separate product.
Header versioning
Header versioning keeps the URL stable and moves the version into a request header:
GET /orders
API-Version: 2026-06-28
Some APIs use media type negotiation:
Accept: application/vnd.example.orders.v2+json
Header versioning can keep resource URLs cleaner, but it is less visible in browser testing, logs, and simple links. It also requires clients and documentation to be disciplined about headers.
Date-based versions
Date-based versions use a date as the contract version:
API-Version: 2026-06-28
This can work well when the provider releases frequent incremental changes and wants clients pinned to a known behavior date.
Date-based versions need strong documentation. Clients must know what changed on each date and how long older dates will be supported.
Backward-compatible changes
The best API versioning strategy is often avoiding a new version when a compatible change is enough.
Usually compatible:
- Add optional response fields.
- Add optional request fields.
- Add new endpoints.
- Add new enum values only if clients are designed to tolerate them.
- Add pagination metadata without changing existing fields.
Potentially breaking:
- Rename fields.
- Remove fields.
- Change required validation.
- Change default sorting.
- Change ID formats.
- Change response status codes.
Read REST API Status Codes Explained before changing status code behavior because clients may use codes for retry, authentication, and error handling.
Versioning response bodies
A response can include version and deprecation metadata:
{
"data": [
{
"id": "ord_123",
"status": "paid"
}
],
"meta": {
"apiVersion": "2026-06-28"
}
}
For deprecated endpoints, headers can be useful:
Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT
Link: </docs/migrations/orders-v2>; rel="deprecation"
The response contract should make migration visible without surprising clients.
Deprecation plan
A practical deprecation plan includes:
- Announce the new version and migration guide.
- Keep the old version stable during the migration window.
- Add monitoring for usage by client, account, API key, or SDK version.
- Contact high-traffic clients before the deadline.
- Return warnings where appropriate.
- Remove or disable the old version only after the policy allows it.
The migration window depends on the API audience. Internal APIs may move quickly. Public APIs often need months or more.
Versioning and SDKs
If you publish SDKs, API versioning becomes a client library problem too.
SDKs can hide some wire-level details, but they also make breaking changes more visible because method names, types, and exceptions become part of the developer experience.
When changing a public API, document both the HTTP contract and the SDK migration path.
Common mistakes
The first mistake is creating a new major version for every small addition. That increases maintenance cost and fragments clients.
The second mistake is changing behavior inside an existing version without calling it a breaking change. Clients care about behavior, not only field names.
The third mistake is supporting too many old versions forever. Every active version needs testing, security review, documentation, and operational support.
The fourth mistake is versioning only endpoints and forgetting webhooks, events, SDKs, documentation, examples, and error schemas.
Choosing a strategy
For many small and medium APIs:
- Use URL versioning for public REST APIs when clarity matters.
- Use backward-compatible changes inside a version whenever possible.
- Use explicit deprecation windows for breaking changes.
- Keep error response shapes stable.
- Track version usage in logs and metrics.
Header or date-based versioning can work well for mature API platforms, but they require stronger operational discipline.
Related reading
Read REST API Design Checklist for contract review, REST vs RPC for API style tradeoffs, PUT vs PATCH in REST APIs for update semantics, and OpenAPI Explained for Backend Developers for documentation. Read REST API Status Codes Explained for stable error contracts. If version changes affect pagination behavior, read Pagination Strategies for REST APIs and API Pagination Metadata Best Practices. Use the JSON Schema Generator and JSON to TypeScript Types Generator to prototype response shapes during API migration planning. For the full sequence, browse the API Design Learning Path.