skip to content

Deprecation and Sunset

The lifecycle of retiring an API version: signalling deprecation in responses, setting a Sunset date, and actually turning it off without stranding clients. Interviewers ask because shipping v2 is easy — killing v1 responsibly is the real skill.

part ofAPI stylesoverview, primer and where to startread it →
on this pageshow

questions

3

An HTTP API version has been switched off permanently. What status code should its endpoints return — 404, 410 Gone, 301, or something else — and what should the response body contain?

level: juniorimportance: should knowfreq 42%

answer

  1. 410 = existed, deliberately gone
  2. 404 = looks like a typo
  3. 308 over 301: method preserved
  4. redirect only if truly equivalent
  5. never 500 — triggers client retries

basics

~20 s

Return 410 Gone: the resource existed and is intentionally, permanently removed. Include a machine-readable error body naming the version, the removal date and the replacement. Use 301/308 redirects only when the new endpoint is genuinely equivalent.

solid answer

~50 s

**410 Gone** is the right default for a retired version. It says the URL was valid and has been deliberately withdrawn, which is exactly what happened — unlike 404, which says "never heard of it" and sends developers hunting for a typo. The body matters more than the code. Return a structured error with a stable code (`api_version_removed`), the version called, its removal date, the current version, and a documentation link. Log and count these calls per client so you know who is still stranded. **Redirects** (301/308) are only honest when the new endpoint is truly equivalent — same semantics, same payload shape. If the replacement changed shape, a redirect delivers a response the client cannot parse, which is worse than a clear refusal. 308 preserves the method and body; 301 historically permits clients to rewrite POST to GET. Avoid 400 and 500: this is neither a malformed request nor a server fault.

go deeper

for a junior

Say 410 Gone and explain the difference from 404; mention including a helpful body.

for a middle

Compare 410, 404 and 308, and describe the structured error payload and its stable code.

for a senior

Cover why 500 and 200 are actively harmful, when redirects mislead, and keeping the 410 handler in place permanently with per-client metrics.

for a principal

Frame it as the tail of a governed lifecycle: notice, headers, telemetry-driven cutoff, and a permanent, documented terminal response.

## Why the code choice matters The caller is a piece of software whose owner may not be watching. The status code and body are your last chance to tell them what happened and what to do next — often the only diagnostic a developer will ever see. ## 410 Gone HTTP defines 410 as: the target resource is no longer available at the origin server and the condition is expected to be permanent. That is a deliberate retirement, stated precisely. It differentiates "we removed this on purpose" from "this URL is wrong", which is the single most useful piece of information for whoever is debugging. Caches may treat 410 as cacheable by default, which is fine for a permanent removal and slightly reduces useless traffic. ## 404 Not Found Not wrong, but weaker. It is what the client already gets for typos, so the shutdown is indistinguishable from a bad path. Some teams still choose it to avoid leaking that a version ever existed; for a public API that concern is usually moot because the old version is documented in changelogs anyway. Prefer 410, and if you must use 404, make the body unambiguous. ## Redirects **301 Moved Permanently** and **308 Permanent Redirect** are appropriate only when the replacement is behaviourally equivalent — for example the path moved from `/v1/users` to `/v2/users` with identical semantics. Use **308** for APIs: it forbids changing the method, whereas historic client behaviour with 301 rewrote POST to GET, which silently turns a create into a read. If the replacement changed field names, types or error shapes, do not redirect: the client will follow the hop and then fail to parse the body, producing a confusing bug far from its cause. A blunt 410 with instructions is kinder. ## The body Design it as a first-class error response, ideally in your existing error envelope or as `application/problem+json`: - a stable machine code, e.g. `api_version_removed` - the version requested and the date it was removed - the current supported version and the migration-guide URL - a support or contact channel Keep serving this response indefinitely, or at least for years. The cheapest possible endpoint is a static handler returning 410; there is no reason to ever make it a connection refusal. ## What not to do **400** blames the caller's syntax. **500** claims a server fault and will trigger the client's retry-with-backoff logic, generating pointless load and paging someone. **200 with an error body** is the worst option: monitoring dashboards stay green while every integration is silently broken. And do not simply drop the DNS record or close the port — timeouts give the client nothing to act on and look like an outage. ## Around the removal All of this is the endpoint of a process, not a substitute for one: advance notice, `Deprecation` and `Sunset` headers on the still-working version, and usage metrics showing traffic near zero should have preceded the day 410 starts being returned.

  • Why prefer 308 over 301 when redirecting a retired API path?
    308 requires the client to repeat the request with the same method and body, so a POST stays a POST. Historically many clients turned a 301-redirected POST into a GET, which silently converts a write into a read and loses the request body. For APIs that distinction is safety-critical.
  • Should the retired endpoint keep returning 410 forever, or eventually stop responding?
    Keep it. A static 410 handler costs essentially nothing and remains the clearest possible diagnostic for a straggler integration years later. Removing the route entirely yields 404s or connection errors that look like an outage and generate support tickets instead of self-service migration.

saying these in an interview costs you the question

  • Returning 500 for a retired version, which makes clients retry and pages your on-call
  • Returning 200 with an error body so dashboards stay green while integrations fail
  • Redirecting to a new version whose payload shape differs, causing parse failures downstream
  • Turning the endpoint off at DNS or firewall level so callers see timeouts with no explanation
  • Returning a bare status code with no body naming the version, date or replacement

context

open as a page

How do the HTTP `Deprecation` and `Sunset` response headers work, what do they carry, and how should a client and a provider each use them?

level: middleimportance: should knowfreq 40%

basics

~20 s

Sunset (RFC 8594) gives an HTTP-date after which the resource becomes unresponsive; Deprecation says it is already or will be deprecated. Pair them with Link rel="sunset" or rel="deprecation" to documentation. Clients should log and alert on them.

open as a page

You need to retire an old version of a public HTTP API that thousands of integrations still call. Walk through how you would run that deprecation from announcement to shutdown.

level: seniorimportance: should knowfreq 45%

basics

~20 s

Instrument usage per client first, announce with a dated timeline and migration guide, emit Deprecation and Sunset headers, chase the remaining callers by name, run short scheduled brownouts near the end, then shut off and return 410 Gone permanently.

open as a page