API versioning enables backward-compatible evolution of public interfaces without breaking existing consumers — a critical concern for any API with external clients. The three dominant strategies are URI versioning (/api/v1/users), request header versioning (Accept: application/vnd.api+json;version=2), and query parameter versioning (?api-version=2.0). Stripe's API versioning model — where each API key pins to a specific version date and new customers default to the latest — is widely regarded as the gold standard for developer experience.

Key Points

  • URI versioning: simplest to implement and cache, violates REST principle that a URI identifies a unique resource — but most widely adopted (Twitter, GitHub, Stripe use it).
  • Header versioning: keeps URIs clean and semantic, harder to test in browser address bar — used by GitHub ("Accept: application/vnd.github.v3+json").
  • Content negotiation: REST-pure approach using Accept header with custom media types — most correct, least common due to tooling complexity.
  • Backward compatibility rules: additive changes (new fields, new endpoints) are safe; removing fields, changing types, or renaming are breaking changes.
  • Deprecation lifecycle: announce deprecation with Sunset header (RFC 8594), provide 6–12 month migration window, log warnings on deprecated endpoint calls.
  • API versioning in gRPC: use package versioning (package myapi.v1) — wire format compatibility via Protocol Buffer field numbering rules.
  • GraphQL avoids versioning: fields are deprecated individually, clients fetch exactly what they need — eliminates over-fetching and most versioning needs.
  • Stripe's version-by-date model: new accounts get the latest API; existing accounts are pinned to their creation date version; Stripe maintains compatibility for all versions indefinitely.

Real-World Example

Stripe maintains 7+ years of API versions simultaneously, with over 100 distinct API versions. Their API version compatibility layer translates requests between versions in middleware — each breaking change adds one translation layer, composing them transparently for older clients.