How Sume Bills a Failed AI Video Job: Reserve, Refund, Retry
A video job reserves its price at submit and refunds on failure. See what Idempotency-Key does on a retry and when to download before storage expires.

Video generation fails more often than text, because jobs run minutes and depend on an upstream provider. What matters to a buyer is whether a failure costs money and whether a retry can double-charge. Here is how Sume's video routes handle both, from its docs.
Reserve then refund
Every video model on Sume is billed at provider list times 1.25. The amount is reserved from the workspace USD balance when you submit, and a failed job refunds it. On a completed job, the poll response carries usage.cost, which is the billable amount, not the provider's list price. The job states are pending, in_progress, completed, failed and cancelled.
This is the opposite of a credit scheme with minimum charges. Runway's pricing page, for example, lists minimums for some models, such as 56 credits for Aleph 2. Sume's reserve is the price of the clip you asked for.
Retries and Idempotency-Key
Send an Idempotency-Key header on POST /v1/videos and a replay returns the original job rather than creating and charging a second one. Auto requests are deterministic under replay, so the same key prices and routes identically. Generate one key per intended clip, not per attempt, and persist it before the first request goes out.
A new key is a new job. If you want a different take of the same prompt, use a new key; no Sume video model accepts a seed, so a re-run is not a reproduction.
Download early
Google's Veo page says generated videos are stored for 2 days. Do not assume longer for any host: fetch the file as soon as the status is completed and keep your own copy. The script below submits, polls, downloads and prints the billed amount.
import os, sys, time, uuid
import requests
key = os.environ.get("SUME_API_KEY", "")
if not key:
sys.exit("Set SUME_API_KEY first")
auth = {"Authorization": f"Bearer {key}"}
body = {"model": "wan-3.0", "prompt": "A ceramic mug rotating on a desk, soft daylight",
"duration": 4, "resolution": "480p", "aspect_ratio": "16:9"}
r = requests.post("https://api.sume.com/v1/videos", json=body, timeout=60,
headers={**auth, "Idempotency-Key": str(uuid.uuid4())})
r.raise_for_status()
job = r.json()
while job["status"] in ("pending", "in_progress"):
time.sleep(15)
job = requests.get(job["polling_url"], headers=auth, timeout=60).json()
if job["status"] != "completed":
sys.exit(f"{job['status']}: {job.get('error')}")
video = requests.get(job["unsigned_urls"][0], headers=auth, timeout=300)
with open("clip.mp4", "wb") as f:
f.write(video.content)
print("saved clip.mp4, billed", job["usage"]["cost"])
Webhooks instead of polling
Pass an HTTPS callback_url and Sume POSTs on a terminal state, with x-sume-webhook-timestamp and x-sume-webhook-signature headers over the raw JSON body. Verify the signature with a non-empty secret, and treat the poll endpoint as the source of truth if a delivery is missed. Job status is also at GET /v1/jobs/{id}/status.
See the model checklist for choosing what to submit.
Sources
Related posts
More in Developers
- httpx MockTransport: test a Sume poll loop with no network
Pass httpx.MockTransport to httpx.Client to feed a Sume status poller canned replies, assert it stops on terminal and honors next_poll_after_seconds.
- Idempotency-Key over 255 characters: hash long business keys
Sume accepts Idempotency-Key values up to 255 characters. Keep readable keys when short and fall back to a prefixed SHA-256 for long ones. Runnable Python.
- Idempotency-Key for a SaaS: customer, order and version
Derive a Format run's Idempotency-Key from customer id, order id, Format slug and a version you bump on purpose, so double clicks never make a second paid run.
- Ideogram 4 download: Hugging Face gate, login and first image
To run Ideogram 4 locally: accept the gate on Hugging Face, log in with hf, pip install the repo, run run_inference.py. The flags and the nf4 or fp8 choice.
Written by Sume