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.

5 min readSume
All posts

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

Pre-submit checks against GET /v1/videos/models, read 2026-10-05
Field on the modelShot fieldExample failure
supported_durationsdurationa 3 s shot sent to seedance-2.5 (4-30 s)
supported_resolutionsresolution480p sent to kling-3 (720p, 1080p)
supported_aspect_ratiosaspect_ratio21:9 sent to wan-3.0 or kling-3
supported_frame_imagesframe_images[].frame_typelast_frame on a model without end frames
supported_input_referencesinput_references[].typean 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

All Developers posts

Written by Sume