Skip to content

Deprecation and Sunset Headers — Programmatic API Version Communication

DodaTech Updated 2026-06-28 5 min read

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.

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

  1. What is the purpose of the Deprecation HTTP header?
  2. What is the Sunset HTTP header and what format does it use?
  3. How does the Link header indicate the replacement version?
  4. How do you track consumer migration progress?
  5. 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

What is the Deprecation HTTP header?

The Deprecation header indicates that the API or endpoint is deprecated. The value can be true or a date string indicating when deprecation started.

What is the Sunset HTTP header?

The Sunset header indicates when a deprecated API will be removed. It uses the HTTP-date format, e.g., 'Sat, 31 Dec 2026 23:59:59 GMT'.

How should the Sunset date be formatted?

Use the standard HTTP-date format: 'Day, DD Mon YYYY HH:MM:SS GMT'. This is the same format used by Expires and Last-Modified headers.

Should deprecation headers be on all endpoints or just version root?

Add them to all responses from a deprecated version. Consumers may not hit the version root endpoint, but they will hit individual API endpoints.

What is the Link header rel successor-version?

The Link header with rel='successor-version' points to the replacement API version, giving consumers a direct link to migrate to.

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