API versioning is about managing change without breaking consumers. URL path versioning is the right default for most teams. The harder problems are defining what counts as a breaking change, running multiple versions in parallel, and deprecating old versions without disruption. This guide covers the strategies, the operational reality of each, and how to implement them on Azure.
Why versioning matters more than you think
Every API change is a potential breaking change for someone. A field you consider unused might be the one a client’s automation depends on. A response format tweak that “improves” the API might break a mobile app that has not been updated in six months.
Without a versioning strategy, teams face a choice between two bad options:
- Never change the API (stagnation, technical debt, inability to improve)
- Change the API and hope for the best (broken clients, support tickets, lost trust)
Versioning gives you a third option: change the API deliberately, give consumers time to adapt, and retire old versions on a schedule.
The three versioning approaches
URL path versioning
GET /v1/orders
GET /v2/orders
The version is part of the URL. This is the most common approach and the one we recommend as the default.
Advantages:
- Explicit and visible. You can see the version in logs, browser network tabs, and curl commands.
- Easy to route at the gateway level. Azure APIM, Nginx, and every API gateway handle path-based routing natively.
- Easy to document. Each version has its own OpenAPI specification and API reference.
- Easy to test. You can hit both versions from any HTTP client without special configuration.
Disadvantages:
- URL proliferation. Each version creates a new set of URLs. This can complicate client code if URLs are constructed dynamically.
- Purist objection: the version is not a property of the resource, so it does not belong in the URL. This is a valid conceptual argument but rarely a practical problem.
Best for: public APIs, mobile app backends, and any API where consumers vary in how quickly they adopt changes.
Header versioning
GET /orders
Accept: application/vnd.myapi.v2+json
Or with a custom header:
GET /orders
Api-Version: 2
The version is in the request header, keeping URLs clean.
Advantages:
- Clean URLs that do not change between versions.
- Aligns with HTTP content negotiation semantics (if using the Accept header).
Disadvantages:
- Invisible in URLs. You cannot tell which version a request targets by looking at the URL in a log or browser.
- Harder to test. You need to set headers explicitly, which adds friction for debugging and documentation.
- Some HTTP clients and tools default to no custom headers, making accidental “latest version” requests common.
Best for: internal APIs where all consumers are controlled by the same team and tooling can be standardised.
Query parameter versioning
GET /orders?api-version=2
The version is a query parameter.
Advantages:
- Visible in the URL (like path versioning) without changing the path structure.
- Easy to add to existing APIs without restructuring routes.
Disadvantages:
- Easily forgotten or omitted. Without a default version policy, missing the parameter can route to an unexpected version.
- Muddies the query string, mixing version control with filtering and pagination.
- Less conventional than path versioning, so some tooling and documentation generators handle it less gracefully.
Best for: Azure-native APIs (Azure’s own APIs use this convention) or as a migration path when adding versioning to an existing unversioned API.
What counts as a breaking change?
This is the most important part of a versioning strategy, and the most frequently misunderstood. A breaking change is any change that requires existing clients to modify their code.
Breaking changes (require a new major version)
- Removing a field from a response
- Changing a field’s data type (string to integer, object to array)
- Changing the meaning or semantics of a field
- Removing an endpoint
- Changing an endpoint’s URL path
- Changing required request parameters
- Changing the structure of error responses
- Changing authentication requirements
Non-breaking changes (safe within the current version)
- Adding a new field to a response (clients should ignore unknown fields)
- Adding a new optional request parameter
- Adding a new endpoint
- Adding a new enum value (if documented as extensible)
- Improving error messages without changing the error structure
- Performance improvements
- Bug fixes that make the API behave as documented
The grey area
Some changes are technically non-breaking but practically disruptive:
- Adding a new required field to a request body (breaks clients that do not send it)
- Changing default values for optional parameters
- Changing rate limits or pagination defaults
- Changing the order of fields in a response (should not break well-written clients, but does break brittle parsers)
Treat grey-area changes as breaking unless you have strong evidence that all consumers handle them correctly. When in doubt, bump the version.
Running multiple versions in parallel
Code organisation
There are three approaches to maintaining multiple API versions in your codebase:
Separate controllers/handlers per version. Each version has its own endpoint handlers. Shared business logic lives in services that both versions call. This is the cleanest approach when versions differ significantly.
Version transformation layer. A single set of handlers serves the latest version. Older versions are served by a transformation layer that converts requests and responses between the old and new formats. This works well when the differences between versions are small.
Branched deployments. Each version runs as a separate deployment (separate App Service, separate container). The API gateway routes to the correct deployment. This provides the strongest isolation but increases operational cost.
For most teams, the transformation layer approach balances maintainability with isolation. Reserve separate deployments for versions with fundamentally different architectures.
Testing across versions
Every version in production needs:
- Contract tests that verify the API conforms to its OpenAPI specification
- Integration tests that cover the critical paths for that version
- Smoke tests that run after every deployment to catch regressions
Automate these in your CI/CD pipeline. A change to shared business logic should trigger tests for all supported versions, not just the latest.
Deprecation workflow
Deprecation is how you retire old versions without breaking consumers. A good deprecation workflow gives consumers clear notice, actionable guidance, and enough time to migrate.
Step 1: announce
Communicate the deprecation through every channel your consumers use:
- Documentation: mark the version as deprecated with a banner linking to the migration guide
- Changelog: publish a deprecation notice with the sunset date
- Email: notify registered API consumers directly
- Developer portal: display the deprecation status in Azure APIM’s developer portal
Step 2: add response headers
Add a Deprecation header and a Sunset header to every response from the deprecated version:
Deprecation: true
Sunset: Sat, 10 Oct 2026 00:00:00 GMT
Link: <https://docs.example.com/migration-v2>; rel="successor-version"
Azure APIM can inject these headers via an outbound policy without changing application code. Clients that monitor response headers can detect deprecation automatically.
Step 3: monitor adoption
Track traffic to the deprecated version. Use Azure APIM analytics or Application Insights to monitor:
- Request volume per version over time
- Unique consumers still using the deprecated version
- Error rates on the deprecated version
Step 4: sunset
On the sunset date, return 410 Gone for requests to the deprecated version, with a response body explaining the situation and linking to the migration guide. Do not silently redirect to the new version. Silent redirects mask breaking changes and cause subtle bugs.
Timeline
- Public APIs: 12 months minimum notice
- Partner APIs: 6-12 months
- Internal APIs: 3-6 months (or less if you control all consumers)
- Mobile app APIs: 12 months minimum (users need time to update the app)
Versioning with Azure API Management
Azure APIM has native versioning support that handles the routing, documentation, and lifecycle management.
Versions vs revisions
APIM distinguishes between two concepts:
Versions represent breaking changes that consumers must opt into (v1, v2). Each version has its own API resource in APIM, its own OpenAPI specification, and its own subscription management.
Revisions represent non-breaking changes within a version. A new revision can be tested in isolation (APIM assigns it a revision-specific URL) and promoted to the current revision when validated. Consumers on that version automatically receive the updated revision.
Version routing
APIM supports all three routing schemes:
- URL path:
/v1/orders,/v2/orders - Header:
Api-Version: 1 - Query parameter:
/orders?api-version=1
Configure the scheme when creating the versioned API in APIM. All subsequent versions use the same scheme.
Deprecation via policy
Add an outbound policy to inject deprecation headers:
<outbound>
<set-header name="Deprecation" exists-action="override">
<value>true</value>
</set-header>
<set-header name="Sunset" exists-action="override">
<value>Sat, 10 Oct 2026 00:00:00 GMT</value>
</set-header>
</outbound>
This marks the version as deprecated at the gateway level without changing application code.
Developer portal
APIM’s developer portal displays all active versions and marks deprecated versions visually. Consumers can browse documentation, test endpoints, and manage subscriptions per version.
For full details on APIM configuration, see Azure API Management.
A versioning policy template
Document your versioning policy and share it with API consumers and internal teams. Here is a template you can adapt.
Version numbering
We use major versions only (v1, v2, v3). Minor and patch changes are non-breaking and do not increment the version number.
Breaking change definition
A breaking change is any change that requires existing clients to modify their code. This includes removing fields, changing types, removing endpoints, and changing auth requirements. See the “What counts as a breaking change” section above for the full taxonomy.
Support window
We support the current version and one prior version. Deprecated versions receive security patches but no new features. The deprecation notice period is [6/12] months.
Communication
Deprecation is communicated via: documentation banner, changelog, email to registered consumers, Deprecation and Sunset response headers, and developer portal status.
Consumer responsibilities
Clients should: ignore unknown fields in responses, handle new optional fields gracefully, monitor Deprecation and Sunset headers, and plan migration within the notice period.
Where to start
If your API has no versioning today:
- Add a
/v1/prefix to your current endpoints. This establishes the convention without changing behaviour. Configure APIM to route the versioned path to your existing backend. - Document your breaking change policy. Define what counts as breaking, your support window, and your deprecation timeline. Share it with consumers.
- Set up version monitoring. Track requests per version in Application Insights or APIM analytics. You will need this data when deprecation decisions arise.
For help designing a versioning strategy or migrating to versioned APIs, see our REST API development and API and integration services or book a consultation.
Frequently asked questions
What is the best API versioning strategy?
When should I increment the major version?
How many API versions should I support simultaneously?
How does Azure API Management handle versioning?
Should I version GraphQL APIs?
How do I communicate deprecation to API consumers?
Related guides
Planning an Integration Strategy: A Guide for Business and Technology Leaders
Integration projects fail when the strategy is wrong, not when the technology is wrong. A practical guide to planning an integration programme that moves at AI-augmented speed.
Backend for Frontend (BFF): API Patterns for Mobile and Web Clients
When to use the BFF pattern, how it compares to a single API or API gateway, and how to implement it on Azure. Practical architecture for teams serving mobile, web, and internal clients.
Mobile API Best Practices: Building Backends That Scale
How to design, secure, and operate APIs for mobile apps. Protocol choice, authentication flows, offline sync, push notifications, versioning, and Azure architecture patterns.