URL, Header and Media-Type Versioning
Compare versioning approaches and implement clean route grouping so clients upgrade without breakage.
Why Version an API at All?
Once real clients depend on your API, you can no longer change a response shape freely. Renaming a field, removing a key, or changing a status code can break production apps you do not control.
Versioning lets you ship breaking changes under a new label while the old behavior keeps working. Clients upgrade on their own schedule.
- Non-breaking change (add an optional field) usually needs no new version.
- Breaking change (remove/rename a field, change types, change semantics) needs a version boundary.
This lesson compares the three classic strategies: URL path, Header, and Media-Type versioning.
Strategy 1: URL Path Versioning
The most common and most visible approach: put the version directly in the path, e.g. /v1/users and /v2/users.
- Pros: obvious in logs and browsers, trivial to route, easy to cache, simple to document.
- Cons: the version leaks into every URL; technically a URL should identify a resource, not a representation.
In FastAPI you express this with a prefix on an APIRouter. Each version gets its own router and its own prefix.
from fastapi import APIRouter, FastAPI
app = FastAPI()
v1 = APIRouter(prefix="/v1", tags=["v1"])
v2 = APIRouter(prefix="/v2", tags=["v2"])
@v1.get("/users/{user_id}")
def get_user_v1(user_id: int):
return {"id": user_id, "name": "Ada Lovelace"}
@v2.get("/users/{user_id}")
def get_user_v2(user_id: int):
# v2 splits name into first/last
return {"id": user_id, "first_name": "Ada", "last_name": "Lovelace"}
app.include_router(v1)
app.include_router(v2)All lessons in this course
- URL, Header and Media-Type Versioning
- Cursor vs Offset Pagination at Scale
- Dynamic Filtering and Sorting Parameters
- Designing Stable Response Envelopes