API versioning is the discipline of managing changes to a public contract so existing consumers don't break when you ship new functionality. The core rule: bump a version only when a change is incompatible with existing clients. For public APIs, URI or path versioning is the pragmatic default. Platform APIs with sophisticated clients often do better with date-based or header-based pinning instead.
TL;DR:
- Path-based versioning is most suitable for public APIs with many unknown consumers due to its cache friendliness and ease of debugging.
- Major incompatibilities, such as renaming fields or changing data types, require a new version, while additive changes like adding optional fields or endpoints do not.
- A clear deprecation window of six to twelve months with signals like RFC 8594 headers helps clients plan migration and avoid sudden disruptions.
- Teams should choose versioning strategies based on their consumer types, cache needs, and operational maturity to reduce complexity and improve reliability.
- Implementing a consistent, early resolution of API versions in the routing layer and automating deprecation signaling saves time and prevents migration errors.
Table of Contents
- What Is API Versioning, and What Does It Actually Protect?
- Why and When Should You Version an API?
- Which API Versioning Strategy Fits Your Situation?
- How Do You Choose a Versioning Strategy for Your API?
- How Do You Implement API Versioning Step by Step?
- How Requestum Handles API Versioning in Production
- What the Standards Get Right (and Where Teams Still Trip Up)
- Get Help Designing and Migrating Your API Versions
- Sources
- FAQ
What Is API Versioning, and What Does It Actually Protect?
An API is a promise. When a mobile app or a partner's backend calls your endpoint, it's trusting that the request and response shapes will keep working tomorrow, next quarter, and probably next year. API versioning is the mechanism you use to keep that promise while still evolving the system underneath it. When you change something that violates the existing promise, you introduce a new version. When you extend it without breaking anything, you don't.
The hard part is knowing the difference. A breaking change is anything that would cause a well-behaved client's existing code to fail or misbehave: removing a field, renaming a field, changing a data type, tightening validation rules, or altering the meaning of a status code. An additive change is safe to ship without a version bump.
Common additive changes include:
- Adding a new optional field to a response
- Adding a new endpoint
- Adding a new optional request parameter with a sensible default
- Loosening a validation rule (accepting more input, not less)
Version labels matter here too. Semantic Versioning formalizes MAJOR.MINOR.PATCH, where the major number increments only for incompatible changes. That works cleanly for libraries and SDKs. For HTTP APIs, many teams borrow only the major number (/v1, /v2) and skip minor/patch granularity entirely, since clients rarely care about patch-level detail in a URL.
Why and When Should You Version an API?
Versioning exists to buy you two things at once: the freedom to redesign your API and the stability your consumers need to build on it without fear. Skip it, and every deploy becomes a trust exercise for whoever integrates with you.
The benefits are straightforward. Consumers get a stable target to build against. Migrations become planned events instead of emergency incidents. Your team can evolve the product without taking down every client that depends on the old shape. The costs are just as real: you're now maintaining multiple code paths, your caching layer has to account for version differences, and your documentation burden doubles with every version you keep alive.
Use this checklist before you bump a major version:
- Does the change remove or rename any field, parameter, or endpoint a client might depend on?
- Does it change a data type, an enum's valid values, or a status code's meaning?
- Does it tighten validation in a way that would reject previously valid requests?
- Have you confirmed the change can't ship as an additive, backward-compatible alternative instead?
If you answer yes to any of the first three and no to the fourth, you have a breaking change and a new version.
Deprecation windows vary by audience, but a common pattern in public API guidance runs six to twelve months for consumer-facing APIs, and longer for enterprise or platform integrations with slower release cycles. During that window, send machine-readable signals: the Deprecation header and the Sunset HTTP header defined in RFC 8594, which tells clients and their tooling exactly when a resource will stop responding.
Which API Versioning Strategy Fits Your Situation?
There's no single correct REST API versioning method. Each approach trades discoverability, cache behavior, and implementation complexity differently, and the right choice depends heavily on who's consuming your API and how many of them there are.
URI or path versioning (/v1/orders, /v2/orders) puts the version in the route itself. It's the most debuggable option because anyone can read a URL and instantly know which contract they're hitting, and it plays cleanly with existing HTTP caches since each version gets its own distinct URL space. That cache friendliness and debugging simplicity is a big reason most public-facing APIs default to path versioning, even when REST purists argue it violates the idea that a URI should identify a resource, not a contract revision.
Query parameter versioning (?version=2) is easy to bolt onto an existing route structure without changing paths, but it's easy to forget, easy to omit accidentally, and murkier for caching because proxies have to be configured to treat the query string as part of the cache key.
Header-based versioning (a custom header like Api-Version: 2) keeps your URLs clean and satisfies the REST argument that a resource's identity shouldn't change across versions. The tradeoff is discoverability. Nobody can see the version by glancing at a URL, and caching requires careful use of the Vary header to avoid serving version 1 responses to version 2 clients.
Media-type or content negotiation versioning uses the Accept header with a vendor-specific media type (application/vnd.myapi.v2+json). This is the most granular approach because you can version individual resources independently rather than the whole API at once, but it demands more from both server routing logic and client tooling. Content negotiation is powerful in theory and underused in practice for exactly that reason.
Date-based pinning (2024-10-01 as a version identifier) communicates a temporal snapshot of the contract rather than an arbitrary number. Platform providers favor this pattern because it lets accounts pin to "whatever the API looked like on this date" without forcing an explicit upgrade decision.
For large-scale platform APIs, Google's AIP-185 documents more advanced patterns: channel-based versioning (stable vs. preview releases), interface-based versioning, and visibility-based versioning (IBV), where different consumer tiers see different surface areas of the same underlying API.
| Strategy | Cache friendliness | Discoverability | Implementation complexity |
|---|---|---|---|
| URI/path | High | High | Low |
| Query parameter | Moderate | Moderate | Low |
| Header-based | Requires Vary tuning | Low | Moderate |
| Media-type negotiation | Moderate | Low | High |
| Date-based | High | Moderate | Moderate |
Pro Tip: Whatever strategy you pick, resolve the version as early as possible in your request pipeline and route to a dedicated handler rather than sprinkling version checks throughout your business logic. Spring's ApiVersionStrategy resolves version from path, header, query parameter, or media type in one place, which keeps that logic out of your controllers entirely.
How Do You Choose a Versioning Strategy for Your API?
Match the strategy to your operational reality, not to whichever approach sounds most theoretically correct. A few factors decide this for you almost every time.
Start with audience. Public APIs consumed by unknown third-party developers benefit from URI/path versioning because it's self-explanatory and shows up in every log line and bug report. Internal APIs consumed by a handful of services you control can tolerate header-based versioning, since your own teams can coordinate around it without discoverability friction.
Client count and breaking-change cadence matter next. If you have thousands of integrators and rarely make breaking changes, a stable major-version path scheme keeps things simple. If you're iterating fast with a small number of sophisticated clients (think: internal microservices or a handful of enterprise partners), date-based or header pinning gives you finer control without forcing a URL change on every revision.
Caching needs point toward path versioning almost by default, since it works with standard HTTP caches with zero configuration. Operational maturity matters too. If your team doesn't have strong observability into which version each client is calling, don't adopt a strategy (like media-type negotiation) that makes that visibility harder to get.
A rough mapping:
- Public API, many unknown consumers, strong caching needs → URI/path
- Internal services, small trusted client set, frequent iteration → header or date-based
- Large platform with tiered access levels → AIP-style channel or visibility-based versioning
- Resource-level granularity genuinely needed → media-type negotiation
Watch for three anti-patterns. Mixing multiple versioning strategies across the same API portfolio is a common and costly mistake that multiplies cognitive load for every consumer. Versioning every release, including purely additive ones, trains consumers to distrust your version numbers. And shipping without a documented default strategy means every new endpoint becomes a fresh argument on your team.
How Do You Implement API Versioning Step by Step?
Once you've settled on a strategy, the rollout itself follows a repeatable sequence. Skipping steps here is where most version migrations go sideways.
- Confirm the change is actually breaking. Run it against the checklist above. If it's additive, ship it into the current version and skip the rest of this process entirely.
- Pick your label format and confirm it matches your team's documented default (path segment, header value, or date string).
- Update routing and SDKs so the new version resolves to its own handler and existing SDK clients aren't silently pointed at code they weren't built against.
- Publish a migration guide with concrete before-and-after request and response examples, not just a changelog entry. Show the exact field that moved or the exact status code that changed.
- Roll out in stages, typically behind a feature flag or a canary percentage of traffic, and monitor error rates closely before opening the new version to everyone.
- Watch old-version traffic continuously. A version with stubbornly high usage six months after a migration guide goes out tells you the guide isn't working or the migration incentive isn't strong enough.
- Announce deprecation formally, then start sending the
Deprecationheader and theSunsetheader on every response from the old version, with the exact retirement date. - Fail loudly at the end. When the sunset date arrives, return a clear error, ideally formatted with RFC 7807's problem-details structure, rather than letting the old version quietly return corrupted or unexpected data.
Pro Tip: Bake the Deprecation and Sunset headers into your API gateway or framework middleware rather than each individual endpoint. Frameworks like Spring already ship deprecation handlers that emit these headers automatically once a version is flagged, which means you set the sunset date once instead of hunting down every handler that needs updating.
How Requestum Handles API Versioning in Production
Some software development agencies build and maintain APIs for SaaS platforms and mobile backends where breaking a client mid-flight isn't an option. The versioning workflow ideally starts before a single line of new code ships: confirm whether the change is genuinely breaking, decide on the labeling format that fits the client base, and write the migration guide before the new version goes live, not after.
For a typical SaaS engagement, this might mean path-based versioning for the public-facing API surface and header pinning for internal service-to-service calls where the team controls both ends. SDK updates for mobile clients could be bundled with the same release cycle as the API change, so a mobile app team is not left guessing which backend version their build targets. Adoption is tracked by monitoring which version each authenticated client is calling, helping to inform deprecation announcements with real traffic data rather than guesses. That visibility helps set sunset dates with confidence rather than pushing them back due to uncertainty about usage.

What the Standards Get Right (and Where Teams Still Trip Up)
The standards bodies have already done the hard thinking here. SemVer tells you when to bump a number. RFC 8594 tells you how to signal a sunset. Google's AIP-185 tells you how to structure versioning for platform-scale APIs with tiered consumers. The gap isn't in the standards. It's in teams treating versioning as a naming convention instead of an operational commitment.
The most overrated piece of advice in this space is "just version everything to be safe." That instinct produces APIs with a dozen live major versions and no consumer who trusts the number anymore. The more defensible position, and the one the standards actually support, is stingy versioning paired with generous deprecation communication. Bump rarely. Announce loudly. Set a real sunset date and hold it.
If you take one thing from this: build your deprecation signaling before you need it, not during your first painful migration. A Sunset header costs you almost nothing to implement and saves you months of manual consumer outreach later.
— Dmitry
Get Help Designing and Migrating Your API Versions
Requestum is the alternative to guessing your way through a version migration. Instead of discovering breaking changes after a client complains, Requestum's engineering teams design the versioning strategy, build the migration path, and update client SDKs as part of one coordinated release, not three separate fire drills.

A first consult typically covers your current API surface, which consumers depend on it, and where your existing contract is already fragile. From there, Requestum's web development team can map out a versioning approach suited to your consumer base, while the mobile development group handles the SDK side for any iOS or Android clients that need to move in step with the backend. For teams running a growing SaaS product, the SaaS development practice folds versioning discipline into the broader platform roadmap instead of treating it as a one-off project.
If your API is overdue for a version strategy, reach out to Requestum and get a plan before your next breaking change forces one on you.
Sources
FAQ
What is v1 and v2 in a REST API?
They're major version markers indicating a breaking change in the contract. Consumers calling /v1/orders and /v2/orders are hitting deliberately different, incompatible versions of the same resource.
Why do we need API versioning?
Versioning lets you change or remove parts of an API without breaking every client currently depending on it, so consumers get stability and your team still gets room to evolve the product.
How do you create API versioning?
Pick a label format (path, header, query parameter, or date), confirm it as your documented default, then route breaking changes to a distinct version while additive changes ship into the current one.
How can I version a REST API?
The most common approach is URI/path versioning (/v1/, /v2/) because it's cache-friendly and easy to debug; header-based or date-based pinning works better for internal services or platform APIs with sophisticated clients, as Requestum applies depending on each project's consumer base.
How long should a deprecated API version stay live?
Deprecation windows commonly run six to twelve months for consumer-facing APIs, communicated through Deprecation and Sunset headers so clients can detect the retirement date programmatically.
