H3 Max Recast in Python: submit, poll and read the result
A Python script for Sume's h3-max-recast: submit with an Idempotency-Key, poll until the job is terminal, read the result. Cost per clip included.

To run H3 Max Recast from Python on Sume, POST to /v1/video-router/generate with model: "h3-max-recast", a source video_url, one to four reference_image_urls, the source length in duration and mode: "async", then poll /v1/jobs/{id}/status until it is terminal and read /v1/jobs/{id}/result. The script below does exactly that with the requests package.
It follows the "client subscribe" pattern from Sume's Jobs and results page: submit async, poll with the server's next_poll_after_seconds, and read the result once. The model contract comes from the Video Router docs, and the description of what Recast does from fal's H3 Max Recast page (read 2026-10-02).
import os, sys, time, requests
BASE = "https://api.sume.com"
AUTH = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
def recast(video_url, photos, seconds, key):
body = {"model": "h3-max-recast", "video_url": video_url,
"reference_image_urls": photos, "duration": seconds,
"resolution": "768p", "mode": "async"}
r = requests.post(f"{BASE}/v1/video-router/generate", json=body,
headers={**AUTH, "Idempotency-Key": key}, timeout=60)
r.raise_for_status()
body = r.json()
job = body.get("request_id") or body["id"]
while True:
s = requests.get(f"{BASE}/v1/jobs/{job}/status", headers=AUTH).json()
if s.get("terminal"):
break
time.sleep(s.get("next_poll_after_seconds") or 10)
if s.get("sume_status") != "completed":
raise RuntimeError(f"job {job} ended as {s.get('sume_status')}")
return requests.get(f"{BASE}/v1/jobs/{job}/result", headers=AUTH).json()
if __name__ == "__main__":
video, photo, secs, key = sys.argv[1:5]
print(recast(video, [photo], int(secs), key))What does the script assume?
It needs SUME_API_KEY in the environment and takes four arguments: a public https URL for the source video, a public https URL for one person photo, the source length in whole seconds (5 to 30), and an idempotency key. Get the length by probing the clip first with video inspect; do not guess.
It sends 768p, Sume's default. Switch to "1080p" for the higher tier. The submit response is read for id, and I have not confirmed the exact result field names for a finished Recast job in the docs, so the script prints the whole result JSON instead of pulling a field. Look at that once, then pick the field you want.
Why is there an Idempotency-Key?
Recast jobs are paid, and a network timeout on submit can leave you unsure whether the job exists. Sume's docs say retrying the submit with the same Idempotency-Key returns the original job instead of billing a second one, and that you must not submit a new paid job for the same intent. The script takes the key as an argument for that reason: generate one value per intended clip, such as a UUID you store beside the job, and reuse it on every retry of that same clip.
If your own process dies mid-poll, do not resubmit. Keep the job id and resume polling it; the doc is explicit that a local timeout is not a reason to repeat the paid request.
What does a run cost?
Recast is billed per second of output. fal lists $0.30 per second at 768p and $0.45 at 1080p; Sume bills list times 1.25, which is $0.375 and $0.5625 per second. Sume reserves the cost on submit from the duration you send, rounded up to whole seconds.
| Source length | 768p | 1080p |
|---|---|---|
| 5 s | $1.88 | $2.81 |
| 12 s | $4.50 | $6.75 |
| 30 s | $11.25 | $16.88 |
What fails, and where do you see it?
Bad bodies are refused at submit, before any reservation; raise_for_status surfaces that as an HTTP error, and the response names the field. The list is in the rejected-fields post. A job that starts and then fails ends with a failed or canceled status, which the script turns into an exception that carries the job id.
The script has no timeout on the overall wait. Add a deadline if you run it in CI, but make it stop polling, not resubmit. For a general Python pattern on a different video model, see the MiniMax H3 Python job post.
Sources
Related posts
More in Developers
- H3 Max Recast request rejected: every field Sume refuses
Sume refuses H3 Max Recast bodies with aspect_ratio, generate_audio, bitrate_mode, frame or audio references, or a duration outside 5-30 s. The full list.
- Genjutsu missing from /v1/video-router/models: when it is listed
If higgsfield-genjutsu is not in GET /v1/video-router/models, Sume hides it when its provider is not configured. How to check, and what to use instead.
- Instagram media_audio_type: MUSIC vs ORIGINAL_SOUND on Reels
Instagram added a media_audio_type field on June 1, 2026 that tells licensed MUSIC from ORIGINAL_SOUND. What it means for a Reel you build with Sume.
- Instagram Reel container status_code: wait for FINISHED, not 200
An Instagram Reel container is not publishable until status_code is FINISHED, and it EXPIRES after 24 hours. A polling loop and the 100-post daily limit.
Written by Sume