Published September 26, 2026

Sora API Alternative: Migrating Your Video Pipeline After the September 24 Shutdown

· 9 min read · For developers who called POST /v1/videos

Direct answer: The Sora API is gone. OpenAI removed the Videos API and the sora-2, sora-2-pro and dated snapshot models on September 24, 2026, and its deprecations page lists no recommended replacement. If your product called POST /v1/videos, the closest like-for-like replacement you can switch to today is the ZSky video API: the same asynchronous pattern (create a job, poll it, download the file), text-to-video and image-to-video, synchronized audio on every clip, clips from 1 to 10 seconds at up to 2560 px on a side, and a monthly spending cap you set instead of an open-ended per-second meter. It is included with the Max plan ($99 a month, or $79 a month billed annually) with a monthly allowance of videos and images; usage beyond it is pay-per-use up to your cap. The field mapping and a working Python port are below.
By Cemhan Biricik 2026-09-26 Developers

What replaced the Sora API?

Nothing from OpenAI. The Videos API and the sora-2 models were removed on September 24, 2026, and OpenAI's deprecations page leaves the recommended-replacement column empty, so every team that built on it picks its own successor. For developers who want the same create, poll and download job shape rather than a new architecture, the ZSky video API is the closest match available today: text-to-video and image-to-video, synchronized audio on every clip, clips of 1 to 10 seconds at up to 2560 px, and a monthly spending cap you set. Teams that need 12-second clips, completion webhooks or Sora's remix endpoints will not find them here; the rest of the migration is a handful of renamed fields, mapped below.

What OpenAI removed on September 24

On March 24, 2026, OpenAI told developers using the Videos API that the API and every Sora 2 model alias and snapshot would be removed on September 24, 2026. The consumer Sora app and website closed earlier, on April 26. The OpenAI deprecations page lists the removals as:

The "recommended replacement" column for every one of those rows is empty. This is not a version bump. There is no Sora 3 model ID to swap in, no grace period, and no migration guide from OpenAI. Any request that names a sora-2 model now fails, and if your application stored Sora video IDs rather than the downloaded files, those downloads are no longer reachable through the API. You choose the replacement, and you port the calls.

What a replacement has to give you

Most Sora integrations are small. They submit a prompt, poll a job until it finishes, download an MP4, and store or serve it. A replacement needs to cover the same surface without a regression your users will notice:

The ZSky video API covers each of those. The table below puts the two APIs side by side so you can check it against your own code.

Sora 2 API vs ZSky video API

ItemSora 2 API (removed)ZSky video API
Status todayRemoved September 24, 2026Live
Create a videoPOST https://api.openai.com/v1/videosPOST https://zsky.ai/api/v1/videos/generate
AuthenticationAuthorization: BearerX-API-Key header
Model selectionmodel: sora-2 or sora-2-proNone. One pipeline, no model parameter.
Promptpromptprompt, up to 2,000 characters
Durationseconds: "4", "8" or "12" (a string)duration: any integer 1 to 10 seconds (default 5)
Resolutionsize: 1280x720 or 720x1280; larger sizes on the Pro modelwidth and height, 256 to 2560 px each (default 1280×720)
Image to videoinput_reference, a multipart image uploadinit_image_base64: PNG, JPEG or WebP up to 12 MiB, base64-encoded
Reproducible outputNot documentedseed (integer)
Poll statusGET /v1/videos/{id} → queued, in_progress, completed, failedGET https://zsky.ai/api/v1/jobs/{job_id} → queued, processing, completed, failed, blocked
DownloadGET /v1/videos/{id}/contentresult_url in the completed job: a signed download link valid for 24 hours
Completion webhooksYesNo. Poll; most clips finish in about 30 seconds.
AudioGenerated with the clipSynchronized audio on every clip, included
Lip-sync to your own audioNoYes: send audio_base64 (up to 10 MB) with lip_sync: true
Remix and character endpointsExistedNot offered
Pricesora-2: $0.10 per second of video. sora-2-pro: $0.30 to $0.70 per second depending on resolution.Included with Max ($99 a month, or $79 a month billed annually) with a monthly allowance of videos and images; usage beyond it is pay-per-use, only if you opt in, with a monthly cap you set ($5 to $1,000). Current rate card: zsky.ai/api-docs.
Rate limitsVaried by account tier20 requests a minute per key, 3 concurrent jobs, and a soft cap of 50 videos and 200 images a day (lifted on request)
RetentionAccount data deleted after the shutdownResults kept 7 days unless you delete them earlier. Send X-ZSky-Store: 0 to get the bytes inline and store nothing.
Content safetyModeration errorsSame safety pipeline as the web app. A refused job returns status: "blocked" with a message.
Public feedn/aAPI outputs never appear in ZSky's public Explore feed
Visible watermarkNoneNone on Max outputs (an invisible provenance mark only)

Two things stand out for a migration. The job lifecycle is the same, so your polling loop, retries and storage code survive with renamed fields. And spend has a ceiling: Sora billed every second with no cap of its own, while ZSky's pay-per-use stops at the monthly cap you set.

Parameter mapping

Sora 2 fieldZSky fieldNotes
model(drop it)ZSky has no model parameter.
promptpromptNatural-language prompts port unchanged. Limit 2,000 characters.
seconds: "8"duration: 8Integer seconds, 1 to 10. A 12-second Sora job becomes a 10-second clip.
size: "1280x720"width: 1280, height: 720Each side 256 to 2560 px. Portrait: 720 by 1280.
input_reference (file)init_image_base64 (string)Base64 of a PNG, JPEG or WebP up to 12 MiB. Image URLs are not accepted.
—seedOptional integer for reproducible runs.
—X-ZSky-Store: 0 (header)Optional zero-retention mode.
id (video_…)job_idReturned by the create call together with poll_url.
status: in_progressstatus: processingTerminal states: completed, failed, blocked.
GET /videos/{id}/contentresult_urlPlain HTTPS GET on the signed URL; no API key needed for the download itself.

The port, in Python and curl

Here is a typical Sora 2 integration using the OpenAI SDK, and the same job on ZSky using requests. The ZSky version is the same number of lines; only the field names and the download step change.

Before (Sora 2, OpenAI SDK):

import time
from openai import OpenAI

client = OpenAI()

video = client.videos.create(
    model="sora-2",
    prompt="A lighthouse beam sweeping across a black sea at night, cinematic",
    seconds="8",
    size="1280x720",
)
while video.status in ("queued", "in_progress"):
    time.sleep(5)
    video = client.videos.retrieve(video.id)

content = client.videos.download_content(video.id)
content.write_to_file("clip.mp4")

After (ZSky video API):

import os, time, requests

BASE = "https://zsky.ai/api/v1"
HEADERS = {"X-API-Key": os.environ["ZSKY_API_KEY"]}

job = requests.post(f"{BASE}/videos/generate", headers=HEADERS, json={
    "prompt": "A lighthouse beam sweeping across a black sea at night, cinematic",
    "duration": 8,
    "width": 1280,
    "height": 720,
}).json()
# {"job_id": "8a1...", "status": "queued", "kind": "video", "poll_url": "/v1/jobs/8a1..."}

while True:
    state = requests.get(f"{BASE}/jobs/{job['job_id']}", headers=HEADERS).json()
    if state["status"] in ("completed", "failed", "blocked"):
        break
    time.sleep(5)

if state["status"] == "completed":
    # result_url is a signed link, valid for 24 hours; download it once and keep the file
    open("clip.mp4", "wb").write(requests.get(state["result_url"]).content)
else:
    print(state["status"], state.get("error"))

The same two calls with curl:

curl -s -X POST https://zsky.ai/api/v1/videos/generate \
  -H "X-API-Key: $ZSKY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"A lighthouse beam sweeping across a black sea at night, cinematic","duration":8,"width":1280,"height":720}'
# {"job_id":"8a1...","status":"queued","kind":"video","poll_url":"/v1/jobs/8a1..."}

curl -s https://zsky.ai/api/v1/jobs/8a1... -H "X-API-Key: $ZSKY_API_KEY"
# {"job_id":"8a1...","status":"completed","kind":"video","result_url":"https://...","expires_in":86400}

Image-to-video replaces Sora's multipart input_reference with a base64 string in the same JSON body:

import base64

frame = base64.b64encode(open("frame.png", "rb").read()).decode()
job = requests.post(f"{BASE}/videos/generate", headers=HEADERS, json={
    "prompt": "slow push-in, wind moving through the trees",
    "duration": 5,
    "init_image_base64": frame,
}).json()

Three details worth handling in production. Every 429 carries a plain-English error field naming the cause: the per-minute limit, the concurrency limit and the daily soft cap also send a Retry-After header, while an exhausted monthly allowance does not, because nothing changes until the counters reset or you enable pay-per-use. One handler that reads error and honors Retry-After when present covers all four. A blocked status means the safety filter refused the prompt or the output; show the user the error text rather than retrying. And the signed result_url expires after 24 hours, so download the file when the job completes and keep your own copy.

What Sora charged, and how ZSky bills

OpenAI published Sora 2 API rates per second of generated video: $0.10 for sora-2 at 720p, and $0.30, $0.50 or $0.70 for sora-2-pro at 720p, 1024p and 1080p. A batch tier halved those rates in exchange for up to 24-hour turnaround. An 8-second sora-2 clip was $0.80; the same clip on sora-2-pro at 1080p was $5.60, and a month's bill had no ceiling other than your own rate limiting.

ZSky's API comes with the Max plan ($99 a month, or $79 a month billed annually), which includes a monthly allowance of videos and images. Usage beyond the allowance is pay-per-use, and only if you opt in: you set a monthly cap between $5 and $1,000 in Settings, generation stops at the cap, and unused cap budget rolls over. The current allowance and rate card are on the API docs page. The practical difference from Sora is that your worst-case month is a number you chose in advance, not a function of how many seconds your users requested. For sustained volume beyond the daily soft cap, email [email protected].

Every limit, in one place

How to get a key today

  1. Subscribe to Max ($99 a month, or $79 a month billed annually).
  2. Open Settings → API + MCP and create your key. It is shown once; rotating it invalidates the previous key immediately.
  3. Confirm it works: curl https://zsky.ai/api/v1/usage -H "X-API-Key: zsky_live_..." returns your current-month counters.

If you want to see the output before you pay, the same generation pipeline runs free in the browser at zsky.ai/create with a free account and no card. Type the prompts your Sora integration sends, and judge the clips yourself.

What ZSky does not replace

A migration guide that only lists wins is not one you can plan against. The gaps are:

Using it from an AI agent

The same key also drives ZSky's MCP adapter, a local stdio server for MCP clients such as Claude Code and Claude Desktop. It exposes tools to generate images and videos, animate a local image, check job status and read usage, and it runs against the same /api/v1 endpoints with the same allowance and limits. If your Sora integration lived inside an agent workflow rather than a product, that is the shorter path. Setup steps are on the API and MCP docs page.

Frequently asked questions

Is the Sora API really gone, or only deprecated?

Gone. Deprecation was announced on March 24, 2026, and removal happened on September 24, 2026. OpenAI's deprecations page lists the Videos API and every sora-2 model ID with no recommended replacement.

Is there a free Sora API alternative?

Not from ZSky as an API. ZSky's web app is free and unlimited for people, but the developer API is a Max feature at $99 a month with a monthly included allowance. The free browser tier is a good way to evaluate the output before subscribing.

Does the ZSky API generate audio like Sora 2 did?

Yes. Every clip comes with synchronized audio at no extra cost, and you can supply your own audio track for lip-synced video with audio_base64 and lip_sync: true.

Can I keep my existing Sora prompts?

Yes. Prompts are plain natural language on both APIs. The only hard limit on ZSky is 2,000 characters per prompt.

How long does a video take?

Typically about 30 seconds from submission to a completed status. Poll the job every few seconds; the response carries a terminal flag when it is done.

What happens when I use up the monthly allowance?

The next request returns 429 with a message naming the allowance. If you have opted into pay-per-use in Settings, generation continues at the current rates until your monthly cap is reached; if not, it resumes when the counters reset on the 1st.

Who owns the output?

You do. Max includes an explicit commercial license, API outputs never appear in the public Explore feed, and you can opt out of storage entirely with the X-ZSky-Store: 0 header.

Is there an OpenAI-compatible endpoint I can point the SDK at?

No. The request and response fields differ, as the mapping table shows. Porting a typical integration is a matter of renaming a few fields and downloading from result_url instead of a content endpoint.

Switch your video pipeline today

Max includes API and MCP access with a monthly allowance, pay-per-use beyond it up to a cap you set, and a key you create yourself in Settings.

Get API access with Max → Read the API overview

Sources: OpenAI API deprecations page (Videos API and sora-2 model removals, September 24, 2026); OpenAI Help Center, "What to know about the Sora discontinuation" (app closure April 26, 2026; API discontinuation September 24, 2026); OpenAI's published Sora 2 API per-second rates as summarized by CostGoat and eesel; ZSky API limits and response fields from the ZSky API documentation, checked September 26, 2026.