Check each shot against /v1/videos/models before submit: Python
A short Python check tests each shot's duration, resolution and ratio against GET /v1/videos/models fields, so a bad shot fails before it is billed.

Before you submit a multi-shot plan to Sume, fetch GET /v1/videos/models once and check each shot against supported_durations, supported_resolutions and supported_aspect_ratios for its model. A shot that breaks a limit then fails on your machine, not in a request that returns 400. The check below is plain Python and runs offline against sample catalog rows.
This is useful this month because the ranges differ so much between models: 2 to 30 seconds for Wan 3.0, 4 to 30 for Seedance 2.5, 4 to 15 for Kling 3, and 3 to 10 for Gemini Omni Flash 1.1. Kling 4.0 is listed at 3 to 30 seconds in the fal explainer (read 2026-10-05), but it is not a Sume model yet.
What the endpoint returns
The video docs list the fields on each model entry: supported_resolutions, supported_aspect_ratios, supported_durations (whole seconds), supported_frame_images, supported_input_references, generate_audio, and seed. The docs say to use the endpoint to find the resolutions, ratios and durations before you submit, because limits are different for each model.
The same page lists the error that you avoid. A parameter that the selected model does not list is rejected, not ignored; for size, the response is 400 unsupported_parameter. Checking first costs nothing, and it shows you which shots need a different model, not just a different number. The endpoint is part of the same API key and base path as generation, so the check needs no extra setup beyond the Authorization: Bearer header you already send.
The checks and what they catch
| Field on the model | Shot field | Example failure |
|---|---|---|
| supported_durations | duration | a 3 s shot sent to seedance-2.5 (4-30 s) |
| supported_resolutions | resolution | 480p sent to kling-3 (720p, 1080p) |
| supported_aspect_ratios | aspect_ratio | 21:9 sent to wan-3.0 or kling-3 |
| supported_frame_images | frame_images[].frame_type | last_frame on a model without end frames |
| supported_input_references | input_references[].type | an audio reference on gemini-omni-flash-1.1 |
Run it on the whole plan
Do the check on the whole plan, not shot by shot as you go, so one error shows all the problems at once. Sum the estimated prices too, and compare them with your balance: a 402 insufficient_credits is raised at submit, not at the poll.
Add two more tests as your plan grows. First, test supported_frame_images: if a shot sends a last_frame, the model must list it, and kling-3, wan-3.0, seedance-2.5 and minimax-h3-max do, while grok-imagine-video-1.5 does not. Second, test supported_input_references: an audio reference works on the Seedance 2.x, Wan 3.0 and MiniMax H3 models but not on Gemini Omni Flash 1.1, and a model with no reference fields, such as kling-3, should get none.
A failed check is cheap to fix. Move the shot to a model that lists what you need, or change the shot: a 3 second shot can move to Wan 3.0 (2 to 30 seconds) or Omni (3 to 10), and a 21:9 shot can move to Seedance 2.5 or MiniMax H3. Print the cheapest model that passes, using the price tables, and the plan fixes itself.
The check does not replace the live response. A model's entry can change when the catalog is updated, so fetch the endpoint at the start of every run and do not copy the ranges into your code.
A plan checker
The sample rows below use real ranges from the catalog for the four models. In production, replace CATALOG with the data array from the endpoint.
CATALOG = {
"wan-3.0": {"d": range(2, 31), "r": {"480p", "720p", "1080p"},
"a": {"16:9", "4:3", "1:1", "3:4", "9:16"}},
"seedance-2.5": {"d": range(4, 31), "r": {"480p", "720p", "1080p"},
"a": {"21:9", "16:9", "4:3", "1:1", "3:4", "9:16"}},
"kling-3": {"d": range(4, 16), "r": {"720p", "1080p"},
"a": {"16:9", "9:16", "1:1"}},
}
def problems(shot):
m = CATALOG.get(shot["model"])
if not m:
return ["unknown model"]
out = []
if shot["duration"] not in m["d"]:
out.append("duration")
if shot["resolution"] not in m["r"]:
out.append("resolution")
if shot["aspect_ratio"] not in m["a"]:
out.append("aspect_ratio")
return out
plan = [
{"model": "wan-3.0", "duration": 2, "resolution": "720p", "aspect_ratio": "16:9"},
{"model": "kling-3", "duration": 20, "resolution": "720p", "aspect_ratio": "21:9"},
]
for i, s in enumerate(plan, 1):
print(i, s["model"], problems(s) or "ok")Then submit
When every shot passes, submit each with its own Idempotency-Key, as in the idempotency post, and size the wave from generation_limits in the admission docs. The Video Router docs describe the older flat fields if you still use that route. Keep the checker in your repository next to the plan file, and run it in CI, so that a changed model range is caught on the day the catalog changes and not on the day of a client delivery.
Sources
Related posts
More in Developers
- Check an Ideogram 4.5 edit left the rest of the image alone (Python)
Diff the source and the Ideogram 4.5 edit with Pillow, count changed pixels outside your text box, and fail the pass before it enters a chain. Code included.
- Check one video model first: GET /v1/video-router/models/{id}
Before you spend on a clip, read one model's limits with GET /v1/video-router/models/{model_id}. It returns capabilities and price, and 404s on an unknown id.
- Reference video job failed on download: check input URLs first
A Sume reference-to-video job fails when an input URL is not public. Python that HEAD-checks references before POST /v1/videos, and how to read the error.
- CI test: assert Seedance 2.5 and Wan 3.0 still list 30 seconds
Fail a build when your 30-second video code outlives the catalog. A short Python test reads supported_durations from GET /v1/videos/models and checks 30.
Written by Sume