Skip to main content
All Posts
2026AI API vs Traditional API: What's Actually DifferentBuilding LLM-Backed APIs: A Practical Architecture GuideEmbedding APIs Explained: How They Work and When to Use ThemCost Optimization for LLM APIs: Caching, Routing, and Model SelectionLLM Rate Limiting: Token Buckets and Concurrency ControlStreaming LLM Responses: Patterns and PitfallsOpenAI API vs Anthropic API vs Gemini API: Production Trade-offsOpen vs Proprietary LLM APIs: Trade-offs for ProductionRAG APIs: Architecting Retrieval-Augmented Generation EndpointsVertical AI API Strategy: Building Domain-Specific AI APIsSmarter AI Traffic with Jev: How WSO2 AI Gateway Mediates LLM and MCP CallsAPI Analytics: The Metrics That Actually Matter (2026 Guide)How to Build a CI/CD Pipeline for APIsAPI Design Tools: Choosing the Right Stack for API-First TeamsAPI Gateway vs Load Balancer vs Reverse Proxy: Clearing the ConfusionAPI Management Platforms: Cloud vs. Self-Hosted (2026)API Management Tools: A Complete Buyer's Guide for 2026Cloud Native API Gateway: Architecture Guide for 2026Envoy vs NGINX: Which Proxy Fits Your API Gateway?Internal Developer Portal: What It Is and How to Build OneKong vs Tyk: Open Source API Gateway ShowdownKubernetes API Gateway: The 2026 Guide to Gateway APIMuleSoft vs WSO2: Enterprise API Management ComparedOpen Source API Gateways Compared: Kong, Tyk, Gravitee, WSO2What Is an API Gateway? Functions, Architecture & Use CasesAPI Architecture: Patterns, Principles & Modern ApproachesAPI Caching Strategies: Edge, Gateway, and Application LayersAPI Federation: Patterns for Multi-Team API ProgramsAPI-First Architecture: Why Design Before Code WinsAPI Gateway Architecture: How API Gateways Actually WorkAPI Security Architecture: Layered Defense for Modern APIsCircuit Breakers and Resilience Patterns for APIsAPI Architecture Diagrams: From L0 to Ln (Complete Guide)Backend-for-Frontend (BFF) Pattern: When and WhyEvent-Driven APIs: Webhooks, WebSockets, and SSEMicroservices vs Monolithic: Trade-offs and When to ChooseService Mesh vs API Gateway: When You Need Each (or Both)API Authentication Best Practices: OAuth, Keys, mTLS, JWTAPI Error Handling: Best Practices for REST APIsAPI Documentation Best Practices: Beyond Reference to Developer ExperienceAPI Rate Limiting: How It Works and Best PracticesAPI Pagination: Design Patterns & Best PracticesAPI Versioning Best Practices: Strategies for Long-Lived APIsGraphQL vs REST: Key Differences & When to Use EachHTTP Status Codes for API Design: A Practical GuideIdempotency Keys for APIs: The Complete GuideJWT vs OAuth: What's the Difference and When to Use EachOAuth 2.1: What's New and How It Differs from OAuth 2.0REST API Design: A Complete Guide for 2026What Is a REST API? A Plain-English GuideAI Agent API Integration: Patterns & Best PracticesEnterprise MCP: The Control Plane for AI AgentsMCP Gateway vs MCP Proxy: What's the Difference?MCP Governance: Controlling Third-Party MCP ToolsMCP Monitoring: Observability for MCP ServersMCP Security: Risks, Threats and ControlsMCP Server Security: 8 Best PracticesMCP Server Catalog: Building an Internal RegistryMCP Tool Poisoning: How It Works & How to Stop ItWhat Is an MCP Gateway? Features & BenefitsWhat Is an AI Gateway? Features, Benefits and How It WorksAI Gateway Comparison: Top Solutions in 2026AI Gateway Security: Guardrails for LLM TrafficAI Gateway vs API Gateway: Key DifferencesBest LiteLLM Alternatives in 2026AI Gateway Observability: Key Metrics, Logging, and Tracing for LLM TrafficLLM Cost Control: Strategies to Cut AI SpendLLM Routing: How It Works, Strategies, and Why It MattersPrompt Injection Defense: Techniques That Actually Work7 Billion Calls a Day: One WSO2 GatewayLLM Fallback: How to Build Resilient AI ApplicationsREST API to MCP Server: A Practical GuideAzure API Management vs AWS API Gateway: Key Differences and FeaturesMuleSoft vs AWS API Gateway: Key Differences and FeaturesMuleSoft vs Azure API Management: Key Differences and FeaturesApigee vs AWS API Gateway: Key Differences and FeaturesApigee vs Azure API Management: Key Differences and FeaturesApigee vs MuleSoft: Key Differences and FeaturesGravitee vs AWS API Gateway: Key Differences and FeaturesGravitee vs Azure API Management: Key Differences and FeaturesGravitee vs MuleSoft: Key Differences and FeaturesGravitee vs Apigee: Key Differences and FeaturesKong vs AWS API Gateway: Key Differences and FeaturesKong vs Azure API Management: Key Differences and FeaturesKong vs MuleSoft: Key Differences and FeaturesKong vs Apigee: Key Differences and FeaturesKong vs Gravitee: Key Differences and FeaturesTop 6 AWS API Gateway Alternatives of 2026Top 6 Azure API Management Alternatives of 2026Top 6 MuleSoft Alternatives of 2026Top 10 Apigee Alternatives of 2026Top 4 Gravitee Alternatives of 2026Top 6 Kong Alternatives of 2026

API Versioning Best Practices: Strategies for Long-Lived APIs

· 19 min read
Technical Writer, WSO2

An API that never changes eventually stops being useful, and an API that changes carelessly eventually stops being trusted. Versioning is how a team keeps shipping improvements without breaking the clients that already depend on it.

This guide covers the four common versioning strategies, a framework for choosing between them, and the practices that keep a versioning scheme workable for years rather than months.

What Is API Versioning?​

API versioning is the practice of managing changes to an API's contract so that existing clients keep working while new functionality ships. In practice, it means giving each meaningfully different version of a resource, endpoint, or response shape its own identifier, whether that's a number in the URL, a value in a request header, or a media type in an Accept header, so that client and server can agree on which contract they're using.

The identifier matters less than what it protects. Once a client integrates with your API, it has effectively signed a contract with the shape of your requests and responses. That's why getting the underlying REST API design fundamentals right from the start makes versioning easier later on. Versioning is the mechanism that lets you change that shape on your own schedule, not your slowest client's.

Why API Versioning Matters and What Happens Without It​

Imagine a payments API that renames a field from total to amount_total in place, with no version boundary. Every client parsing the old field name starts failing silently. Orders stop reconciling, dashboards show zeros, and nobody notices until finance asks why yesterday's numbers don't add up. That's what a breaking change looks like when there's no version boundary to contain it: the API contract broke without warning, and every consumer paid for it at the same moment.

This is also where the commercial stakes appear. Teams looking for versioning guidance are usually past the theoretical stage. Either they've just broken a client integration, or they're building a public or partner-facing API and don't want to learn about breaking changes from a support ticket. A clear versioning strategy turns "we changed something and hoped nobody noticed" into "we changed something, told consumers in advance, and gave them time to move."

Without a version boundary, every change to an API is a bet placed on every consumer at once. With one, a breaking change becomes an opt-in upgrade instead of a surprise outage. That's the real reason this topic keeps coming up in API design discussions. It's less about the mechanics of putting /v2/ in a URL and more about maintaining a contract that other people's software relies on.

The 4 Main API Versioning Strategies​

Most REST APIs express their version through one of four mechanisms. They all solve the same problem, telling the server which contract a request expects, but they differ in where that information lives and how visible it is.

1. URI Path Versioning​

The version appears directly in the URL path, before the resource: GET /v2/orders/4521. This is the most common approach in practice, because it's immediately visible, trivially cacheable by URL, and easy to route at the gateway or load balancer without inspecting headers.

The cost is that every version becomes a distinct URI. /v1/orders/4521 and /v2/orders/4521 are, strictly speaking, different URIs for what a consumer thinks of as the same order. That's a practical trade-off rather than a rule REST imposes, since a resource can have more than one URI or representation. It also means routing tables and internal documentation grow with every version you keep alive.

2. Query Parameter Versioning​

The version travels as a query string parameter on an otherwise stable path: GET /orders/4521?version=2. This keeps the resource path stable across versions. The query string is still part of the URI, though (RFC 3986), so ?version=1 and ?version=2 are still two different URIs, not one canonical one. Teams choose this approach for the stable path and simpler documentation it offers, not because it produces a single literal URI.

The trade-off is that nothing in URI syntax prevents a client from leaving the parameter out, even when the API treats it as required, so the requirement has to be enforced on the server rather than assumed. Caching needs similar care. A cache's default key is the full request URI, including the query string (RFC 9111), so ?version=1 and ?version=2 are separate cache entries by default. The real risk runs the other way: a CDN or reverse proxy deliberately configured to strip or normalize query strings for efficiency can then serve one version's response to a request for another.

3. Header Versioning​

The version travels in a custom request header, completely separate from the URL: GET /orders/4521 with Api-Version: 2 set on the request. URIs stay entirely stable across versions, which keeps documentation and bookmarks simple.

The trade-off is visibility. A version carried only in a header doesn't show up in a browser's address bar, in most access logs by default, or in a quick curl command unless you remember to set it. Working out which version served a given request takes an extra step compared with a version sitting in plain sight in the URL.

4. Media-Type (Content Negotiation) Versioning​

The version is embedded in the media type of the Accept header, for example Accept: application/vnd.example.v2+json. This uses HTTP content negotiation the way Fielding's dissertation originally described it, choosing among several representations of one resource, so the version lives in the representation format rather than in the resource's identifier. Some practitioners see this as the closest fit to REST's split between resources and representations for that reason. REST's constraints don't formally rank one versioning mechanism above another, though; it's a design trade-off, not a requirement.

It's also the least common approach in practice. It requires every client and every piece of tooling in the request path (proxies, API gateways, test scripts, documentation generators) to understand and correctly forward custom media types, which raises the bar for adoption compared with a header or a path segment.

Comparing the Four Strategies​

StrategyWhere the version livesVisibilityCaching behaviorBest fit
URI pathIn the URL, e.g. /v2/resourceHigh; visible everywhereSimple; cache keys include it naturallyPublic APIs, simpler gateway routing
Query parameterQuery string, e.g. ?version=2Medium; visible in the URL but easy to omitDepends on cache configurationInternal APIs with light versioning needs
HeaderCustom request headerLow; invisible in the URL and browserRequires explicit cache-key configurationAPIs that prioritize stable, clean URIs
Media typeAccept header content typeLow; requires client tooling supportRequires explicit cache-key configurationTeams with strict REST/HATEOAS discipline

The same request for order 4521 on v2, showing where the version sits with URI path, query parameter, and request header versioning

How to Choose the Right Versioning Strategy​

The four strategies aren't ranked by quality. They suit different contexts, and choosing the wrong one shows up months later as either a documentation headache or a debugging headache. Four questions narrow the field faster than a feature comparison.

1. Who consumes the API? A public API with thousands of unknown external consumers benefits from a version that's impossible to miss, which points to URI path versioning, because you can't contact every consumer to explain a subtler signal. An internal API used by a handful of services your own team controls can tolerate a quieter mechanism, because you can coordinate upgrades directly with the few consumers involved.

2. How often does the API change? An API with infrequent, carefully planned major releases can afford the overhead of maintaining parallel URI paths for each version. An API that changes weekly benefits from a lighter mechanism, such as a header or query parameter, where adding a version doesn't mean duplicating an entire route tree.

3. What are the caching requirements? If the API sits behind a CDN or shared cache and different versions must never collide, URI path versioning makes the version part of the cache key automatically, with nothing extra to configure. Header and media-type versioning both require deliberately configuring the cache to vary on that header, which is easy to forget and hard to spot until stale data appears. This is also where governing versioning policy across teams pays off, because a documented default catches the "we forgot to configure the cache" case before it ships.

4. How much tooling maturity can you assume of consumers? Public developer portals, Postman collections, and quick curl-based exploration all favor a version that's visible in the URL. Media-type versioning assumes consumers are comfortable setting custom Accept headers. That's a reasonable assumption for a sophisticated internal platform team and a poor one for a broad external developer audience.

None of these questions has a universally correct answer. A fintech API with regulatory audit requirements might choose URI versioning precisely because every version is traceable in access logs without extra configuration. A platform team running dozens of internal microservices might choose headers precisely to stop hundreds of route definitions from doubling with every release. The organizational context, not the technical elegance of the strategy, decides the fit.

API Versioning Best Practices​

Once you've chosen a strategy, four practices determine whether it holds up over years of changes rather than falling apart at the first major release.

Maintain Backward Compatibility​

The cheapest version to support is the one you never had to create. Before reaching for a new major version, check whether the change can be made additively: new optional fields, new endpoints, and new optional query parameters. Additive changes don't need a new major version. Under semantic versioning they call for a MINOR bump instead, so a release might move from 1.2.0 to 1.3.0 while the public major version, the /v1/ a client actually sees, stays exactly where it was. Existing clients that ignore the new field keep working as before, provided they don't enforce closed enums or strict schema validation that rejects fields or values they don't recognize.

Save major version bumps for genuinely incompatible changes: removing a field, renaming a field, changing a field's type, or changing required parameters. Treating a backward-compatible addition like a breaking change teaches consumers to fear every release, which defeats the purpose of having a versioning scheme at all.

Use Semantic Versioning​

Semantic Versioning gives version numbers a predictable meaning. MAJOR increments for incompatible changes, MINOR for backward-compatible additions, and PATCH for backward-compatible fixes. Applying that discipline to the API itself, not just to a client SDK, means a consumer can look at a version number and know, before reading a changelog, whether an upgrade is safe to take automatically or needs a compatibility review.

Most REST APIs expose only the MAJOR component in the URI or header (/v2/, Api-Version: 2) and track MINOR and PATCH internally in the changelog, since additive and fix-level changes shouldn't force clients to opt into a new version. What matters is that the numbering scheme is consistent and documented, not that every part of the SemVer number is visible externally.

Communicate Changes and Publish Changelogs​

A version number tells consumers that something changed. A changelog tells them what changed and why. Every version bump, including minor and patch releases, deserves an entry describing the change, whether it's additive or breaking, and what the consumer needs to do.

Publish the changelog somewhere durable and linkable, not buried in release notes that scroll out of view, and keep it alongside the rest of your published API documentation rather than on a separate, easily missed page. For major versions in particular, pair it with proactive notification: email, a status page entry, or an in-console banner for authenticated developers. Consumers who learn about a breaking change from a changelog they had to go looking for are already behind.

Set a Clear Deprecation Timeline​

A deprecation notice with no plan behind it is easy to ignore. Deprecation and a promised shutdown date are two different things: a team can mark a version deprecated and discourage new integrations against it without yet committing to a removal date. When a sunset date is known, publish the actual date rather than a vague "in a future release," and give consumers enough lead time to plan the upgrade. Communicate whatever you know, the deprecated status and, once it exists, the sunset date, through more than one channel: documentation, the changelog, and machine-readable response headers.

Leaving a deprecation notice untouched for years also erodes trust, even without a fixed sunset date. Consumers start to treat "deprecated" as just a label rather than a real signal to move. If a team announces three deprecations that never lead anywhere, the fourth gets ignored, and the team is back to breaking consumers without warning.

Deprecating and Sunsetting Old API Versions​

Deprecating a version and sunsetting it are two different events, and keeping them as two separate signals is what makes the distinction useful. Deprecation means a version is no longer recommended, but it may keep working indefinitely with no removal date attached. Sunsetting means a removal date has actually been set, after which the version stops responding.

RFC 9745 defines a standard Deprecation response header for the first case. It's a structured signal, carrying the date deprecation took effect, that a resource will be or has been deprecated, without committing to when it will stop working. RFC 8594 defines a standard Sunset header for the second case, signaling that a URI is likely to become unresponsive at a specified point in the future. Used together, Deprecation says a version is on its way out, and Sunset says exactly when. A team can send the first long before it has the second to send.

A practical sunset sequence looks like this:

  1. Announce the deprecation with a specific sunset date and a migration guide.
  2. Add the Sunset header to every response from the deprecated version, so automated tooling can detect it.
  3. Step up outbound communication as the date approaches.
  4. On the sunset date, return a clear error (commonly 410 Gone) rather than silently changing behavior, so a client that missed every earlier warning still gets an unambiguous signal about what happened.

Don't sunset a version the moment its usage drops to zero on your dashboard. Long-tail consumers, batch jobs, and rarely run integrations often show "no traffic this week" right up until they run once at quarter-end and fail. A published timeline protects both sides: consumers know exactly how long they have, and the API team has a defensible date to point to instead of negotiating with each straggler individually.

Real-World Examples​

Two production APIs show how the strategy, and the discipline around it, matter more than which of the four mechanisms is chosen.

Stripe uses date-based versioning carried in a request header. Each account is pinned to the API version that was current when it made its first request, and that version keeps governing request and response behavior until the account explicitly upgrades. Major releases carry breaking changes, while the releases between them stay backward-compatible, so an integration built years ago keeps working without constant maintenance. Behind that stability, Stripe's engineering team has described translating current responses back into the shape an older pinned version expects, and has been open about the fact that this compatibility layer isn't free to maintain. Every additional version is more code the team has to understand and carry forward.

GitHub uses a request header carrying a date-named version. Instead of a sequential /v1/, /v2/ scheme, GitHub names each REST API version after its release date, and clients select it with the X-GitHub-Api-Version header. GitHub commits to supporting the previous version for at least 24 months after a new one ships, which gives integrators a predictable, unhurried migration window instead of a scramble. As that window closes, GitHub sends separate Deprecation and Sunset headers: Deprecation carries the date the version will close down, and Sunset carries the date it starts returning 410 Gone. It's the same two-signal idea that RFC 9745 and RFC 8594 describe, applied to a fixed support window.

Both examples rely on the discipline this guide has covered: a stable, well-documented way to signal the version, paired with a published support window that's actually honored. Neither company's approach works because of the particular mechanism it chose. It works because the mechanism is applied consistently and the support commitments are kept.

How WSO2 API Platform Simplifies Versioning​

Everything in this guide (choosing a strategy, applying semantic versioning, publishing changelogs, and honoring a deprecation timeline) is process and communication work that a team has to do consistently, release after release. WSO2 API Platform doesn't replace that judgment, but it removes a meaningful share of the manual coordination behind it.

Versioning is part of the platform's core lifecycle rather than an afterthought. Teams design APIs using OpenAPI or AsyncAPI, apply governance policies, and publish with versioning already built into that flow. Each API moves through documented lifecycle states: Created, Pre-Released, Published, Deprecated, and Retired. A deprecated version stays available to existing subscribers while new subscriptions are disabled, which maps directly onto the deprecation and sunset stages described above.

Governance works through Spectral rulesets, grouped into policies that apply across the organization's API proxies and are evaluated whenever a proxy is updated. Violations are graded by severity (error, warn, or info) and shown on a compliance dashboard with notifications. A rule that checks versioning conventions, for example, flags a non-compliant change as soon as it's made rather than after a support ticket brings it to light. Rulesets can be complemented by AI compliance analysis against governance standards uploaded as PDF documents. None of this catches every breaking change automatically; it catches the ones a rule has been written for. The platform's governance framework explicitly lists versioning consistency among the standards it helps enforce across an organization's API portfolio, alongside API design and documentation.

That consistency matters most for organizations running more than one API gateway. Through the platform's federated gateway model, the control plane can discover APIs running on AWS, Azure, Kong, and Envoy gateways, catalog them in one developer portal, and monitor them against the same governance rules as WSO2-managed APIs. For supported gateways, it can also publish and deploy API configurations directly. A versioning convention defined once is therefore checked everywhere instead of being tracked by hand for each gateway, even though how a deprecation is enforced at runtime still depends on each gateway. For a platform team that inherited gateways from separate acquisitions or business units, that's the difference between one deprecation policy and five slightly different ones that each need separate tracking.

None of this removes the need to decide deliberately which of the four strategies fits a given API, or to communicate changes clearly to consumers. What it changes is how much of that work has to be redone by hand for every API and every gateway an organization runs.

Conclusion​

The four versioning strategies covered here, URI path, query parameter, header, and media type, solve the same problem in different ways, and no single one is right for every API. What separates a versioning scheme that lasts for years from one that collapses at the first major release isn't the mechanism. It's whether the team chooses one deliberately, based on its real audience and pace of change, and then backs it with semantic versioning, real changelogs, and deprecation timelines it actually honors.

Choose a strategy that fits your API's audience and change frequency, commit to it, and pair it with a deprecation policy consumers can plan around. If part of what's slowing you down today is reimplementing the same governance and versioning policy across every gateway your organization runs, WSO2 API Platform's governance framework is built to apply that policy consistently from one control plane, so the discipline in this guide becomes easier to enforce, not just easier to describe.

Frequently Asked Questions​

How do you decide when to create a new API version?​

Create a new major version only for changes that break existing clients, such as removing or renaming a field, changing a field's type, or making an optional parameter required, because those are the changes a client can't safely ignore. Additive, backward-compatible changes, like new optional fields or new endpoints, can usually ship without a version bump.

What are the best practices for managing deprecated API versions?​

Mark a version as deprecated as soon as the decision is made, using the Deprecation header, even before a removal date is set. Add a specific sunset date and the Sunset header once retirement is actually planned, and communicate through more than one channel as that date approaches. When the sunset date arrives, return a clear error instead of silently changing behavior.

WSO2 API PlatformWSO2 API Platform

The open, universal platform for managing every API and AI service at scale. 100% open source.

Explore

BlogTutorialsTopics
© WSO2 LLC. All rights reserved.
WSO2 LegalDo Not Sell My Personal InformationModern Slavery Statement