7 min read
On this page

Deprecation & Sunsetting

Deprecation is the announcement that something will go away. Sunsetting is the act of removing it. The gap between announcement and removal is where the hard work happens — migrating consumers, monitoring usage, and making judgment calls about customers who never migrate.

Every long-lived API accumulates deprecated endpoints. The discipline is not in creating new versions. It is in responsibly retiring old ones.

The Sunset Header

RFC 8594 defines the Sunset HTTP response header. It tells consumers exactly when an endpoint will stop working.

HTTP/1.1 200 OK
Sunset: Sat, 01 Jun 2025 00:00:00 GMT
Deprecation: true
Link: <https://api.example.com/v2/users>; rel="successor-version"

Three signals in one response: the endpoint is deprecated (Deprecation: true), it will stop working on June 1, 2025 (Sunset), and the replacement is at /v2/users (Link).

Consumers that check these headers can alert their developers automatically. Consumers that ignore them will discover the change when their integration breaks.

{
  "id": "user_123",
  "name": "Jane Doe",
  "email": "jane@example.com",
  "_deprecation": {
    "message": "This endpoint is deprecated. Use GET /v2/users/{id} instead.",
    "sunset_date": "2025-06-01",
    "migration_guide": "https://docs.example.com/migration/v1-to-v2"
  }
}

Some APIs include deprecation metadata in the response body as well. This is redundant with the headers but useful for consumers that log response bodies.

Documentation Practices

Mark Deprecated Endpoints Clearly

In your OpenAPI specification:

{
  "paths": {
    "/v1/users/{id}": {
      "get": {
        "summary": "Get user by ID",
        "deprecated": true,
        "description": "DEPRECATED: Use GET /v2/users/{id} instead. This endpoint will be removed on 2025-06-01.",
        "x-sunset-date": "2025-06-01"
      }
    }
  }
}

The deprecated: true flag in OpenAPI causes documentation generators (Swagger UI, Redoc) to visually strike through the endpoint. Developers see immediately that it is deprecated.

Changelog Entries

Every deprecation should appear in your changelog:

2024-06-01: Deprecated GET /v1/users/{id}
  - Replaced by GET /v2/users/{id}
  - Sunset date: 2025-06-01
  - Migration guide: https://docs.example.com/migration/v1-to-v2
  - Reason: v2 response format includes expanded relationship data

Email Notifications

For APIs with registered consumers, send email notifications at key milestones:

  • When deprecation is announced (12 months before sunset)
  • At 6 months before sunset
  • At 3 months before sunset
  • At 1 month before sunset
  • At 1 week before sunset

Stripe sends these notifications to the email address associated with each API key, with specifics about which deprecated features that account is actually using.

Migration Guides

A deprecation without a migration guide is a demand without a solution. Every deprecated endpoint needs a clear path forward.

Structure of a Migration Guide

A good migration guide answers four questions:

  1. What changed? — the specific differences between old and new
  2. Why did it change? — the motivation helps developers understand the direction
  3. How do I migrate? — step-by-step instructions with code examples
  4. What is the timeline? — when the old version stops working

Example: Migrating from v1 to v2

## Migrating GET /v1/users/{id} to GET /v2/users/{id}

### What Changed
- Response field `email_address` renamed to `email`
- Response field `full_name` split into `first_name` and `last_name`
- New required field `account_status` added to response

### Before (v1)
{
  "id": "user_123",
  "full_name": "Jane Doe",
  "email_address": "jane@example.com"
}
### After (v2)
{
  "id": "user_123",
  "first_name": "Jane",
  "last_name": "Doe",
  "email": "jane@example.com",
  "account_status": "active"
}
### Migration Steps
1. Update your response parsing to use `email` instead of `email_address`
2. Replace `full_name` references with `first_name` + " " + `last_name`
3. Handle the new `account_status` field in your data model
4. Update your API base URL from /v1/ to /v2/
5. Test with your staging environment

Stripe's migration guides are the gold standard. Each API version change includes a detailed guide with before/after examples, code snippets in multiple languages, and a list of affected endpoints.

Grace Periods

The minimum grace period between deprecation announcement and sunset depends on your consumer base:

Consumer Type Minimum Grace Period
Internal services 1-3 months
B2B partners 6-12 months
Public API consumers 12 months
Enterprise contracts Per contract terms

Stripe maintains API versions for years. Their oldest supported version dates back several years, and they provide tooling to test upgrades before committing. This generosity is strategic — it builds trust that makes developers choose Stripe over competitors.

Twilio similarly maintains deprecated endpoints for extended periods, with clear documentation about which endpoints are deprecated and when they will be removed.

Monitoring Deprecated Endpoint Usage

You cannot sunset what you cannot measure. Track these metrics for every deprecated endpoint:

Request Volume

deprecated_endpoint_requests{endpoint="/v1/users", method="GET"} 14523
deprecated_endpoint_requests{endpoint="/v1/users", method="POST"} 8901

Plot request volume over time. A declining curve means consumers are migrating. A flat line means they are not.

Unique Consumers

deprecated_endpoint_consumers{endpoint="/v1/users"} 47

Total request volume can be misleading. One consumer making 10,000 requests per day inflates the number. Track unique API keys or client IDs instead.

Consumer Identification

Know exactly who is still using deprecated endpoints:

Consumer: api_key_abc123 (Acme Corp)
  /v1/users GET: 5,200 requests/day
  Last seen: 2024-12-15
  
Consumer: api_key_def456 (Widget Inc)
  /v1/users GET: 120 requests/day
  Last seen: 2024-12-14

This lets you reach out to specific consumers with targeted migration assistance.

Alerting

Set up alerts for deprecated endpoint usage patterns:

  • Alert when a new consumer starts using a deprecated endpoint
  • Alert when usage increases instead of decreasing
  • Alert when sunset date is approaching and usage is still significant

The Hard Part: Customers Who Never Migrate

Some consumers will not migrate no matter how many emails you send. They have working code, no engineering bandwidth, and no incentive to change. This is the hardest problem in API deprecation.

Strategies

Direct outreach — for high-value consumers, assign a developer advocate to help them migrate. Provide custom migration scripts or pair-program the migration with them.

Automated migration — if the change is mechanical (field rename, URL change), consider providing a compatibility layer that translates old requests to new ones automatically.

GET /v1/users/123 → internally routes to GET /v2/users/123
Response: transform v2 response back to v1 format

This buys time but adds maintenance burden.

Hard cutoff with error messages — after the sunset date, return a clear error instead of silently failing:

{
  "error": {
    "type": "gone",
    "code": "endpoint_sunset",
    "message": "This endpoint was removed on 2025-06-01. Please use GET /v2/users/{id} instead.",
    "migration_guide": "https://docs.example.com/migration/v1-to-v2"
  }
}

Return HTTP 410 Gone, not 404 Not Found. 410 communicates that the resource existed but was deliberately removed.

Gradual degradation — instead of a hard cutoff, reduce availability gradually. Start returning 429 (rate limited) more aggressively. Slow down response times. This nudges consumers to migrate without causing a sudden outage.

Stripe's Versioning as a Case Study

Stripe's approach to deprecation is the industry benchmark:

  1. Account-pinned versions — each Stripe account is pinned to the API version that was current when the account was created. Changes only take effect when you explicitly upgrade.

  2. Version changelog — every API version change is documented with the exact date, what changed, and how to migrate. The changelog goes back years.

  3. Dashboard warnings — the Stripe dashboard shows warnings when your account uses a deprecated API version, with a one-click upgrade path.

  4. Test mode — you can test a version upgrade in test mode before applying it to production. This eliminates the fear of upgrading.

  5. Incremental upgrades — because versions are date-based, you can upgrade one version at a time. Going from 2023-01-01 to 2024-06-01 means applying each intermediate change in sequence, not a single massive migration.

  6. Rolling upgrades per endpoint — Stripe allows overriding the version on a per-request basis:

POST /v1/charges
Stripe-Version: 2024-06-01

This lets you upgrade one endpoint at a time rather than upgrading your entire integration simultaneously.

The result: Stripe has thousands of API consumers across millions of integrations, and version upgrades rarely cause production incidents. The engineering investment in their versioning system pays for itself in customer retention.

Common Pitfalls

  • No sunset date — marking something as deprecated without saying when it will be removed. Deprecated without a deadline means "we will maintain this forever."
  • Too short a grace period — giving consumers 30 days to migrate a critical integration. Enterprise consumers need months, not days.
  • Silent removal — removing an endpoint without prior deprecation. This breaks trust permanently.
  • No migration guide — telling consumers to move to v2 without explaining how. Every deprecation needs a migration path.
  • Ignoring monitoring data — sunsetting an endpoint that still serves significant traffic. Check the numbers before pulling the plug.
  • Deprecating without a replacement — removing functionality without providing an alternative. If the use case is valid, provide a new way to accomplish it.
  • One-size-fits-all timeline — applying the same grace period to a field rename and a complete endpoint redesign. Larger changes need longer timelines.

Key Takeaways

  • Use the Sunset HTTP header (RFC 8594) to communicate endpoint removal dates programmatically. Pair it with the Deprecation header and a Link to the replacement.
  • Migration guides are mandatory, not optional. Every deprecation needs step-by-step instructions with before/after code examples.
  • Minimum grace periods: 1-3 months for internal APIs, 6-12 months for partner APIs, 12 months for public APIs. Err on the side of longer.
  • Monitor deprecated endpoint usage by unique consumer, not just total request volume. Know exactly who needs to migrate and reach out directly.
  • Return HTTP 410 Gone after sunset, not 404 Not Found. Include the migration guide URL in the error response.
  • Stripe's date-based versioning with account pinning and per-request overrides is the gold standard for deprecation management. Study it before designing your own system.