API Deprecation — Complete Guide
In this tutorial, you'll learn about API Deprecation. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
API deprecation is the Process of marking an API version or feature as no longer recommended for use, while communicating a timeline for its eventual removal to clients.
What You'll Learn
By the end of this lesson, you will implement deprecation warnings, design sunset policies, and manage the full lifecycle of API version deprecation.
Why It Matters
Proper deprecation gives clients time to migrate. Abrupt removal breaks integrations, erodes trust, and creates emergency work for client teams.
Real-World Use
Twilio deprecates API versions with 12+ months notice. They send deprecation emails, add headers to responses, and provide Migration guides.
Deprecation Lifecycle
flowchart LR
Active[Active] --> Deprecated[Deprecated]
Deprecated --> Sunset[Sunset Window]
Sunset --> Removed[Removed]
Active -.->|Critical Fix| Deprecated
Deprecated -.->|Client Request| Deprecated
Deprecation Header Middleware
# deprecation.py
from datetime import datetime, timedelta
from typing import Dict, Optional
class DeprecationMiddleware:
def __init__(self):
self.deprecated_versions: Dict[str, Dict] = {}
def deprecate(self, version: str, sunset_date: str,
migration_url: str, deprecation_date: str = None):
self.deprecated_versions[version] = {
"sunset": sunset_date,
"migration_url": migration_url,
"deprecation_date": deprecation_date or datetime.now().isoformat(),
}
def add_headers(self, version: str, status_code: int = 200) -> Dict:
info = self.deprecated_versions.get(version)
if not info:
return {}
headers = {
"Deprecation": info["deprecation_date"],
"Sunset": info["sunset"],
"Link": f'<{info["migration_url"]}>; rel="deprecation"',
}
sunset_dt = datetime.fromisoformat(info["sunset"])
days_left = (sunset_dt - datetime.now()).days
if days_left < 30:
headers["Warning"] = f'299 - "This version will be removed in {days_left} days. Migrate to the new version."'
return headers
mw = DeprecationMiddleware()
mw.deprecate("v1", sunset_date="2026-12-31", migration_url="/docs/migrate-v2")
responses = [
("v1", 200),
("v2", 200),
("v1", 200),
]
for version, status in responses:
headers = mw.add_headers(version)
if headers:
print(f"Version {version}: Deprecation={headers.get('Deprecation')}")
print(f" Sunset={headers.get('Sunset')}")
print(f" Link={headers.get('Link')}")
else:
print(f"Version {version}: No deprecation headers (current version)")
Expected output:
Version v1: Deprecation=<current-date>
Sunset=2026-12-31
Link=</docs/migrate-v2>; rel="deprecation"
Version v2: No deprecation headers (current version)
Version v1: Deprecation=<current-date>
Sunset=2026-12-31
Link=</docs/migrate-v2>; rel="deprecation"
Deprecation Logging and Metrics
# deprecation_logger.py
from datetime import datetime
from typing import Dict, Optional
class DeprecationLogger:
def __init__(self):
self.log: list = []
def record_call(self, version: str, endpoint: str, client_id: str):
self.log.append({
"version": version,
"endpoint": endpoint,
"client_id": client_id,
"timestamp": datetime.now().isoformat(),
})
def get_deprecated_usage(self, start_date: Optional[str] = None) -> Dict:
result = {}
for entry in self.log:
ver = entry["version"]
if ver not in result:
result[ver] = {"count": 0, "clients": set(), "endpoints": set()}
result[ver]["count"] += 1
result[ver]["clients"].add(entry["client_id"])
result[ver]["endpoints"].add(entry["endpoint"])
for ver in result:
result[ver]["clients"] = list(result[ver]["clients"])
result[ver]["endpoints"] = list(result[ver]["endpoints"])
return result
logger = DeprecationLogger()
logger.record_call("v1", "/users", "client-a")
logger.record_call("v1", "/users", "client-b")
logger.record_call("v2", "/users", "client-a")
usage = logger.get_deprecated_usage()
for ver, info in usage.items():
print(f"{ver}: {info['count']} calls, {info['clients']}")
Expected output:
v1: 2 calls, ['client-a', 'client-b']
v2: 1 calls, ['client-a']
Migration Guide Generator
# migration_guide.py
from typing import Dict, List
class MigrationGuide:
def __init__(self, old_version: str, new_version: str):
self.old_version = old_version
self.new_version = new_version
self.changes: List[Dict] = []
def add_change(self, endpoint: str, old: str, new: str, breaking: bool = False):
self.changes.append({
"endpoint": endpoint,
"old": old,
"new": new,
"breaking": breaking,
})
def generate(self) -> str:
lines = [f"# Migration Guide: {self.old_version} -> {self.new_version}"]
breaking = [c for c in self.changes if c["breaking"]]
additive = [c for c in self.changes if not c["breaking"]]
if breaking:
lines.append(f"\n## Breaking Changes ({len(breaking)})")
for c in breaking:
lines.append(f"### {c['endpoint']}")
lines.append(f"- Old: {c['old']}")
lines.append(f"- New: {c['new']}")
if additive:
lines.append(f"\n## Additive Changes ({len(additive)})")
for c in additive:
lines.append(f"- {c['endpoint']}: {c['new']}")
lines.append(f"\n## Timeline\n- {self.old_version} deprecated: now\n- {self.old_version} sunset: +6mo\n- {self.old_version} removed: +12mo")
return "\n".join(lines)
guide = MigrationGuide("v1", "v2")
guide.add_change("GET /users", "returns array", "returns object with data[] and meta", breaking=True)
guide.add_change("POST /users", "accepts name only", "accepts name, email (optional)", breaking=False)
guide.add_change("GET /users/{id}", "200", "200 with _version field", breaking=False)
print(guide.generate())
Expected output:
# Migration Guide: v1 -> v2
## Breaking Changes (1)
### GET /users
- Old: returns array
- New: returns object with data[] and meta
## Additive Changes (2)
- POST /users: accepts name, email (optional)
- GET /users/{id}: 200 with _version field
## Timeline
- v1 deprecated: now
- v1 sunset: +6mo
- v1 removed: +12mo
Common Mistakes
1. No Deprecation Warning
Removing an API version without any warning period or deprecation headers. Always give at least 6 months notice.
2. No Migration Path
Deprecating without providing a clear migration guide. Clients must know exactly what to change.
3. No Sunset Date
Vague deprecation like "will be removed eventually". Give a specific date for removal.
4. Deprecating Too Aggressively
Major version changes too frequently. Aim for 12-24 months between breaking changes for public APIs.
5. Not Monitoring Deprecated Usage
Without monitoring, you do not know which clients still use deprecated versions and when it is safe to remove them.
Practice Questions
1. What HTTP header indicates API deprecation?
The Deprecation header, which contains the date the version was deprecated.
2. How long should you support a deprecated version?
At minimum 6 months for public APIs. 12 months is better for enterprise APIs.
3. What is the Sunset header?
It indicates when the API version will be completely removed. Clients should migrate before this date.
4. How do you communicate deprecation to clients?
Deprecation headers, email notifications, dashboard warnings, API documentation notices, and migration guides.
Challenge
Create a full deprecation lifecycle manager that tracks all deprecated versions, monitors their usage, sends alerts to affected clients, and enforces sunset dates.
FAQ
Mini Project: Deprecation Manager
# deprecation_manager.py
from datetime import datetime, timedelta
class DeprecationManager:
def __init__(self):
self.versions = {}
def schedule_deprecation(self, version: str, notice_months: int = 6):
now = datetime.now()
self.versions[version] = {
"deprecated_at": now,
"sunset": now + timedelta(days=notice_months * 30),
"removed": False,
}
def check_status(self, version: str) -> str:
info = self.versions.get(version)
if not info:
return "not_deprecated"
if info["removed"]:
return "removed"
if datetime.now() > info["sunset"]:
info["removed"] = True
return "removed"
if datetime.now() > info["deprecated_at"] + timedelta(days=30):
return "ending_soon"
return "deprecated"
mgr = DeprecationManager()
mgr.schedule_deprecation("v1")
print(f"v1 status: {mgr.check_status('v1')}")
print(f"v2 status: {mgr.check_status('v2')}")
Expected output:
v1 status: deprecated
v2 status: not_deprecated
What's Next
You understand API deprecation. Next, learn about sunset headers, then explore versioning database schemas.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro