Public APIs
All posts

API Versioning Best Practices: What 225 Public APIs Do

Updated

Quick answer: Version only on a breaking change, and put the version in the URL path. Of the 225 public APIs whose OpenAPI specs we read on 22 October 2026, 140 (62%) do exactly that, 1 uses a header, 2 a query parameter and 84 (37%) declare no version at all. Only 17 mark any operation deprecated, so a clear deprecation policy already puts you ahead.

Versioning is the contract problem behind every public API: you need to evolve, and the clients you do not control need yesterday's behaviour to keep working. Most guides argue the strategies from first principles. We list public APIs for a living, so we measured which strategies are actually in production.

How we measured it

On 22 October 2026 we probed the 1,629 live APIs in our directory for an OpenAPI document at the conventional locations (/openapi.json, /swagger.json, /v3/api-docs and the rest) and kept one spec per listed API: 225 APIs, 10,644 endpoint paths and 11,481 operations. For each spec we recorded where the version lives and whether any operation is marked deprecated: true, then called one GET endpoint per spec and read the response headers. The details are in the methodology section.

The same caveat applies as in our REST API naming conventions study, which used the same probe: an API that publishes a spec at a conventional location is better tooled than average, so read these numbers as "what careful teams do".

Where do real public APIs put the version?

| Where the version lives | APIs | Share of 225 | |---|---|---| | In every path (/v1/users) | 53 | 23.6% | | In some paths (/v1/ next to unversioned or /v2/ paths) | 40 | 17.8% | | Only in the base URL (servers or basePath ends in /v2) | 45 | 20.0% | | A bare number in the path (/2/tweets) | 2 | 0.9% | | A request header | 1 | 0.4% | | A query parameter | 2 | 0.9% | | Nowhere in the spec | 84 | 37.3% |

Two APIs count twice: Notion versions in the path and in a header, monday.com in the path and in a query parameter. Add the URL rows up and the URL carries the version in 140 of 225 APIs (62.2%). v1 is the version 110 of them expose, v2 32, v3 7, and nothing in the sample goes past v6. Sixteen APIs expose two or more major versions in one spec, which is what a migration in progress looks like: REST Countries documents /countries/v5/... beside its older paths, and one German job-search API runs v4 and v6 side by side.

The lone header user is Notion: every path starts with /v1/, and a Notion-Version header with a date value selects the behaviour. The bare-number pattern belongs to X, whose 186 paths all start with /2/.

One number that looks like versioning and is not: 198 of the 225 specs (88%) carry a semantic version in info.version, values like 2.15.0 or 1.960.0. No client can request 2.15.0. The only version that counts is the one a request can select.

What are the main API versioning strategies?

URI path versioning

The version lives in the path: api.example.com/v1/users. It is explicit, visible in logs and docs, trivially cacheable, and every client understands it instantly. The cost is philosophical: the same resource now has two URLs. It is the majority choice in our sample. The 45 APIs that keep the version only in the base URL get the same effect, but the version then disappears from every path in their documentation, which is where developers copy from.

Header or media-type versioning

The URL stays clean and the version moves into a request header. GitHub's REST API is the best-known example: clients send an X-GitHub-Api-Version header naming a dated version, requests without it default to the 2022-11-28 version, and a version that is no longer supported answers 410 Gone (GitHub docs, read 22 October 2026). This keeps resource URLs stable at the cost of being invisible: a curl command without the header silently hits the default. One API in 225 declares such a header in its spec, so expect thinner tooling around it.

Date-based version pinning

Stripe runs the most elaborate version of this model. Since its 2024-09-30.acacia release it ships a new API version every month with no breaking changes and a major release twice a year that carries the breaking ones; the current version is 2026-09-30.endive, and a request uses the account's default version unless a Stripe-Version header overrides it (Stripe docs, read 22 October 2026). It is the most client-friendly model and the most work to operate. No API in our sample puts a date in its URL; Notion's dated header is the closest match.

Query parameter versioning

?v=1 appended to the URL. Two APIs in the sample declare one (the Hebrew Calendar API's v, monday.com's version). It works, but it interacts poorly with caches, gets stripped by intermediaries, and clutters every example. There is little reason to pick it for something new.

When do you actually need a new API version?

Only for breaking changes. A change breaks clients when it:

  • removes or renames a field, endpoint or parameter
  • changes a field's type, format or meaning
  • makes a previously optional input required
  • changes authentication requirements or error semantics

Additive changes (new endpoints, new optional parameters, new fields in responses) need no version bump, provided you document one rule: clients must ignore fields they do not recognise. Version on breakage, and only on breakage.

Which strategy should you pick?

  • Small or new public API: /v1/ in every path from the first endpoint. That is the pattern 53 APIs in the sample follow completely; the 40 with a version in only some paths are a mix of a /health endpoint left outside /v1/, legacy routes kept beside new ones, and a /v2/ rolled out endpoint by endpoint.
  • Developer platform with long-lived integrations: header or date-based pinning, as GitHub and Stripe run it, because URL stability and deliberate client upgrades matter more than curl-friendliness.
  • Internal API with few consumers: often no versioning at all, which is also where 37% of public specs sit, with additive evolution and coordination between the teams involved.

Whatever you choose, choose once. Only 2 of 225 APIs mix two mechanisms, and both are large platforms that can afford the documentation to explain it.

How do you deprecate an old API version gracefully?

Here the sample is thin, which is the finding. 17 of 225 specs (7.6%) mark at least one operation deprecated: true, 87 of 11,481 operations (0.8%). Of the 220 endpoints that answered our GET, none sent a Sunset header and one sent Deprecation: Frankfurter's /v1/latest answered deprecation: @1779103800, the RFC 9745 form for 18 May 2026. Ten responses carried a header naming the API version, x-api-version in six of them. The documentation pages of the 251 keyless, CORS-enabled APIs we list told the same story: 244 answered, and not one carried either header.

So doing the following already puts you ahead of 9 in 10 public APIs:

  1. Announce early, with dates. A changelog entry and an email beat any header, because humans plan migrations and machines only fail them.
  2. Send machine-readable signals. Mark the operation deprecated: true in your spec, return a Deprecation header (RFC 9745) on old-version responses and a Sunset header (RFC 8594) with the shutdown date, so client libraries and monitors can alert automatically. GitHub sends both while a version approaches its end of support.
  3. Publish a migration guide that maps every breaking difference to the new version, with before-and-after request examples.
  4. Watch real usage before the shutdown. Traffic on the old version tells you which consumers have not migrated.
  5. Fail loudly at the end. When the sunset date arrives, return a clear error with a link to the migration guide (GitHub uses 410 Gone), not a silently different response.

What are the most common API versioning mistakes?

  • Versioning every release. Version numbers are for breakage, not for marketing. SemVer minor and patch numbers belong in changelogs, not URLs.
  • Breaking behaviour without a version bump. Changing a date format or tightening validation inside v1 is a breaking change, however small it feels.
  • No documented default. If requests without an explicit version get served, document exactly what they get and how stable that default is.
  • Keeping old versions alive forever, silently. Sixteen specs in the sample expose two or more major versions, and most carry no deprecation marker on the older one. An unmaintained v1 that still answers is a security surface and a data-consistency risk.
  • Forgetting errors are part of the contract. Changing error codes or response shapes breaks clients as hard as changing success payloads. Our REST API best practices guide covers the error shape and API rate limiting the 429 contract.

If you are building your first API, the development APIs in the directory are a useful field study: open a few of their specs and note how each one handles versions, defaults and deprecation notices. Patterns you see three times are conventions; patterns you see once are opinions.

Methodology

Date: 22 October 2026. Corpus: the 1,629 live public APIs in our directory (status published, archived false). Probe: for each listing, the origin of its stored URL, the folder of that URL, the bare domain and its api. subdomain, each tried at 26 conventional spec locations; a document counted when it parsed as OpenAPI 2 or 3 (JSON or YAML) with at least one path. 275 documents answered; where one listing served several, we kept the one with the most paths, giving 225. Counting rules: a path is versioned when a segment matches v plus digits; the base URL when servers[].url or basePath does; a header or query parameter when the spec declares one whose name is version or v and whose description selects the API version (three look-alike parameters that select a chart library, an app version or a dataset snapshot were excluded). Live headers: one GET per spec, the first parameter-free GET path under the first server, 220 answered with any status. Documentation pages: one GET to the stored URL of each of the 251 keyless, CORS-enabled listings, 244 answered. We will re-run this with the naming study.

FAQ

Is a /v1/ path RESTful?

Strict REST theory says a version does not belong in a resource identifier, and header-based versioning is the purist answer. In practice 140 of the 225 public APIs we measured put it in the URL and one in a header, because explicitness wins for onboarding and debugging. Both are legitimate; pick based on your audience, not the argument.

Should I version my API from day one?

Expose /v1/ from launch. It costs nothing, sets client expectations, and turns your first breaking change from an architecture crisis into a routine release.

Does semantic versioning apply to APIs?

Loosely. Consumers of a hosted API only experience major versions, because minor and patch changes are additive by definition. Keep SemVer for your SDKs and changelogs; keep the API surface to simple, rarely-changing major versions.

How long should old API versions be supported?

There is no standard. GitHub commits to supporting a previous REST API version for at least 24 months after a new one is released (its docs, read 22 October 2026); smaller public APIs commonly promise 6 to 12 months between notice and sunset. The commitment matters more than the number: pick a window you can honour, publish it, and honour it.