Card 04/ 09

ComparisonDifficulty: Intermediate2 min

Path vs Header vs Media Type vs Query Versioning

Four places to put a version, each trading visibility against how cleanly a URL names a resource.

When a change must break, the API publishes a new version beside the old one, and each request has to say which version it wants. There are four common places for that: the path, a header, the media type and a query parameter. All four work; they differ in how visible the version is, how caches and routers handle it, and whether a URL still names one resource.

Each versioning strategy, an example request, and its trade-offs
StrategyExample requestStrengthsCosts
PathGET /v2/orders/1001Visible in every link and log; simple to route and cacheEvery URL changes between versions; the same order has two URLs
HeaderGET /orders/1001 with Api-Version: 2URLs stay stableInvisible in a link; caches need Vary on the header; easy to forget in a browser
Media typeAccept: application/vnd.example.v2+jsonVersions the representation, which is what changedClients must set a custom content type; awkward to try by hand
Query parameterGET /orders/1001?version=2Visible and easy to tryEasy to leave off by accident; mixes the version with filters

Which to choose. Path versioning is the most common for public APIs, because it is the easiest for clients to see and use, and its costs fall mainly on people who care about URLs as stable names. Header and media type versioning suit teams that want one URL per resource and clients able to set headers.

Dated versions are a variant. Stripe sends a date such as Stripe-Version: 2024-06-20 in a header, and pins each account to the version it started on. GitHub's REST API does the same with X-GitHub-Api-Version. A date says when the behaviour was defined, and lets an API make many small breaking changes without a major number for each.

What every strategy needs. A default for requests that send no version, which should be the oldest supported or the version pinned to the client rather than the newest, so a client that forgot is not broken by the next release. And a version only for breaking changes; additive changes go into the current version.

So choose one place, use it everywhere, and create a version only when something must break.