# API Versioning Strategies Compared — Routebase

> URL path, header, query parameter, media type and date-based versioning, with the routing, caching, tooling and visibility trade-offs of each.

Canonical page: https://routebase.dev/guides/api-versioning/api-versioning-strategies/
Chapter 2 of 12 · API Versioning · Last reviewed 2026-09-13 · The Routebase Team

The five strategies below all solve the same problem, which is telling the server which contract the caller expects. They differ in how the version travels, and that one difference decides how the request routes, how it caches, how visible the version is, and how much work a migration turns out to be.

## URL path versioning

The version sits in the address, as in `/v2/orders/42`. It is the most common choice on public APIs and the easiest to explain.

Routing is trivial, because a proxy or a router matches a prefix and needs to know nothing about the request. Caching works without configuration, since the version is already part of the URL and therefore part of the cache key. Every log line, browser address bar, bug report and support ticket carries the version, which shortens a surprising number of conversations.

The cost is conceptual and it is real. One resource now has two addresses, which conflicts with the idea that a URL identifies a thing and not a representation of it. Clients that build links by string concatenation have to be updated in every place they do it, and a migration therefore touches more code than a header change would.

## Header versioning

The version travels in a request header, such as `Api-Version: 2` or a vendor-specific name. The resource keeps one address for its whole life.

This is the version of the argument that HTTP purists win. It also has a practical trap, because a shared cache that does not know about your header will happily serve one version's response to a caller who asked for the other. The fix is a `Vary` header naming your version header on every response, and it has to be there from the first day.

Header versioning is quieter than the path in every log you own, so plan for that. A caller cannot try version 2 by editing a URL, which makes debugging over the phone harder, and it makes accidental version drift in a client less likely.

## Query parameter versioning

The version is a query parameter, as in `/orders/42?api-version=2026-01-01`. It caches like the path, because the query string is part of the cache key, and it is the easiest of all to try by hand.

The drawback is tidiness. Version parameters mix with filtering and pagination parameters in the same namespace, and it becomes easy for a caller to omit yours by accident. Any API using this strategy therefore needs an explicit answer for a request that carries no version at all.

## Media type versioning

The caller negotiates the version through content negotiation, sending something like `Accept: application/vnd.example.v2+json`, and the server answers with a matching `Content-Type`.

This is the most faithful to HTTP, since it treats a version as one representation of an unchanged resource. It is also the least well served by tooling. Browsers cannot ask for it without help, command line examples get longer, and a caller who sends a plain `Accept: application/json` needs a documented default. Teams that adopt it usually do so because they genuinely version representations separately, and not because they wanted a tidier URL.

## Date-based versioning

Date-based versioning changes what the version is called instead of where it travels. Rather than counting majors, each release is named by its date, and consumers pin to the date they integrated against.

Stripe uses date-based versions with release names such as `2026-08-26.dahlia`, carried in the `Stripe-Version` header, with an account-level default and a per-request override. Its documentation describes monthly releases as backward compatible and major releases as the ones that are not, and webhook endpoints carry their own version so event payloads stay stable while the caller upgrades. GitHub uses plain dates in the `X-GitHub-Api-Version` header, defaults requests without that header to `2022-11-28`, and answers a request naming a retired version with `410 Gone`. Both pages were retrieved on 13 September 2026.

The appeal is that a date is honest about what a version is, which is the state of the contract on a given day. The cost is that consumers can end up pinned to dozens of distinct dates, so this strategy needs strong tooling and a deliberate retirement policy behind it.

## The comparison

_Figure: The same call drawn five times, with the version selector highlighted in each. What separates the strategies is whether the selector rides in the address, as it does for a URL path and a query parameter, or alongside it as metadata, as it does for a header and a media type. An address is cached, logged and linkable, and metadata is none of those things, which is what the table below measures. Date-based numbering is a scheme rather than a carrier, so it rides on whichever of the four you already chose._

| | URL path | Header | Query | Media type | Date-based |
|---|---|---|---|---|---|
| Routing | Prefix match, trivial | Needs header inspection | Query parsing | Content negotiation | Follows its carrier |
| Caching | Free, part of the key | Needs `Vary` | Free, part of the key | Needs `Vary: Accept` | Follows its carrier |
| Visible in logs | Always | Only if logged | Always | Rarely | Follows its carrier |
| Try by hand | Edit the URL | Needs a client | Edit the URL | Needs a client | Depends |
| Tooling support | Universal | Good | Universal | Patchy | Good |
| Migration cost | Every URL a client builds | One client setting | Every URL a client builds | One client setting | One client setting |
| Fits | Public APIs, wide audiences | Internal and platform APIs | Platforms with URL sharing | Representation-heavy designs | Products with frequent contract changes |

Azure API Management offers the first three of these as configurable schemes, calling them path, header and query string, with a version identifier that is any string you choose. Its documentation is at `learn.microsoft.com/azure/api-management/api-management-versions`, retrieved on 13 September 2026. That a managed gateway exposes exactly those three is a useful signal about which of them the wider ecosystem actually supports.

## What every strategy needs answered

Whichever carrier you pick, three questions decide whether it works in practice, and none of them is about the carrier.

**What happens when no version is sent.** Serving the latest is friendly and it silently upgrades every unversioned caller into the next breaking change. Serving a fixed default is predictable and it strands newcomers on old behaviour. Rejecting the request is strict and it is the only option that never surprises anybody. Public APIs usually pick a fixed default, and internal APIs can afford to require the version.

**What happens when an unknown version is sent.** A typo should produce a clear error and never a silent fallback. Returning the nearest version is the one behaviour that guarantees a confusing bug report later.

**Where the version applies.** A version per API is simple and forces the whole surface forward at once. A version per endpoint is flexible and multiplies the combinations you have to test. The middle path, which most teams end up at, is one version for the API with additive change inside it.

## In Routebase

A specification carries its versioning strategy as configuration instead of convention. You choose URL path, header, query parameter or content negotiation, name the carrier, and set what happens when a consumer sends nothing or sends something unknown.

_Screenshot: The strategy card sets the carrier and the default behaviour, and previews the resulting request as curl._

The setting is not decorative. A Routebase mock server routes requests by the strategy you configured, so your frontend team develops against the same version selection your production API will use. Strict mode rejects an unknown version identifier instead of guessing, and the documentation portal shows consumers which strategy the API uses next to its version switcher. The [Spec Versioning guide](https://docs.routebase.dev/versioning/) walks through each field, and [Mock Server](https://docs.routebase.dev/mock-server/) covers how version routing behaves against a mock.

## Frequently asked questions

### What are the main REST API versioning strategies?

There are five in common use. URL path versioning puts the version in the address, and header versioning carries it in a request header. Query parameter versioning appends it to the URL, while media type versioning negotiates it through the Accept header. Date-based versioning replaces a counter with a release date. The first four decide where the version travels, and the fifth decides what the version is called.

### Is URL path or header versioning better?

URL path versioning is visible in every log line, every browser address bar and every support ticket, and it caches without any extra configuration. Header versioning keeps one address for one resource, which is closer to how HTTP was designed. It needs a Vary header, because otherwise a cache can serve one version's response to a caller who asked for another. Most public APIs choose the path for its visibility.

### How does Stripe version its API?

Stripe uses date-based versions with release names, such as 2026-08-26.dahlia, sent in the Stripe-Version header. An account has a default version set in the dashboard, requests may override it per call, and webhook endpoints carry their own version so event payloads stay stable. The documentation for this is at docs.stripe.com/api/versioning, retrieved on 13 September 2026.

### Does API versioning affect caching?

Yes, and it is the difference that most often decides the choice. A version in the path or the query string is part of the cache key already, so two versions never collide. A version in a header is invisible to a cache unless the response carries a Vary header naming it. Without that header, a shared cache can serve a version 1 response to a caller who asked for version 2.

---

[Routebase](https://routebase.dev/) — [Sign up](https://app.routebase.dev/): Every account starts with a 14-day Pro trial — no credit card required.
