Guide

How to deprecate an API endpoint safely

Last checked 2026-11-097 min read

To deprecate an API endpoint safely is to retire it in stages, so that every caller has moved before it stops answering: name who owns the retirement, find every consumer team, tell them with a date and a replacement, mark the endpoint deprecated in the spec and in its responses, check the traffic until it reaches zero, and only then remove it. Between teams in one company, the hard step is the second one. Public-API advice assumes you know your callers by their API keys; internally you often do not. Graphwake answers that step: it keeps the consumer teams of every endpoint and alerts them when the endpoint is removed.

ExamplePull request #1284 · orders-api · not merged
79/100Highbefore the merge
3 breaking changes
10 each, up to 30
30
3 consumer teams affected
12 each, up to 36
36
13 people notified
1 each, up to 14
13
Blind spots
10 each, up to 20
0
Low0-29Medium30-59High60+
Four public rules add up to the score, so a reviewer can check every point of it.
On this page
  1. Deprecate, sunset, remove: three different moments
  2. The checklist
  3. Deprecation and Sunset headers
  4. A notice you can copy
  5. Where Graphwake fits: who is affected
  6. When traffic data alone is enough
  7. Questions

Deprecate, sunset, remove: three different moments

MomentWhat it meansDoes the endpoint still work?
DeprecatedCallers should stop using it and move to the replacementYes. RFC 9745 says deprecation does not change its behaviour
SunsetThe date after which it is expected to stop answeringUntil that date, as a hint, not a promise (RFC 8594)
RemovedThe route is gone from the code and the specNo: callers get 404 or 410 Gone

Keep the three apart in your notice. “Deprecated” without a sunset date reads as “someday”, and teams rightly put it at the bottom of the backlog.

The checklist

  1. Name the owner of the retirement

    One team decides the dates, answers questions and does the removal. Usually it is the team that owns the endpoint. If nobody owns it, settle that first: an endpoint with no owner has nobody to approve the removal or to call when something breaks.

  2. Find every consumer team

    Start from what is written down (a dependency map, catalog entries, earlier notices), then search the code and the access logs for callers the list misses. How to find which teams use an endpoint walks through each search. The output is a list of teams, each with a person who can answer.

  3. Have the replacement ready first

    Ship the new endpoint, document the mapping from old to new, and make sure it covers what callers use today. A deprecation notice that says “a replacement is coming” starts a second round of notices later.

  4. Tell the consumer teams, with dates

    Send the notice to those teams in the channels they read, not to a company-wide list. Give a deprecation date, a sunset date, the replacement and a reply deadline. Track the replies per team: migrated, in progress, or not ours. A template is below.

  5. Mark it in the spec and in the responses

    Set deprecated: true on the operation in the OpenAPI spec and name the replacement in its description. Add the Deprecation and Sunset response headers, with a Link to the migration notes. They help clients that log them; they do not replace the notice.

  6. Watch the traffic until it reaches zero

    Count calls to the endpoint per caller, every week. Chase the callers that are not on your list: they are the teams the notice did not reach. Wait a full business cycle at zero, including a month end, so a monthly job has had its chance to call.

  7. Remove it, and answer with 410 for a while

    Remove the route from the code and the operation from the spec in the same change. If you can, answer the old path with 410 Gone for a few weeks, so a missed caller gets a clear error instead of a confusing 404.

Deprecation and Sunset headers

Two HTTP response headers tell a client, in the response itself, that an endpoint is on its way out. Both are optional for the client: a client that does not read them works exactly as before, so they reach only the callers whose code or logs look for them.

text
HTTP/1.1 200 OK
Deprecation: @1798761599
Sunset: Wed, 31 Mar 2027 23:59:59 GMT
Link: <https://docs.example.com/orders/invoice-v2>; rel="deprecation"; type="text/html"
Example: a response from the deprecated endpoint. The URL and dates are made up.
  • `Deprecation` (RFC 9745, Standards Track, March 2025): a structured-field date, written as @ and Unix seconds. @1798761599 is 31 December 2026, 23:59:59 UTC. The date may be in the past. The endpoint keeps working.
  • `Sunset` (RFC 8594, Informational, May 2019): an HTTP-date after which the endpoint is expected to stop answering. The RFC calls it a hint: the date can move, and the endpoint may go earlier or later.
  • Order: RFC 9745 says the Sunset date must not be earlier than the Deprecation date. The two use different date formats for historical reasons.
  • `Link` with `rel="deprecation"` points to a page for people: what replaces the endpoint and how to move. RFC 8594 defines rel="sunset" for a page about the retirement policy.
yaml
paths:
  /orders/{id}/invoice:
    get:
      deprecated: true
      description: >-
        Deprecated. Use GET /invoices/{id} instead.
        Removal planned for 31 March 2027.
Example: the same endpoint marked deprecated in an OpenAPI spec.

In OpenAPI, deprecated: true on an operation says consumers should stop using it. It changes nothing at runtime: the endpoint answers as before, and the flag reaches only people and tools that read the spec.

A notice you can copy

text
Subject: GET /orders/{id}/invoice will be removed on 31 March 2027

Your team is listed as a consumer of GET /orders/{id}/invoice (orders-api).

What changes:  the endpoint will return 410 Gone after 31 March 2027.
What to use:   GET /invoices/{id}. Migration notes: <link>
Why:           invoices moved to billing-api.
What we need:  reply "migrated", "in progress (date)" or "not ours" by 15 January 2027.
Questions:     #orders-api, or the owner, <team>.
Example: a retirement notice to one consumer team. Fill in your own endpoint, dates and links.

One notice per team, with a reply deadline, beats one announcement for everyone. A team that replies “not ours” is useful too: it takes a name off the list, or tells you the list was wrong.

See who your next breaking change reaches.Paste a spec URL or add one CI step. 7-day free trial. Cancel before day 7 and you pay nothing.
Start free trial

Where Graphwake fits: who is affected

Graphwake answers step 2 and shows the reach of the final step. It keeps which teams subscribe to each endpoint, read from your OpenAPI specs and the teams’ own subscriptions, and alerts those teams on a breaking change to the endpoint, and always when it is removed.

  • The consumer list is already there

    Each endpoint shows its owner and the teams that subscribe to it. Start the retirement notice from that list instead of a search; an endpoint no team subscribes to is flagged as an orphan.

  • Removal is always breaking

    Removing an endpoint from the spec is always a breaking change in Graphwake. It alerts the subscribed teams in Slack, Microsoft Teams or email. Through the CI step, that happens while the removal is still a pull request.

  • The removal gets an impact score

    A removed endpoint counts as a breaking change and as a blind spot, and each subscribed team adds to the impact score. The reviewer sees how far the removal reaches before the merge.

  • A move in one spec update is flagged

    When the old endpoint is removed and its replacement added in the same spec update, the alert says the endpoint may have been moved and lists the endpoints added with it, and Discovery suggests the replacement to the teams that subscribed to the old one. Someone confirms before it becomes a subscription.

To keep the consumer list itself current, see the team-level API dependency map. For what happens when the removal reaches the subscribed teams, see API breaking change alerts.

Good to know

When traffic data alone is enough

  • You need proof that nobody calls the endpoint. Gateway analytics, access logs or traces over a long enough window show that.
  • Your callers are external customers with API keys. Gateway analytics already list each caller and its volume.

Questions

What is the difference between deprecating and sunsetting an API endpoint?

Deprecating means callers should move off it, but it still works. Sunsetting is the date after which it is expected to stop answering. Removal is when the route is actually gone.

Which HTTP headers mark an endpoint as deprecated?

Deprecation (RFC 9745), with a date written as @ and Unix seconds, and Sunset (RFC 8594), with an HTTP-date for the expected end. Add a Link header with rel="deprecation" that points to the migration notes.

How long should a deprecation period be for an internal API?

Long enough for every consumer team to plan the work into a sprint and ship it, and for a full business cycle at zero traffic afterwards. Set it with the consumer teams’ replies in hand, not before.

How do I know it is safe to remove the endpoint?

Every listed consumer team has said it migrated, and the traffic to the endpoint has stayed at zero for a full business cycle, including a month end. The list alone is not enough: only traffic shows callers nobody listed.

Where does Graphwake fit when I retire an endpoint?

It answers who is affected: the teams subscribed to the endpoint. When the endpoint is removed from the spec, it alerts those teams and scores the removal.

Pick an endpoint. See who it reaches. 7-day free trial. Cancel before day 7 and you pay nothing.

Start free trial

Sources and last verified

Sources checked on 2026-11-09.