Model Deprecations
GET https://api.aimlapi.com/v1/models/deprecations
GET /v1/models/deprecations lists the models that are retiring, have already been withdrawn, or were folded into another model — with the date it happened and the id to move to.
No API key is required for this request. You can also open the feed directly in a browser.
It exists to answer the one question GET /v1/models structurally cannot. A model missing from the catalogue may have been withdrawn, may have been folded into another id that still works, or may never have existed — and the catalogue returns the same "not here" for all three. Treating them alike is how a model-sync job ends up disabling models that are still serving.
Get the deprecation feed
Returns the deprecation entries matching the given filters, newest first.
Keep only entries in these states: deprecated, superseded or withdrawn. Values are case-insensitive and may be comma-separated (?status=superseded,withdrawn) or repeated (?status=superseded&status=withdrawn). Unknown values are ignored rather than rejected — a parameter holding no recognised value filters nothing and returns the whole feed.
withdrawnKeep only entries whose deprecated_at or shutdown_at falls on or after this date. Plain YYYY-MM-DD, no time part. Entries carrying neither date are dropped, because there is nothing to compare against. A malformed value is ignored rather than rejected, so a broken poll returns the whole feed instead of a 400.
2026-08-01The deprecation feed, newest first.
Always list.
listWhen this response was built, in UTC. It moves on every call and is deliberately excluded from the ETag.
2026-08-24T13:59:21.643ZNot Modified — the ETag you sent as If-None-Match still matches. Empty body.
curl -L \
--url 'https://api.aimlapi.com/v1/models/deprecations?status=withdrawn&since=2026-08-01'{
"object": "list",
"generated_at": "2026-08-24T13:59:21.643Z",
"data": [
{
"id": "openai/gpt-5-codex",
"aliases": [
"gpt-5-codex"
],
"status": "withdrawn",
"deprecated_at": "2026-07-30",
"shutdown_at": "2026-07-30",
"replaced_by": "deepgram/aura-2",
"reason": "provider_delisted"
}
]
}The three states
status is the field to branch on, and the difference between the three is what your integration should do next.
status
Does the id still work?
What to do
deprecated
Yes — still serving, retirement announced
Plan the migration before shutdown_at. Nothing breaks today.
superseded
Yes — folded into another model, still resolves
Nothing is broken. Switch to replaced_by when convenient.
withdrawn
No — requests return 404
Switch to replaced_by, or pick a replacement from the catalogue.
superseded is not a soft withdrawn. The id still resolves — it lives on as an alias of replaced_by — and shutdown_at is always null for these entries. Conflating the two is exactly the false positive this feed exists to prevent.
A deprecated model is read from its live model card, so it announces its own sunset while it still works. Once a real retirement date is set, it is dropped from GET /v1/models — but it keeps serving, and GET /model/{id} still returns its card with the sunset dates attached. The other two states come from a durable record instead: a withdrawn model has no card left to read.
Response shape
Dates are plain YYYY-MM-DD. deprecated_at is the date the provider announced the retirement, or the date we learned of it when they gave no notice; shutdown_at is the date it stopped, or will stop, serving. reason is provider_delisted, consolidated or unknown — and null on an entry read from a live model card.
Entries are ordered newest-first by the most recent date they carry, then by id. A model published under several endpoint types appears once: the sunset belongs to the model, not to the route.
Match your integrated model name against id and aliases together, never id alone. The name you originally integrated against is often the alias rather than the canonical id, and an id-only comparison will miss the entry that explains what happened to it.
Filtering
Both filters are optional, and both fail open: an unrecognised value is ignored rather than rejected. This is a discovery surface, and a typo in a polling job should not turn every poll into a 400.
status— case-insensitive, comma-separated (?status=superseded,withdrawn) or repeated (?status=superseded&status=withdrawn). A parameter holding no recognised value filters nothing and returns the whole feed.since— plainYYYY-MM-DD, no time part. Keeps entries whosedeprecated_atorshutdown_atfalls on or after that date. Entries carrying neither date are dropped, because there is nothing to compare against.
Polling and caching
Responses carry a weak ETag and Cache-Control: public, max-age=300. Send the tag back as If-None-Match and you get 304 Not Modified with an empty body when nothing changed:
Keeping an integration in sync
The feed and the catalogue answer different halves of the same question, so read both. The catalogue says what you can use today; the feed explains anything the catalogue no longer lists.
The last branch is the one worth keeping separate. A name in neither list was never a model id here, which is a different problem from a model that went away — and it is the case a catalogue-only check silently reports as a deprecation.
Last updated
Was this helpful?