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:
- What changed? — the specific differences between old and new
- Why did it change? — the motivation helps developers understand the direction
- How do I migrate? — step-by-step instructions with code examples
- 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:
-
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.
-
Version changelog — every API version change is documented with the exact date, what changed, and how to migrate. The changelog goes back years.
-
Dashboard warnings — the Stripe dashboard shows warnings when your account uses a deprecated API version, with a one-click upgrade path.
-
Test mode — you can test a version upgrade in test mode before applying it to production. This eliminates the fear of upgrading.
-
Incremental upgrades — because versions are date-based, you can upgrade one version at a time. Going from
2023-01-01to2024-06-01means applying each intermediate change in sequence, not a single massive migration. -
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.