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.

4 min readSume
All posts

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 failed line carries an error field on the poll response; read it before you change anything.
  • Cost: usage.cost is 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 Authorization header 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

All Developers posts

Written by Sume