Turn a shot list into Gemini Omni requests: Python on Sume

Submit every line of a shot list as its own Omni job with stable Idempotency-Keys, then poll them in one pass. Runnable Python and the cost of a 3-shot list.

5 min readSume
All posts

A shot list becomes Omni requests by sending one POST /v1/videos per line, giving each a deterministic Idempotency-Key, and then polling all the job URLs in a single loop. Because Omni caps a clip at 3 to 10 seconds in Sume's catalog, a film is always a list of short jobs; the list is the plan.

The script below needs only SUME_API_KEY in the environment and the requests package.

Shape the list so keys are stable

Key each shot by its number and a version string you bump on purpose, for example ad-v3-shot-02. If the process crashes and you rerun it, the same body with the same key returns the original job instead of a new charge. The Sume doc notes that reusing a key with a different body is a 409, so edit the version, not just the prompt, when you mean to re-roll.

The script

It submits all shots first so they run in parallel on Sume's side, then polls each polling URL every 15 seconds until none is pending or in progress.

import os, time, requests
BASE = 'https://api.sume.com/v1/videos'
H = {'Authorization': 'Bearer ' + os.environ['SUME_API_KEY']}
VERSION = 'ad-v1'
SHOTS = [
    ('Wide: a bakery storefront at sunrise, steam from the vents.', 6),
    ('Close-up: hands dust flour over dough, warm light.', 5),
    ('Medium: a baker slides a tray of loaves from the oven.', 6),
]
def main():
    jobs = {}
    for n, (prompt, secs) in enumerate(SHOTS, 1):
        body = {'model': 'gemini-omni-flash-1.1', 'prompt': prompt,
                'duration': secs, 'resolution': '720p', 'aspect_ratio': '16:9'}
        r = requests.post(BASE, json=body, timeout=60,
                          headers={**H, 'Idempotency-Key': f'{VERSION}-shot-{n:02d}'})
        r.raise_for_status()
        jobs[n] = r.json()['polling_url']
    done = {}
    while len(done) < len(jobs):
        time.sleep(15)
        for n, url in jobs.items():
            if n in done:
                continue
            s = requests.get(url, headers=H, timeout=60).json()
            if s['status'] in ('completed', 'failed', 'cancelled'):
                done[n] = s
    for n in sorted(done):
        print(n, done[n]['status'], done[n].get('unsigned_urls'))
main()

What a 3-shot list costs

Sume bills list times 1.25 per second, rounded up to the cent per job, with fal list rates for Omni Flash 1.1 (recorded 2026-08-28) of $0.03, $0.10, $0.15 and $0.30 per second at 360p, 720p, 1080p and 4K. The script's three shots total 17 seconds (6 + 5 + 6).

Three shots totalling 17 seconds, per resolution
ResolutionPer secondCost of 17 seconds across 3 jobs
360p$0.0375about $0.64 before per-job rounding
720p$0.125about $2.13 before per-job rounding
1080p$0.1875about $3.19 before per-job rounding

Failure handling you should add

A failed poll carries an error string; a bad image URL, for example, reads as a download problem rather than a provider wrapper. Print it, fix the input, bump the version, and resubmit only that line. A 402 means the workspace balance is below the reserve, and a 429 means slow down; both are in the doc's error table. Prefer callback_url over polling once you run this from a server; see the related post on polling backoff.

Where the script goes next

Once the jobs are complete, the list of unsigned_urls is your shot order. Download each file, check the duration and the frame size, and join them with a Sume timeline if you want one file. All shots from one model share the same aspect ratio, so no crop is needed; mix models only after checking frame rates and ratios. Keep your SHOTS list in version control: the key is built from the version and the shot number, so the list is also your audit trail of what you ran and what you paid for.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume