Sora replacement smoke test: one prompt, three Sume models, in Python
A short Python script that sends the same 5-second prompt to seedance-2.5, wan-3.0 and gemini-omni-flash-1.1 on Sume and prints status, cost and URLs.

To check which replacement fits a Sora-era prompt, send the identical request to a few candidates and compare status, cost and the saved file. The script below does that against three Sume catalog ids, seedance-2.5, wan-3.0 and gemini-omni-flash-1.1, using the documented submit, poll and download flow.
Why these three
Higgsfield's Sora migration guide, published October 3, lists Seedance 2.5, Kling 3.0 and Wan 3.0 as alternatives, noting Seedance 2.5 and Wan 3.0 at 30 seconds and 1080p. Sume's docs list seedance-2.5 at 4 to 30 seconds, wan-3.0 at 2 to 30 seconds and gemini-omni-flash-1.1 at 3 to 10 seconds with 360p to 4K. A 5-second clip is inside all three duration ranges, which makes it a fair first test. Check supported_resolutions for each id before you rely on 720p.
The script
It submits all three jobs first, so they run in parallel, then polls each one. It needs SUME_API_KEY in the environment and the requests package.
import os
import time
import requests
BASE = "https://api.sume.com/v1/videos"
AUTH = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
MODELS = ["seedance-2.5", "wan-3.0", "gemini-omni-flash-1.1"]
PROMPT = "A paper boat drifts down a rain-soaked street, slow tracking shot"
def submit(model):
body = {"model": model, "prompt": PROMPT, "duration": 5,
"resolution": "720p", "aspect_ratio": "16:9"}
headers = {**AUTH, "Idempotency-Key": f"smoke-{model}-001"}
r = requests.post(BASE, json=body, headers=headers)
r.raise_for_status()
return r.json()["polling_url"]
def wait(url):
while True:
job = requests.get(url, headers=AUTH).json()
if job["status"] in ("completed", "failed", "cancelled"):
return job
time.sleep(30)
urls = {m: submit(m) for m in MODELS}
for model, url in urls.items():
job = wait(url)
print(model, job["status"], job.get("usage", {}).get("cost"), job.get("unsigned_urls"))Reading the output
Each line prints the model, the final status, usage.cost and the download URLs. Compare three things:
- Status: a
failedline carries anerrorfield on the poll response; read it before you change anything. - Cost:
usage.costis the Sume billable amount; the balance is reserved on submit at provider list times 1.25. - File: download each URL with your key in the
Authorizationheader and watch the clips side by side.
Limits of a one-prompt test
One prompt says little about quality across your catalog. Use it to confirm that each model accepts your fields and returns a file, then run your real prompts. If a model rejects resolution or aspect_ratio, the 400 response is unsupported_parameter; check the model's entry in GET /v1/videos/models and adjust.
The script is safe to re-run. The fixed idempotency keys replay the original jobs, so change the suffix when you want fresh renders. See Jobs and results for status handling, including why a local timeout should never trigger a resubmit.
Sources
Related posts
More in Developers
- Sort your MCP tool allowlist: stable order keeps the prompt cache warm
MCP 2026-07-28 asks servers for ttlMs, cacheScope and a deterministic tool order. On the client, sort your media tool allowlist the same way. Python snippet.
- Split a narration script by model character limit: Python
ElevenLabs lists a 40,000 character limit for Flash v2.5 and 10,000 for v4 and v4 Turbo. A Python splitter that cuts on sentences, with a concat step on Sume.
- Patching Supabase Postgres 17.11 vs Sume's 10-attempt webhook budget
Supabase's September 25 Postgres 15.19 and 17.11 releases fix 44 CVEs. A restart can outlast Sume's ten 30-second webhook attempts, so plan a redeliver.
- Supabase cached egress is $0.03/GB: cost of serving a 20 MB AI clip
Supabase lists cached Storage egress at $0.03 per GB. Worked arithmetic for serving generated clips, and when to link a Sume media URL instead of copying.
Written by Sume