Before you start: decide it is worth it
Every breaking change transfers work from you to every consumer you have. Before committing, check whether an additive change would do: a new field alongside the old one, a new endpoint, or a new optional parameter. Additive changes are usually safe for clients that ignore unknown fields. See what counts as a breaking change.
If the change must break, decide how it will be versioned. A new URL version, a header version or a dated version each changes what consumers must do to opt in. See API versioning strategies. Write down the end state: which version is removed, on what date, and what the replacement is.
Announce early, through every channel
Consumers miss announcements constantly, so redundancy is the point:
- Publish a changelog entry and a dedicated migration guide.
- Email the technical contacts on each affected account, not only the account owner.
- Show a notice in the dashboard or developer console next to the affected resource.
- Post in any community channels your developers actually use.
Give a lead time proportional to the effort. A field rename may need weeks. Removing an entire API version used by businesses with release freezes may need many months. State the dates in the first announcement and do not move them earlier.
Signal it in the API itself
Announcements reach people. Headers reach code. Return machine-readable signals on every response that touches deprecated behavior:
Deprecation: @1767225600
Sunset: Wed, 30 Jun 2027 23:59:59 GMT
Link: <https://example.com/docs/migrate-v2>; rel="deprecation"The Sunset header is defined in RFC 8594 and carries the date after which the resource may stop working. The Deprecation header, standardised in RFC 9745, signals that the resource is deprecated and when that took effect. A Link with rel="deprecation" points at the migration documentation. See the Sunset header entry and how to deprecate an API.
Headers are cheap, but only useful if consumers read them, so tell people in the announcement that they exist and what they look like.
Write a changelog a machine could act on
Vague changelogs are the main reason migrations go wrong. "Improved the invoices response" forces every consumer to reverse engineer the change. A precise entry names, for each change:
- The exact endpoint, field or parameter affected, with old and new names.
- Old and new types, formats and units. If amounts move to minor units, say so, and say how currencies without a minor unit are handled.
- Old and new enum values, and whether clients must tolerate unknown values in future.
- Changed defaults: page size, sort order, included fields, time zones.
- Changed errors and status codes.
- The version or date from which the change applies.
Publish the before and after OpenAPI documents too. Consumers can then run an OpenAPI diff themselves, and you can check your own changelog against the diff before publishing it.
Ship the migration, not just the notice
The cheapest way to speed up adoption is to do part of the work for consumers:
- Release updated SDKs that support both versions during the transition, with the new behavior behind an explicit option.
- Provide codemods or upgrade scripts for common languages where the change is mechanical.
- Offer a test mode or sandbox that already serves the new version, so consumers can verify before switching.
- Include worked before and after examples for the tricky cases, not only the common one.
Each of these removes a step consumers would otherwise do by hand, under deadline, with less context than you have. See API migration for the consumer side of the process.
Run both, and measure who has moved
Serve the old and new behavior side by side for the whole deprecation window. Let consumers opt in per request or per account, so they can migrate one service at a time and roll back if they need to.
While both run, instrument usage of the deprecated path. Track, per consumer: requests on the old version, last seen date, and which endpoints they still call. That gives you a list of who to contact, rather than a guess. Contact the stragglers directly and early, with their own usage numbers.
Consider brownouts before final removal: short, announced windows where the old behavior returns errors. They surface forgotten integrations, such as monthly jobs and rarely used admin tools, while there is still time to fix them.
Remove it on the date you said
When usage has dropped to an acceptable level and the sunset date arrives, remove the old behavior. Return a clear error with a link to the migration guide rather than a generic 404 or 500, so the remaining callers can diagnose the cause quickly.
Keep the migration guide published after removal. Late adopters will still need it. Then write down what slowed adoption, and fix it before the next breaking change.
If you want to make the consumer side cheaper still, Releo turns a provider change into verified migration pull requests in each consumer repository, checked by replaying real traffic. See how it works.
Frequently asked questions
What is the difference between the Deprecation and Sunset headers?
The Deprecation header says a resource is deprecated and since when; it still works. The Sunset header, from RFC 8594, gives the date after which the resource is expected to stop working. Use both, plus a Link to migration docs.
How long should a deprecation period be for an API?
Long enough for your slowest important consumer to plan, build, verify and release a change. That ranges from weeks for small changes to many months for removing a whole version. Publish the date up front and never shorten it.
How do I know when it is safe to remove a deprecated API?
Measure it. Track calls to the deprecated path per consumer, contact anyone still using it, run announced brownouts to find forgotten integrations, and remove it on the published sunset date once usage is acceptably low.