Deprecation and Sunset Headers — Programmatic API Version Communication
In this tutorial, you'll learn about Deprecation Headers. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
HTTP deprecation headers provide a standardized way to inform API consumers that a version, endpoint, or field is deprecated and will be removed in the future.
What You'll Learn
By the end of this lesson, you will implement Deprecation and Sunset headers, communicate deprecation timelines, notify consumers programmatically, and track Migration progress.
Why It Matters
Without deprecation headers, consumers discover API changes when their code breaks. Headers provide early, programmatic notification of upcoming changes.
Real-World Use
Durga Antivirus Pro adds Deprecation and Sunset headers to all v1 and v2 responses, giving consumers automated warning 6 months before each version is removed.
Deprecation Header Flow
sequenceDiagram
Consumer->>API: GET /api/v1/scan
API->>Consumer: 200 + Deprecation: true
API->>Consumer: Sunset: Sat, 31 Dec 2026
Consumer->>Consumer: Log deprecation warning
Consumer->>API: GET /api/v2/scan (migrated)
API->>Consumer: 200 (no deprecation headers)
Deprecation Header Middleware
Add deprecation headers to API responses.
from typing import Dict, Set, Optional, List
from datetime import datetime, timedelta
import time
class DeprecationMiddleware:
def __init__(self):
self.deprecations: Dict[str, Dict] = {}
def deprecate_endpoint(self, path: str,
sunset_date: datetime,
replacement: str = "",
deprecation_date: Optional[datetime] = None):
self.deprecations[path] = {
"sunset": sunset_date,
"replacement": replacement,
"deprecated_since": deprecation_date
or datetime.utcnow(),
}
def add_headers(self, path: str,
headers: Dict) -> Dict:
dep = self._find_matching_deprecation(path)
if dep:
headers["Deprecation"] = "true"
headers["Sunset"] = dep["sunset"].strftime(
"%a, %d %b %Y %H:%M:%S GMT"
)
if dep["replacement"]:
headers["Link"] = (
f'<{dep["replacement"]}>; rel="successor-version"'
)
return headers
def _find_matching_deprecation(self,
path: str
) -> Optional[Dict]:
for pattern, dep in self.deprecations.items():
if path.startswith(pattern):
return dep
return None
def days_until_sunset(self, path: str
) -> Optional[int]:
dep = self._find_matching_deprecation(path)
if dep:
delta = dep["sunset"] - datetime.utcnow()
return max(0, delta.days)
return None
def get_all_deprecations(self) -> List[Dict]:
return [
{
"path": path,
"sunset": dep["sunset"].isoformat(),
"replacement": dep["replacement"],
"days_remaining": max(0, (
dep["sunset"] - datetime.utcnow()
).days)
}
for path, dep in self.deprecations.items()
]
mw = DeprecationMiddleware()
mw.deprecate_endpoint(
"/api/v1", datetime(2026, 12, 31),
"/api/v2"
)
headers = mw.add_headers("/api/v1/scan", {})
print(f"Deprecation headers: {headers}")
print(f"Days until sunset: {mw.days_until_sunset('/api/v1/scan')}")
Consumer Notification System
Track which consumers are using deprecated endpoints and notify them.
from typing import Dict, Set, Optional, List
from datetime import datetime
from collections import defaultdict
class ConsumerDeprecationTracker:
def __init__(self):
self.consumer_usage: Dict[str, Dict[str, datetime]] = \
defaultdict(dict)
def record_request(self, consumer_id: str,
endpoint: str):
self.consumer_usage[consumer_id][endpoint] = \
datetime.utcnow()
def get_consumers_on_endpoint(
self, endpoint: str
) -> List[str]:
return [
cid for cid, endpoints
in self.consumer_usage.items()
if endpoint in endpoints
]
def get_consumers_on_deprecated(
self, deprecated_endpoints: Set[str]
) -> Dict[str, List[str]]:
result = defaultdict(list)
for cid, endpoints in self.consumer_usage.items():
for ep in endpoints:
for dep in deprecated_endpoints:
if ep.startswith(dep):
result[cid].append(ep)
return dict(result)
def get_migration_status(
self, consumer_id: str,
old_endpoint: str,
new_endpoint: str
) -> str:
usage = self.consumer_usage.get(consumer_id, {})
uses_old = old_endpoint in usage
uses_new = any(
e.startswith(new_endpoint)
for e in usage
)
if uses_new and not uses_old:
return "migrated"
if uses_old and uses_new:
return "in_progress"
if uses_old:
return "not_migrated"
return "unknown"
tracker = ConsumerDeprecationTracker()
tracker.record_request("partner-1", "/api/v1/scan")
tracker.record_request("partner-1", "/api/v2/scan")
status = tracker.get_migration_status(
"partner-1", "/api/v1", "/api/v2"
)
print(f"Migration status: {status}")
Automated Migration Reminder
Send automated reminders to consumers using deprecated endpoints.
from typing import Dict, List, Optional
from datetime import datetime, timedelta
class MigrationReminder:
def __init__(self):
self.reminders: List[Dict] = []
self.notified: set = set()
def check_and_notify(self, consumer_id: str,
endpoint: str,
days_remaining: int) -> Optional[str]:
key = f"{consumer_id}:{endpoint}"
if key in self.notified:
return None
if days_remaining <= 0:
return None
message = None
if days_remaining <= 30:
message = (
f"URGENT: Endpoint {endpoint} will be "
f"removed in {days_remaining} days. "
f"Immediate migration required."
)
elif days_remaining <= 90:
message = (
f"REMINDER: Endpoint {endpoint} will be "
f"removed in {days_remaining} days. "
f"Please plan migration."
)
elif days_remaining <= 180:
message = (
f"NOTICE: Endpoint {endpoint} deprecated. "
f"{days_remaining} days until removal."
)
if message:
self.reminders.append({
"consumer": consumer_id,
"endpoint": endpoint,
"message": message,
"sent_at": datetime.utcnow(),
})
self.notified.add(key)
return message
reminder = MigrationReminder()
msg = reminder.check_and_notify(
"partner-1", "/api/v1/scan", 30
)
print(f"Reminder: {msg}")
msg2 = reminder.check_and_notify(
"partner-1", "/api/v1/scan", 30
)
print(f"Duplicate: {msg2}")
Common Mistakes
Mistake 1: Not Including Sunset Header
Deprecation without a sunset date does not tell consumers when they need to migrate.
Mistake 2: Too Short Notice
Less than 90 days notice is too short for enterprise consumers with change management processes.
Mistake 3: No Link to Replacement
Consumers need to know what to migrate to. Include a Link header with rel="successor-version".
Mistake 4: Deprecating Without Monitoring
Track which consumers receive deprecation headers and follow up with those who have not migrated.
Mistake 5: Inconsistent Headers
Add deprecation headers to every response from a deprecated version, not just specific endpoints.
Practice Questions
- What is the purpose of the Deprecation HTTP header?
- What is the Sunset HTTP header and what format does it use?
- How does the Link header indicate the replacement version?
- How do you track consumer migration progress?
- What is a reasonable deprecation timeline?
Challenge
Build a deprecation header system that adds Deprecation: true and Sunset headers to all responses from deprecated versions, includes Link headers pointing to the replacement, tracks which consumers see deprecation warnings, and sends automated reminders at 90, 60, and 30 days before sunset.
FAQ
Mini Project
Build a deprecation header system that adds Deprecation: true and Sunset headers to deprecated API responses, includes Link headers to replacement versions, tracks consumer requests to deprecated endpoints, sends automated email reminders at 90, 60, and 30 days before sunset, and reports migration completion status.
What's Next
Learn about Migration Guides for consumer transition support, or explore Breaking Changes for change management strategies.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro