Skip to content
5 min read·Lesson 9 of 10

API Versioning and Deprecation

How to evolve an API without breaking clients: additive changes, version strategies, deprecation policy, and sunset headers.

Every API ages. Fields get added, semantics shift, names change. The question is not whether to change but how to do so without making your customers' day worse than yesterday's.

Additive vs Breaking Changes

Additive (safe)Breaking (need a version)
New endpointRemoving an endpoint
New optional field in requestMaking an existing field required
New field in responseRemoving or renaming a field
New optional query paramChanging default behaviour
New status code in error response (when documented as possible)Changing a status code's meaning

Most evolution is additive. Save versions for the rare moments where breaking change is unavoidable.

Versioning Strategies

1. URL versioning

GET /v1/orders
GET /v2/orders
  • + Visible, simple to test, easy to route.
  • + Browsers, curl, and humans handle it without thought.
  • − Implies "v1" and "v2" are different products even when they overlap heavily.

The most common choice. Stripe, GitHub, Twitter use variants of this.

2. Header versioning

GET /orders
Accept: application/vnd.api+json; version=2
  • + URL stays canonical.
  • + Cleaner caching boundaries.
  • − Harder to test in a browser; tooling matters more.

3. Date versioning

GET /orders
Stripe-Version: 2024-09-30
  • + Each customer pins to a date; new clients pick the latest by default.
  • + No breaking changes silently arrive.
  • − Server must maintain logic for many versions simultaneously.

Stripe popularised this; great for billing-grade stability.

4. Don't version (GraphQL)

GraphQL APIs typically deprecate fields rather than version the whole schema:

type Order {
  total: Int! @deprecated(reason: "Use totalCents")
  totalCents: Int!
}

Clients keep working; new ones use the new field; the old field is removed only after adoption metrics fall to near zero.

Picking One

For new APIs: URL versioning is the safest default unless you have a specific reason. Pick /v1, document it, then strain to never need a /v2.

Deprecation Policy

What you publish matters more than the mechanism. A good policy includes:

  • How long deprecated features remain (commonly 6–12 months for paid APIs).
  • How customers are notified — email, dashboard, response headers, changelog.
  • Migration documentation with concrete diffs.
  • Optional: a developer dashboard showing which deprecated calls a customer still makes.

Telling Clients Now

Two standard headers:

Deprecation: Sat, 01 Jun 2025 00:00:00 GMT
Sunset: Sat, 01 Dec 2025 00:00:00 GMT
Link: <https://docs.example.com/migrate-v2>; rel="deprecation"
  • Deprecation — the date the feature was deprecated (or true if simply deprecated).
  • Sunset — the date the feature will be removed.
  • Link — pointer to migration docs.

Good SDKs surface these as warnings; great ones tag the call site.

Tracking Adoption

Before you remove anything, you need data:

  • Per-customer usage of each deprecated path.
  • Trend over time (going to zero or stuck?).
  • Top-N customers still on the old version.

Reach out to those customers individually before sunset day. Big customers, in particular, deserve a phone call. Don't surprise people with 410 Gone.

What Removal Looks Like

The day after sunset:

GET /v1/orders
→ 410 Gone
{
  "error": {
    "type": "version_removed",
    "message": "v1 was sunset on 2025-12-01. See https://docs.example.com/migrate-v2"
  }
}

410 Gone is the right code — it tells caches and clients the resource is permanently gone, not temporarily down.

Internal vs Public APIs

  • Public APIs — slow, deliberate, with policy and notice.
  • Internal APIs — can move faster if all callers are known. Use the same patterns at smaller scale to keep good habits.

The Hardest Truth

The best version is the one you never had to ship. Spend the design effort up front: model resources well, name fields generously, allow extension via input objects rather than positional arguments, default to optional fields. Most APIs that need v2 within a year did so because v1 was rushed.

Cert Mapping

CertScope
AWS SAAAPI Gateway stages, deployments, custom domains
AWS Data EngineerSchema evolution in data APIs

The final lesson covers what makes the difference between an API people tolerate and one they recommend: documentation and developer experience.

Key Takeaways

  • Most changes can and should be additive — no version bump needed.
  • URL versioning is simplest; header versioning is cleaner; pick one and commit.
  • A clear deprecation policy is more important than the versioning strategy.
  • Sunset headers, deprecation warnings, and adoption metrics drive client migration.
  • GraphQL prefers field-level deprecation over global versioning.

Test your knowledge

Try exam-style practice questions to reinforce what you've learned.

Practice Questions →