API Lifecycle

API Versioning

APIs change. Versioning is how you ship improvements without breaking the clients that already depend on you — and how you retire old versions on purpose.

intermediate13 min readUpdated Sep 15, 2026
versioning.js
js
// versioning.js
app.get("/v1/posts", listPostsV1);
app.get("/v2/posts", listPostsV2);

// signal that v1 is going away
app.use("/v1", (req, res, next) => {
  res.set("Deprecation", "true");
  res.set("Sunset", "Wed, 31 Dec 2026 23:59:59 GMT");
  res.set("Link", '</v2/posts>; rel="successor-version"');
  next();
});
Breaking
Removes or changes behaviour
Additive
Usually safe
Common
URI versioning
Flexible
Header versioning
Retirement
Deprecation and Sunset
Rule
Never break silently

Why it matters

Why versioning matters

Evolve without breaking

Versioning lets you improve the API while clients keep working on the old contract until they migrate.

Predictable changes

A clear policy on what counts as breaking means clients know when they must act.

Deliberate retirement

Deprecation and sunset headers turn removing an old version into a communicated plan, not a surprise.

The big picture

The three ideas behind versioning

Know what breaks clients, choose a strategy you can live with, and retire versions on a schedule.

Compatibility

Classify

Decide whether a change is additive and safe or breaking and version-worthy.

Strategy

Expose

Choose how clients select a version, in the URL, a header or the media type.

Lifecycle

Retire

Announce deprecation, set a sunset date and monitor usage before removing anything.

Versioning at a glance

The core ideas

URI versioning

/v1/posts, the most visible and easiest to test.

Header versioning

A custom header or Accept parameter selects the version.

Query parameter

?version=2, simple but easy to forget.

Additive changes

New optional fields and endpoints rarely break clients.

Deprecation

The Deprecation header signals an upcoming removal.

Sunset

The Sunset header gives the date a version stops working.

A short history

From frozen APIs to continuous evolution

  1. 2000s

    Versioned URLs

    Public APIs adopt /v1 style paths as the norm.

    2000s
  2. 2012

    Header negotiation

    Some APIs move versioning into headers to keep URLs stable.

    12
  3. 2017

    Deprecation headers

    Standard headers for deprecation and sunset gain traction.

    17
  4. 2020s

    Continuous evolution

    Additive, backwards-compatible change reduces the need to bump versions.

    2020s
  5. Today

    Explicit policy

    Mature APIs document what counts as breaking and how long versions live.

    Today

The complete guide

API Versioning: Everything you need to know

Why versioning matters

An API is a promise. Once clients depend on it, changing it carelessly breaks their apps, and broken clients are expensive for everyone. Versioning is how you ship improvements while giving clients a stable contract and a clear path to migrate.

The goal is not to avoid change. It is to make change predictable: know which changes are safe, expose versions in a consistent way, and retire old ones on a communicated schedule instead of surprising people.

Breaking versus additive changes

Most versioning pain comes from not classifying changes. Start with a clear rule.

Usually safe (additive):

  • Adding a new optional field to a response.
  • Adding a new endpoint.
  • Adding a new optional request parameter.
  • Adding a new enum value, if clients tolerate unknown values.

Breaking (needs a version):

  • Removing or renaming a field.
  • Changing a field’s type or format.
  • Making an optional parameter required.
  • Changing the meaning or default of existing behaviour.
  • Changing status codes or error shapes clients depend on.

Designing clients to ignore unknown fields is the single best way to keep additive changes safe. Document the rule so nobody has to guess.

Versioning strategies

There are four common ways to let clients select a version.

URI versioning — the version is part of the path.

GET /v1/posts
GET /v2/posts

It is visible, easy to route, easy to cache and easy to test in a browser. It is the most common choice for public APIs, even though purists argue the URL should not change.

Header versioning — a custom header selects the version.

GET /posts
X-API-Version: 2

URLs stay stable, which is nice for caching by resource, but the version is invisible in logs, links and browser tests.

Media-type versioning — content negotiation selects the version.

GET /posts
Accept: application/vnd.example.v2+json

This is the most REST-aligned approach and composes with content negotiation, but it is the hardest to discover and debug.

Query parameter?version=2. Simple, but easy to omit and awkward for caching.

Whichever you choose, apply it consistently and document it. Mixing strategies is worse than picking a less fashionable one.

Deprecation and sunset

Removing a version should be a process, not an event.

Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT
Link: </v2/posts>; rel="successor-version"
  • Deprecation announces that a version or endpoint is going away.
  • Sunset gives the exact date it stops working.
  • Link points to the replacement.

Pair the headers with a migration guide, changelog entries and direct communication to heavy users. Then monitor usage: if a meaningful share of traffic is still on the old version near the sunset date, extend it rather than break those clients.

Running versions side by side

Supporting two versions means the code must serve both. Common approaches:

  • Versioned handlers that map to shared services, so business logic lives in one place.
  • Adapters that translate between the old representation and the new one.
  • Feature flags for gradual rollout of new behaviour.
  • Separate specs per version, generated and published alongside the API.

Keep the difference between versions small. Large forks of logic are hard to maintain and easy to let drift.

Communicating change

Versioning only works if clients know what is happening.

  • Publish a changelog and mark breaking changes clearly.
  • Keep an up-to-date OpenAPI spec per version.
  • Send deprecation notices in headers and, where possible, by email.
  • Provide a migration guide with before-and-after examples.
  • Give a support window that matches your users’ release cycles.

Best practices

  • Classify every change as additive or breaking before shipping.
  • Prefer additive changes; bump the version only when you must.
  • Pick one versioning strategy and use it everywhere.
  • Never remove a field or endpoint without a deprecation period.
  • Announce deprecation with headers and a sunset date.
  • Monitor traffic per version before retiring one.
  • Keep shared logic behind version-specific adapters.

Common mistakes

  • Breaking clients silently by changing a response shape.
  • Versioning every tiny change and creating a maintenance burden.
  • Mixing versioning strategies across the same API.
  • Removing a version without warning or a migration path.
  • Letting old versions run forever with no plan.
  • Forgetting to update documentation and specs per version.

Where to go next

Versioning is how an API survives contact with real clients. Build on clean REST design, describe each version with OpenAPI, and use HTTP headers to communicate deprecation. Then write a one-page policy for your own API: what counts as breaking, and how long versions live.

Making a change

Adding an optional field is usually safe. Renaming or removing a field, or changing a type, breaks clients and needs a new version.

Additive
{
  "id": "42",
  "title": "Hello",
  "tags": []
}
// new optional field,
// existing clients unaffected
Breaking
{
  "id": "42",
  "headline": "Hello"
}
// "title" removed;
// every client breaks

Selecting a version

URI versioning is explicit, cacheable and easy to test. Header versioning keeps URLs stable but is harder to see and share.

URI
GET /v2/posts
Accept: application/json
Header
GET /posts
Accept: application/vnd.example.v2+json

Trade-offs

Should you version at all?

Versioning is a safety net, not a goal. Design for compatibility first, and reach for a new version only when a change truly breaks clients.

Strengths

  • Protects existing clients

    A new version lets you ship breaking improvements while old clients keep working until they migrate on their own schedule.

  • Forces a clear policy

    Deciding what counts as breaking makes the contract explicit and keeps teams honest about compatibility.

  • Enables deliberate retirement

    Deprecation and sunset headers turn removing an old version into a communicated, monitored plan.

Trade-offs

  • Every version is a cost

    Each supported version multiplies testing, documentation and maintenance, so old ones must be retired on a schedule.

  • Fragments the ecosystem

    Clients, docs and SDKs split across versions, and support questions become harder to answer.

  • Often avoidable

    Additive, backwards-compatible changes cover most needs, so versioning can become a habit that outpaces real breakage.

FAQ

Frequently asked questions

Keep learning

Related topics from the roadmap.

$ start learning

Ready to start learning API Versioning?

Our interactive tutorial walks you through API Versioning step by step — with quizzes and real code you can run in the browser.