Model Deprecation
Models do not stay in the catalog forever. When your platform operator decides to retire one, the platform tells you — in the portal, by email, and in the HTTP responses your own code already receives — and then keeps serving the model right up to the date it named.
This page is the canonical description of that process. It is the page the Link: …; rel="deprecation" response header points at.
The two dates
A retirement is made of two moments, and they are always published together:
| What it is | |
|---|---|
Announcement (deprecated_at) | The moment the model was declared to be going away. From here on, everything below starts happening. |
Retirement (sunset_at) | The moment it stops serving. Never earlier than the announcement. |
A model can be announced before a retirement date has been fixed. That is a normal, expected state: the model has been declared to be going away, no date is set yet, and nothing is refused. When that is the case, no countdown is shown anywhere and no email goes out — a date has to exist before anyone can be told one.
Every retirement also carries an answer for you: either a successor model to move to, or a short note from your operator explaining that there deliberately is none. One of the two is always present.
What happens, and when
While the model is announced but not yet retired
- It stays in your catalog and in
GET /v1/models. - It serves every request, exactly as before. Nothing is throttled, nothing is refused, nothing is billed differently.
- The portal marks it Retiring, with the date, the countdown and the successor. See Models → Retiring Models.
- Every response carries the deprecation headers below.
- You are emailed at up to five points: on announcement, then 30 days, 7 days and 1 day before the date, and once more on the retirement day itself.
From the retirement date onwards
- The model drops out of
GET /v1/models, at the same instant the inference endpoints start refusing it. You can never read a model out of the listing that the next request would reject. - Every request naming it is answered
410 Gone. - A realtime transcription WebSocket naming it is refused at admission.
The response headers
Deprecation travels in HTTP headers, never in the response body. The bodies on these endpoints are OpenAI-compatible schemas that clients parse strictly, and an unknown response header is ignored by every SDK — so adding this can never break a client that is not looking for it.
Deprecation: @1767225599
Sunset: Thu, 31 Dec 2026 23:59:59 GMT
Link: </models/deprecation>; rel="deprecation", </models/qwen3-8b-v2>; rel="successor-version"
| Header | Format |
|---|---|
Deprecation | RFC 9745. A structured-header Date: @ followed by unix seconds. Present whenever the model has been announced |
Sunset | RFC 8594. An HTTP-date. Present only once a retirement date has been fixed |
Link | One header field holding comma-separated link values. rel="deprecation" points at this page and is always present. rel="successor-version" names the successor and appears only when there is one |
The two timestamp formats are different on purpose and are easy to mistake for each other: Deprecation is a number of seconds, Sunset is a date string. Parse each in its own format rather than trying one and falling back.
The headers are written before the first byte of the body, which includes the first byte of a server-sent-event stream. Nothing is ever injected into the event stream itself, so a streaming client sees exactly the events it saw before — with three extra response headers in front of them.
These headers appear on every customer-facing surface that resolves a model:
POST /v1/chat/completions (streaming and not) · POST /v1/completions · POST /v1/embeddings · POST /v1/images/generations · POST /v1/audio/speech · POST /v1/audio/transcriptions · POST /v1/audio/translations · GET /v1/models/{model_id}
GET /v1/models carries the same information as fields on each model instead — see Retirement fields in the API.
The refusal
Once the retirement date has passed, the model answers 410 Gone, with the same Link headers attached so the successor is still machine-readable on the refusal:
{
"error": {
"message": "Model 'qwen3-8b' was retired on Thu, 31 Dec 2026 23:59:59 GMT and is no longer available. Use 'qwen3-8b-v2' instead.",
"code": "model_sunset"
}
}
Branch on code: "model_sunset"; the sentence in message is for a human reading a log. Where there is no successor, the operator's note takes the place of "Use … instead.".
410, not 404404 means "no such model", and sends a developer hunting for a typo that is not there. 410 means "it existed, it is gone, and here is what to use instead" — which is the true statement, and the one your error handling should treat as actionable rather than retryable. Retrying a 410 will never succeed.
A streaming request gets a real 410 — not a 200 that opens a stream and then emits an error event inside it.
404GET /v1/models/{model_id} for a private model your organization was never granted returns the ordinary not-found response, retired or not. A 410 would confirm the model exists, which is exactly what the not-found answer exists to avoid.
What you should do
- Read the headers. A
Deprecationheader on a response you are already receiving is the earliest, cheapest possible warning, and it needs no polling. Log it, or alert on it. - Read
replacement_model_namefromGET /v1/models, orrel="successor-version"from theLinkheader. It is the model name you would put inmodel— it is resolved for you, so there is no lookup to do. - Switch before the date. Migration is yours to schedule; the platform does not move traffic for you and does not extend the date on request.
- Do not hardcode the retirement date. A date can be moved — usually later — and the change is published on the same fields and the same headers. The announcement date does not move.
What deprecation is not
- It is not a pause, a throttle, or a price change. A retiring model serves identically until its date.
- It is not the same as a model being unavailable. A model that is temporarily not serving is reported through
is_serviceableand503, and it recovers on its own. See Availability. - It is not the same as the deployment status. A retiring model very often still reads Running, because that pill answers a different question: whether something is serving the model right now.