On 24 September 2026, OpenAI's Videos API stopped answering. The deprecations page records the announcement on 24 March — sora-2, sora-2-pro, every variant and the API itself — with no replacement listed1. Six months is a generous notice period. It is still six months in which every team that built on Sora had to find a different vendor, a different job API and a different set of prompts that work.
Most retirements are smaller: one model, a named successor, a date. They still fail the same way when nobody acts. Anthropic says it plainly: requests to models past the retirement date will fail2. This guide is the playbook for the months between the email and the date.
How the three largest vendors retire models
The vocabulary differs, and so do the promises.
OpenAI separates deprecation, the announcement, from shutdown, when the model is no longer accessible, and calls models that no longer receive updates legacy. It notifies customers by email and in its documentation, and gives at least six months' notice for generally available models, at least three months for specialized variants, and as little as two weeks for preview models1.
Anthropic moves models through active, legacy, deprecated and retired, notifies customers with active deployments, and gives at least 60 days' notice before retiring a publicly released model. Its dates apply to Anthropic-operated platforms; Amazon Bedrock and Google Cloud set their own schedules. It also offers a usage export by API key and model to find what still calls a deprecated model2.
Google defines a deprecation as the announcement that a model will be shut down, and says the shutdown dates in its table are the earliest possible dates, with notice given before the exact date3.
Published deprecation policies
Minimum notice, days
- OpenAI, generally available≥ 6 months
- OpenAI, specialized variants≥ 3 months
- Anthropic, public models≥ 60 days
- INFRO catalog entries≥ 30 days
- OpenAI, preview models≥ 2 weeks
What retired this year
- 2026-06-15Claude Sonnet 4, Opus 4 retiredAnnounced 2026-04-14.
- 2026-06-30Veo 3.0 shutdown dateReplacement: the Veo 3.1 previews.
- 2026-08-05Claude Opus 4.1 retiredAnnounced 2026-06-05; replacement claude-opus-4-8.
- 2026-08-17Imagen 4 shutdown dateReplacement: gemini-3.1-flash-image.
- 2026-09-24OpenAI Videos API and Sora 2 shut downAnnounced 2026-03-24; no replacement listed.
- 2026-10-01gpt-5.4-cyberReplacement: gpt-5.6-cyber.
- 2026-12-01gpt-image-1.5, gpt-image-1-miniReplacement: GPT Image 2.5.
- 2027-02-26whisper-1 and gpt-4o transcribe modelsReplacement: gpt-transcribe or gpt-live-transcribe.
Anthropic retired Claude Sonnet 4 and Opus 4 on 15 June 2026 and Opus 4.1 on 5 August2. Google's table lists Veo 3.0 for 30 June, Imagen 4 for 17 August and gemini-2.5-flash-image for 2 October3, and its Imagen page now says the models are shut down4. OpenAI shut down the Videos API on 24 September and has scheduled gpt-image-1.5 and gpt-image-1-mini for 1 December and whisper-1 with the gpt-4o transcription models for 26 February 20271. The Sora 2 model page now reads as discontinued5.
If you use several vendors, that is a retirement to manage most months.
What actually breaks
The model id is the obvious failure. Three less obvious ones cause most migration incidents:
- Parameters. Anthropic has deprecated
temperature,top_pandtop_kfor Claude Opus 4.7 and later: a non-default value returns a 400, and its Python SDK from v1.0 removes the parameters so passing them raises aTypeError2. Code that always sendstemperature: 0breaks on the replacement, not on the retiring model. - Behaviour. Output length, formatting, refusals, tool-call patterns and structured-output support shift between versions. Nothing errors; downstream parsers and budgets notice first.
- Whole APIs. When the Videos API went, the job protocol went with it1. Any replacement had a different request shape, not just a new model name.
The playbook
1 · Day 0
Inventory
Which keys, projects and routes call the model, and how much traffic each carries.
2 · Week 1
Candidate and eval
Run the replacement on recorded tasks; check parameters it refuses and features it lacks.
3 · Week 1
Put it behind a route
Applications call a route name; the model becomes configuration.
4 · Week 2
Canary by weight
Small weight first, compare errors, length, cost per task; raise in steps.
5 · Before the date
Block the old id
An allowlist stops new code from reaching the retiring model.
6 · After
Watch after cut-over
Served model, finish reasons, output length and estimated cost for two weeks.
1. Inventory from traffic, not from code
A code search misses model ids in configuration, environment variables, fallback lists, notebooks and scheduled jobs. Start from what was actually sent: requests by model, by API key and by project over the last 30 days. Anthropic's usage export does this per key for its own API2; a gateway's request log does it across every provider. Then keep a register of every model you use with its vendor status and date, and check it automatically.
"""Fail CI when configured models are near or past a vendor retirement date."""
import datetime as dt
import json
import sys
# Maintained by hand from the vendors' deprecation pages; review monthly.
REGISTER = {
"claude-opus-4-1-20250805": {"retires": "2026-08-05", "replacement": "claude-opus-4-8"},
"gpt-image-1.5": {"retires": "2026-12-01", "replacement": "gpt-image-2.5-sunburst"},
"whisper-1": {"retires": "2027-02-26", "replacement": "gpt-transcribe"},
}
WARN_DAYS = 60
def check(config_path: str, today: dt.date) -> int:
with open(config_path) as f:
models = set(json.load(f)["models"]) # every model id your services may call
failures = 0
for model in sorted(models & REGISTER.keys()):
entry = REGISTER[model]
days_left = (dt.date.fromisoformat(entry["retires"]) - today).days
if days_left <= 0:
print(f"ERROR {model} retired {-days_left} days ago -> {entry['replacement']}")
failures += 1
elif days_left <= WARN_DAYS:
print(f"WARN {model} retires in {days_left} days -> {entry['replacement']}")
return failures
if __name__ == "__main__":
sys.exit(1 if check(sys.argv[1], dt.date.today()) else 0)2. Qualify the replacement on recorded work
Take the vendor's recommended replacement as the first candidate, not the only one. Run it on a few hundred recorded requests from each workload the inventory found, with the exact request shape your code sends, including sampling parameters and tools. Compare task success, refusals, output tokens and truncation, and compute cost per successful task at your provider's rate. A replacement with a higher list rate can cost less per task if it needs fewer retries; one with a lower list rate can cost more if its answers run longer.
3. Put the model behind a name you control
If services call a vendor model id directly, every retirement is a multi-repository change. Point them at a route or alias you own, so the model becomes configuration. Do this before the migration, not during it.
4. Move traffic by weight
Stage 0
Route before: old model weight 100
Stage 1
Replacement weight 5, old 95
Stage 2
Replacement 50, old 50
Stage 3
Replacement 100, old model removed
Hold each stage long enough to see the rarer paths — tool calls, long inputs, the weekly batch job — and compare the replacement against the old model on the same metrics you used to qualify it. Keep the retiring model off the route's fallback list once you are past the first stage: a fallback to a model that is about to disappear protects nothing.
5. Close the door on the old id
Before the shutdown date, stop new code from calling the retiring model at all. An allowlist that no longer includes it turns a future outage into an immediate, explicit refusal that shows up in development.
6. Watch what serves after cut-over
For two weeks after the switch, watch output tokens per request, truncation, refusals, fallback rate and estimated cost, and read the model id the provider reports on each response. Aliases and snapshots mean the name you send is not always the model that answers.
// Log when the model that served differs from the one this service was qualified on.
const QUALIFIED = new Set(["anthropic/claude-sonnet-5.5"]);
export function checkServedModel(response: { id: string; model: string }, route: string) {
if (!QUALIFIED.has(response.model)) {
console.warn(JSON.stringify({ event: "unqualified_model_served", route, served: response.model, request: response.id }));
}
}Running a migration with INFRO routes
In INFRO the application calls a route — support-agent, nightly-summary — that resolves to an ordered list of targets, each a model and one of your own provider connections. A weighted route picks the first target in proportion to its weight and falls through the rest if it fails; a target at weight 0 is a standby that never takes a first attempt. Targets are saved as a whole list in one write, so production never passes through an intermediate state with two primaries or none. That is the canary above, done in the console with no deploy.
A route may not reach a model your model allowlist forbids: the target is skipped, and a direct call to a disallowed model is refused with 403 model_not_allowed before any provider is called. Removing the retiring model from the allowlist is step 5. An API key can also be pinned to named routes, so it cannot call a model id directly at all.
For the inventory, the request log records the model, provider, connection and key of every request, and each row carries served_model — the model id your provider's reply named — which GET /v1/requests filters on. Traffic changes raise a traffic.model_changed finding when a model on one connection starts being answered by an id that connection had not reported in the previous 14 days, with output tokens, truncation and estimated provider cost before and after. It reads metadata only, so it tells you that the served model changed and what it costs, not whether answers got worse.
INFRO's own catalog has a lifecycle too. Catalog entries are deprecated with at least 30 days' notice, announced by email and in the console, and a retired catalog id becomes an alias for its designated successor, so old code does not start returning 404 from INFRO. That alias is a catalog convenience, not a migration: the request is then served and estimated as the successor, which your connections must serve and which you have not necessarily evaluated. The response model field always reports the model that served; log it. See models. Whether a model answers at all is decided by the provider whose account you connect.
