Python argparse CLI that checks seconds per Sume model first
A 25-line argparse CLI: choices reject unknown models, a duration check uses the documented limits, and --dry-run prints the body. Runs offline.

You can stop a bad Sume video request before it leaves your machine with an argparse CLI that holds each model's duration range, rejects a model name it does not know, and offers --dry-run to print the request body. The 25-line script below does that. sys.exit with a string prints it to stderr and exits with status 1, which is what a shell script or CI job expects.
The ranges come from the Sume video docs and are copied into the script. That is quick, and it can go stale. The last section shows how to replace the table with a live catalog read.
The documented limits
Other models have their own ranges, and the catalog lists them in supported_durations. Add a row to LIMITS only after you read that field.
| Model id | Seconds accepted |
|---|---|
| seedance-2.5 | 4 to 30 |
| wan-3.0 | 2 to 30 |
| minimax-h3 | 5 to 15 |
| gemini-omni-flash-1.1 | 3 to 10 |
The script
Run python3 clip.py wan-3.0 "A mug on a desk" --seconds 12 --dry-run and it prints the JSON body. Run it with minimax-h3 and --seconds 20 and it exits with minimax-h3 accepts 5 to 15 seconds, not 20. Without --dry-run it posts to /v1/videos, so set SUME_API_KEY and CLIP_KEY, the idempotency key for the job.
import argparse, json, os, sys, urllib.request
LIMITS = {"seedance-2.5": (4, 30), "wan-3.0": (2, 30), "minimax-h3": (5, 15),
"gemini-omni-flash-1.1": (3, 10)} # seconds, from the Sume video docs
p = argparse.ArgumentParser(prog="clip")
p.add_argument("model", choices=LIMITS)
p.add_argument("prompt")
p.add_argument("--seconds", type=int, default=8)
p.add_argument("--dry-run", action="store_true")
a = p.parse_args()
lo, hi = LIMITS[a.model]
if not lo <= a.seconds <= hi:
sys.exit(f"{a.model} accepts {lo} to {hi} seconds, not {a.seconds}")
body = {"model": a.model, "prompt": a.prompt, "duration": a.seconds}
if a.dry_run:
print(json.dumps(body))
sys.exit(0)
req = urllib.request.Request(
os.environ.get("SUME_BASE", "https://api.sume.com") + "/v1/videos", method="POST",
data=json.dumps(body).encode(),
headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json", "Idempotency-Key": os.environ["CLIP_KEY"]})
with urllib.request.urlopen(req, timeout=35) as r:
print(r.status, json.load(r))Choices as a first gate
choices=LIMITSmakes argparse reject a misspelled model with a list of valid ones, before any network call.- The default of 8 seconds is valid for three of the four models and wrong for none;
gemini-omni-flash-1.1accepts up to 10. - The dry run prints exactly what would be sent, so you can diff it against an earlier run.
Replacing the table with the catalog
GET /v1/videos/models returns supported_durations for each model. Fetch it once at start, build LIMITS from the list, and keep the hard-coded table only as a fallback for offline runs. Then a launch that changes a range changes the CLI with no edit. The related post on checking a new model shows the fields to read.
Keep the script's output small and stable. A deploy job that parses it will break the day you add a chatty line.
Exit codes and CI
Used in a pipeline, the script has three outcomes. A valid dry run exits 0 and prints the body. A duration outside the range, or a model that is not a choice, exits non-zero before any request. A live run that gets an HTTP error raises, and Python exits with status 1 and a traceback.
That is enough for a CI gate: run the dry run for every clip in a shot list, and fail the build if any line exits non-zero. No job is created and nothing is billed, so the gate can run on every commit.
If you later add polling, keep it out of this file. A small tool that does one thing is easier to test than one that submits, waits, and downloads.
Sources
Related posts
More in Developers
- Python asyncio price check: one Omni Flash clip at four resolutions
A runnable Python script submits a 3-second Omni Flash clip at 360p, 720p, 1080p and 4K together and prints usage.cost. Expect $2.20 in total.
- Python check: is this MP4 over TikTok's 516 kbps? Size and duration
A short Python function turns file bytes and duration_seconds into average kbps and tests TikTok's 516 kbps floor, 500 MB cap and 10-minute limit.
- Python fallback chain for Sume video models: 404 and 503 only
After the Sora API shutdown, a model chain must not retry everything. This Python function moves on at 404 and 503 and stops on 429, 402, 400, 409.
- Python: find Sume image models that list a ratio like 8:1 or 4:5
A 15-line Python script reads GET /v1/images/models and prints every Sume image model that lists a given aspect ratio, so you stop guessing before a 400.
Written by Sume