Check GET /v1/videos/models before you pay: a Python validator

Read GET /v1/videos/models and reject bad duration, resolution or audio flags in Python before POST /v1/videos. Fewer 400s, no paid retries.

5 min readSume
All posts

GET /v1/videos/models lists each video model with the durations, resolutions, aspect ratios and frame-image support it accepts. A client can read that list once and refuse a bad request locally. That turns a 400 invalid_request or unsupported_capability into a line of your own code, before any job exists.

The catalog also carries generate_audio, seed: false and pricing_skus.

What the catalog tells you

  • supported_resolutions, supported_aspect_ratios and supported_durations for each model.
  • supported_frame_images and supported_input_references, for image-to-video and reference-to-video.
  • generate_audio: whether the model can make audio.
  • seed: false on every v1 model. Sending seed returns 400 unsupported_parameter; so do size and a non-empty provider.options.
  • pricing_skus: for example seedance-2 lists per-1000-video-tokens at 0.0154, the billable rate.

Limits to check by model

These values come from the video models guide. The catalog is the source of truth, so let the script read it and treat this table as a sample. Gemini Omni Flash 1.1 is the one with native audio and 16:9 or 9:16 only; Wan 3.0 spans the widest duration range of the group at 2 to 30 seconds, so a duration that fails on one model can pass on another.

Duration and resolution by model (read 2026-10-07)
ModelDurationResolution
seedance-2.54-30 ssee catalog
seedance-2-fast4-15 ssee catalog
kling-34-15 s720p, 1080p
wan-3.02-30 s480p, 720p, 1080p
minimax-h35-15 s480p, 768p
gemini-omni-flash-1.13-10 s360p, 720p, 1080p, 4K

A validator in Python

The check below fetches the catalog, finds the model and compares the duration. It prints the problem and exits non-zero, so a CI job or a queue worker stops before it POSTs.

import json, os, sys, urllib.request

def get(path):
    req = urllib.request.Request(
        "https://api.sume.com" + path,
        headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"]})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)

def check(model, duration):
    body = get("/v1/videos/models")
    rows = body.get("data", body) if isinstance(body, dict) else body
    row = next((m for m in rows if m.get("id") == model), None)
    if row is None:
        return "unknown model " + model
    ok = row.get("supported_durations") or []
    if ok and duration not in ok:
        return "duration %s not in %s" % (duration, ok)

problem = check("wan-3.0", 12)
if problem:
    sys.exit(problem)
print("ok to POST /v1/videos")

Where to run it

A good place for the catalog read is service start-up, with a refresh every few hours. The read counts against the read bucket, which is separate from the write bucket and is 40 times larger. A Free key can read 4,800 times a minute and write 120 times, so a catalog refresh will not eat into your submit budget.

Keep the validator thin. It should check the fields the catalog names, and nothing else. When Sume adds a model or changes a range, your code follows the catalog and you do not need a release.

Errors the catalog helps you avoid

Each of these is cheap, because no job is created. The point of the local check is not to save money. It saves round trips and keeps your logs free of failures you could have predicted.

Video submit errors that a local check can prevent (read 2026-10-07)
CodeStatusTypical cause
invalid_request400A field out of range, such as a duration the model does not list
unsupported_parameter400size, seed or a non-empty provider.options
unsupported_capability400Frame images or references on a model that does not take them
model_not_found404A model id that is not in the catalog

What the check does not cover

The catalog cannot tell you if your input URL is reachable. A frame image that Sume cannot download fails the job with a public message such as "Could not download an input media URL (image_url)". Test those URLs yourself, and keep the real submit under an Idempotency-Key.

A 402 insufficient_credits is raised when the job reserves its cost, so the catalog cannot catch it either.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume