Skip to content

By Raywake ·

The Sora API Has Shut Down: Where to Move Your Video Workloads

OpenAI's Sora API and Videos API shut down on September 24, 2026 with no replacement. Here's how to pick a new video model by workload and migrate your code.

A jaguar in a jungle, an example frame from a video workflow

On September 24, 2026, OpenAI removed the Videos API and every Sora 2 model from its API. If your product called sora-2 or sora-2-pro, those requests no longer work, and OpenAI's deprecations page lists no recommended replacement. The shutdown means applications need a new video backend and a reviewed request flow.

This guide covers what shut down, how to pick a new model for each kind of work you ran on Sora, and what the migration looks like in code.

What exactly shut down

OpenAI notified developers on March 24, 2026. On September 24, 2026, it removed:

  • the Videos API
  • sora-2 and sora-2-pro
  • the dated snapshots sora-2-2025-10-06, sora-2-2025-12-08 and sora-2-pro-2025-10-06

OpenAI's API deprecations page confirms the date, affected aliases and absence of a recommended replacement. This guide concerns the developer API; account exports and consumer app availability are separate.

Pick the replacement by workload, not by leaderboard

No single video model is best at everything. So the useful question isn't "what's the best Sora alternative?" but "what was I actually using Sora for?"

What you used Sora forWhere to startWhy
Turning a written scene into a clip with soundVeo 3.1 FastText to video, built around atmosphere, camera movement and sound
Animating an existing image (product shot, key visual, character)Kling 3 ProImage to video (plus text to video): keeps a strong still frame and adds directed motion
Short atmospheric or looping clips from a descriptionSeedance 2.5Text to video, with subject, action and mood set in the prompt

Two caveats. First, clip length, resolution and reference-image limits change often, so read each model's current input schema instead of trusting a blog table (including this one). Second, the only reliable test is your own prompt. Run the same shot on two or three models before you commit.

What migration looks like in code

Alternative platforms have their own request bodies and job lifecycle. Check both contracts before treating migration as a base-URL change. Plan to rewrite two things: how you build the request and how you wait for the result.

On Raywake every generation follows the same three steps, whatever the model:

  1. Quote: price the exact model and input. Nothing is charged.
  2. Generate: submit the same input with the quote_id and an Idempotency-Key.
  3. Job: read the status and output link (or wait inline with ?wait=true).
import os, time, uuid, requests

API = "https://api.raywake.com/v1"
H = {"Authorization": f"Bearer {os.environ['RAYWAKE_API_KEY']}"}

# Get the exact model ID and input schema from:
#   GET /v1/models?category=text-to-video   (or image-to-video)
model = "veo3.1/fast"
inp = {"prompt": "A jaguar slowly turns toward the camera in a lush jungle. Soft daylight, steady close camera."}

# 1. Price it first. Free.
payload = {"model": model, "input": inp}
response = requests.post(f"{API}/quotes", headers=H, json=payload, timeout=30)
response.raise_for_status()
quote = response.json()
print("Credits to reserve:", quote["credits"])

# 2. Start it. One idempotency key per intended job.
key = str(uuid.uuid4())
response = requests.post(f"{API}/generate", params={"wait": "true"},
                    headers={**H, "Idempotency-Key": key},
                    json={**payload, "quote_id": quote["quote_id"]}, timeout=200)
response.raise_for_status()
job = response.json()

# 3. Poll if it's still running (every 2-5 seconds).
TERMINAL = {"succeeded", "failed", "submission_unknown", "needs_review", "needs_reconciliation"}
for _ in range(120):
    if job["status"] in TERMINAL:
        break
    time.sleep(5)
    response = requests.get(f"{API}/jobs/{job['job_id']}", headers=H, timeout=30)
    response.raise_for_status()
    job = response.json()
else:
    raise TimeoutError(f"Continue polling job {job['job_id']}; do not submit a replacement")

if job["status"] == "succeeded":
    print(job["outputs"][0]["url"])   # signed for 7 days, download it
else:
    print(job["status"], job.get("error"))  # don't auto-resubmit

This example starts a paid generation when you run it with a funded key. Install requests, create a key with generate and jobs:read, and persist the payload, idempotency key and returned job ID in your application. If submission times out, reuse that saved key and body; restarting the script creates a new key.

Switching from Veo 3.1 Fast to Kling 3 Pro is a change of the model string and its input fields. The key, the balance and the flow stay the same.

Four things that bite during a migration

  • Not every "done" is a success. Besides succeeded and failed, a job can end as submission_unknown, needs_review or needs_reconciliation. Credits stay held in those cases, so don't submit a replacement job automatically.
  • A timeout isn't a failure. Video jobs take time. If a request times out, retry with the same Idempotency-Key, never a new one, so you don't pay twice.
  • Price before you run. Video is the expensive part of any creative pipeline. A quote gives you the credit amount to reserve before anything starts; usage-billed models settle within that upper bound, and a failed generation releases its reserved credits.
  • Download what you keep. Generated media is stored for 30 days and signed links expire. Copy outputs to your own storage.

A migration checklist

  1. Inventory your stored outputs and make sure important clips are in storage you control.
  2. List every place your code calls the Videos API, including background jobs and retries.
  3. Group those calls by workload: text to video, image to video, short loops.
  4. Run the same 3–5 real prompts on two or three candidate models and compare output and cost.
  5. Rewrite request building and polling. Add idempotency keys to every submit.
  6. Move output storage off temporary links.

Build a small migration test

Use one prompt that represents your real workload, rather than a dramatic scene selected for a showcase. Keep the aspect ratio, intended clip length and evaluation criteria fixed. Record the input, model ID, quote, job status and final output together so a later catalog change can be traced.

For a product shot, review whether the object stays the same through the last frame. For a speaking scene, review audio and visual timing. For a background loop, check the join between the final and first frames. A clip that looks attractive but misses the requirement is still a failed migration test.

Changing models may mean changing supported settings or rewriting the prompt. Keep a model-specific input adapter behind your application's common job interface. That lets your product present the same progress and download behavior while each adapter builds a request that its selected model actually accepts.

For image-led workloads, follow our image-to-video walkthrough. For retries and credit settlement, see AI video API costs.

Try it

Available models run in the Raywake studio and through the API on one balance, so you can test a prompt by hand in the studio and then call the same model from code.

Try video in the studio Read the API quickstart
← All articles