API Versioning & Deprecation
Versioning Model
SpeedPy uses URL-path versioning. Every resource endpoint lives under a version prefix:
/api/v1/me/
/api/v1/teams/
/api/v1/products/
The unversioned /api/ prefix is a dispatcher — it mounts versioned routes and shared infrastructure (schema, docs, auth). Individual endpoints must always be under a version prefix like /api/v1/....
Auth Endpoints
Authentication endpoints (/api/auth/token/, /api/auth/token/refresh/, /api/auth/token/revoke/) are intentionally unversioned. They are considered stable infrastructure and are not subject to the versioning lifecycle described below.
When to Add to v1 vs. Plan v2
Add to /api/v1/ (backward-compatible)
- New endpoints
- New optional request/response fields
- New query parameters
- New non-conflicting enum values
- New HTTP methods on existing endpoints
Plan /api/v2/ (breaking changes)
- Removing or renaming fields
- Changing a field from optional to required (or vice versa)
- Changing field semantics or data types
- Removing endpoints
- Changing URL structure
- Changing authentication or permission requirements
When in doubt, prefer adding to v1 in a backward-compatible way.
Support Window
Once /api/v(N+1)/ is released, /api/vN/ continues to receive bug fixes and security patches for at least 12 months. After the support window closes, the old version may be removed.
Projects using SpeedPy as a boilerplate can adjust this window to fit their needs — 12 months is the recommended default.
Deprecation Pattern
SpeedPy defines a deprecation pattern for communicating upcoming removals. The pattern is documented here for use when needed — the boilerplate does not ship with active deprecations.
1. Mark in the OpenAPI Schema
Use drf-spectacular's extend_schema to mark deprecated endpoints or parameters:
from drf_spectacular.utils import extend_schema
from rest_framework.views import APIView
from rest_framework.response import Response
class OldEndpointView(APIView):
@extend_schema(deprecated=True)
def get(self, request):
return Response({"message": "Use /api/v2/new-endpoint/ instead"})
2. Return Sunset and Deprecation Headers
Add Deprecation and Sunset headers (RFC 8594) to signal when an endpoint will be removed:
from datetime import datetime
from rest_framework.response import Response
from rest_framework.views import APIView
class LegacyItemsView(APIView):
def get(self, request):
data = {"items": []}
response = Response(data)
response["Deprecation"] = "true"
response["Sunset"] = "Sat, 01 Mar 2026 00:00:00 GMT"
response["Link"] = '</docs/api-versioning>; rel="sunset"'
return response
The Sunset header value must be an HTTP-date indicating when the endpoint will stop functioning. The Link header should point to migration documentation.
3. Bump OpenAPI Schema Version
Update the info.version in the OpenAPI schema for each release that changes API surface:
- Patch bump (e.g.
1.0.0→1.0.1): bug fixes, documentation changes - Minor bump (e.g.
1.0.0→1.1.0): new endpoints, new optional fields, deprecations - Major bump (e.g.
1.0.0→2.0.0): new/api/vN/version introduced
This is configured in settings.py via SPECTACULAR_SETTINGS["VERSION"].
4. Document in the Changelog
Record all API changes in the Changelog under an API Changes section:
- New endpoints or fields
- Deprecations (with sunset date)
- Removals
- Migration guides for major version bumps
Summary
| Question | Answer |
|---|---|
| Versioning scheme | URL-path (/api/v1/..., /api/v2/...) |
| Auth endpoints versioned? | No — unversioned and stable |
| Backward-compatible addition? | Add to current version |
| Breaking change? | New major version |
| Support window after new version | 12 months (configurable) |
| Deprecation signal | Deprecation + Sunset headers, OpenAPI deprecated=True |
| Where to log changes | Changelog, OpenAPI schema version bump |