← Back to W30D1

API Versioning Strategies

Change the API without breaking the clients you forgot about

Why Versioning Matters (Especially for ML)

APIs tend to keep their old clients forever. A mobile app someone installed 18 months ago is still calling your endpoints today. A partner integration nobody remembers is a cron job in their cloud, hitting /predict every hour.

ML makes this worse. Retraining changes outputs. Feature additions change inputs. If you don't plan for versioning, your first meaningful model update will break production for every client you didn't know existed.

Rule: breaking changes need a new version. Additive changes (new optional fields, new endpoints) don't. The hard part is being honest about which is which.

Three Common Strategies

recommended for MVPsURL versioning

Put the version in the path: /v1/predict, /v2/predict. Blunt, visible, hard to misuse.

GET  /v1/predict
POST /v1/predict

# Later, once v2 is ready, v1 keeps working:
POST /v2/predict

Pros

  • Obvious in logs, browser, curl
  • Easy to route — different code paths or even different services
  • Clients can't accidentally use the wrong version
  • Supported by every HTTP tool without config

Cons

  • URLs for the same resource diverge across versions
  • Purists argue it violates REST (resource shouldn't change URL)
  • Version is visible to end users — can feel ugly

common at scaleHeader versioning

Keep the URL stable; clients send a version header.

GET /predict
Accept: application/vnd.myapi.v1+json

# Or a custom header:
X-API-Version: 2

Pros

  • URLs stay clean and resource-shaped
  • Works well with content negotiation
  • Can version request format and response format separately

Cons

  • Invisible in logs and browser URLs — harder to debug
  • Clients forget to set it, default quietly to the wrong version
  • Curl examples get longer

meta-strategySemantic versioning

Not a transport strategy — a rule for when to bump a version number. MAJOR.MINOR.PATCH, usually reported in /metadata and response bodies.

MAJOR

Breaking change. Remove a field, change a type, change an error shape, rename an endpoint. Bump v1v2.

MINOR

Backward-compatible additions. New optional request field, new optional response field, new endpoint. Same URL version, bump 1.2.01.3.0.

PATCH

Bug fixes, docs, performance. No observable contract change. 1.2.01.2.1.

Picking a Strategy — Decision Tree

Q1: Is this an MVP or early-stage product?
Yes → Use URL versioning with /v1/. Simplest possible choice.
Q2: Do you have large enterprise clients who pin headers and can't change URLs easily?
Yes → Header versioning, usually via Accept:.
Q3: Either way — are you reporting api_version in /metadata and response bodies?
Always yes. Even if you use URL versioning, include semantic version in responses so minor changes are visible without a URL change.

Is This a Breaking Change? — Quick Test

✅ Safe (minor bump)

  • Add a new optional field to the response
  • Add a new endpoint
  • Add a new optional query parameter
  • Loosen a validation (was "1-100 chars", now "1-5000 chars")

❌ Breaking (major bump)

  • Remove or rename a field
  • Change a field's type (string → object)
  • Tighten validation (was optional, now required)
  • Change an error shape or status code
  • Change the meaning of an existing field
Trick question: is renaming predictionlabel in the response a breaking change? Yes. Every client parsing the response is broken. This is the change that gets shipped on a Friday, paged on Monday. Version it.

What You'll Use Tonight

In your api/main.py and api/api_spec.md: