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.

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_ratiosandsupported_durationsfor each model.supported_frame_imagesandsupported_input_references, for image-to-video and reference-to-video.generate_audio: whether the model can make audio.seed: falseon every v1 model. Sendingseedreturns 400unsupported_parameter; so dosizeand a non-emptyprovider.options.pricing_skus: for example seedance-2 listsper-1000-video-tokensat 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.
| Model | Duration | Resolution |
|---|---|---|
| seedance-2.5 | 4-30 s | see catalog |
| seedance-2-fast | 4-15 s | see catalog |
| kling-3 | 4-15 s | 720p, 1080p |
| wan-3.0 | 2-30 s | 480p, 720p, 1080p |
| minimax-h3 | 5-15 s | 480p, 768p |
| gemini-omni-flash-1.1 | 3-10 s | 360p, 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.
| Code | Status | Typical cause |
|---|---|---|
| invalid_request | 400 | A field out of range, such as a duration the model does not list |
| unsupported_parameter | 400 | size, seed or a non-empty provider.options |
| unsupported_capability | 400 | Frame images or references on a model that does not take them |
| model_not_found | 404 | A 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
- Claude Code allowedMcpServers: the serverUrl rule for mcp.sume.com
Allow only Sume's hosted MCP endpoint in Claude Code managed settings with an allowedMcpServers serverUrl entry, and what the rule does not control.
- claude mcp add --header Bearer: a Sume API key for CI runs
For unattended Claude Code runs, pass a Sume API key as a Bearer header. That session sees every tool, so cap spend and send idempotency keys.
- Claude Code MCP scope: local, project or user for the Sume server
Use project scope for a shared .mcp.json with the Sume URL only, user scope for your own machine, and keep API keys out of shared files.
- Codex 0.160.0 MCP status for one server: confirm Sume with mcp_health
Codex 0.160.0 adds single-server MCP status discovery with thread connection reuse. Confirm the Sume session itself with mcp_health and tools_list.
Written by Sume