Check duration, resolution, ratio against /v1/videos/models in Node
A Sora-era request will not fit every Sume model. A Node script reads GET /v1/videos/models and lists what the model rejects before you pay for a job.

Sume's GET /v1/videos/models returns, for each model, supported_durations, supported_resolutions, supported_aspect_ratios, supported_frame_images and supported_input_references, so a request can be checked against the model before it is submitted. The Node script below does that for one model and prints every mismatch. Run it in CI or in a pre-submit hook on a ported Sora client.
The docs are direct that limits are different for each model: gemini-omni-flash-1.1 takes 3 to 10 seconds, seedance-2.5 takes 4 to 30, wan-3.0 takes 2 to 30. A duration that worked on one backend can fail on the next, and the catalog is the place to find out which.
The script
It needs Node 18 or later for fetch, and reads the key from the environment. It treats an empty list as no constraint so models without a duration or ratio (such as edit-only ones) do not report false failures.
const MODELS_URL = "https://api.sume.com/v1/videos/models";
async function main() {
const key = process.env.SUME_API_KEY;
if (!key) throw new Error("SUME_API_KEY is not set");
const res = await fetch(MODELS_URL, { headers: { Authorization: `Bearer ${key}` } });
if (!res.ok) throw new Error(`models request failed: ${res.status}`);
const { data } = await res.json();
const req = { model: "gemini-omni-flash-1.1", duration: 12,
resolution: "720p", aspect_ratio: "9:16" };
const m = data.find((x) => x.id === req.model);
if (!m) throw new Error(`unknown model ${req.model}`);
const problems = [];
const check = (field, list, value) => {
if (list && list.length && !list.includes(value)) {
problems.push(`${field} ${value} not in [${list.join(", ")}]`);
}
};
check("duration", m.supported_durations, req.duration);
check("resolution", m.supported_resolutions, req.resolution);
check("aspect_ratio", m.supported_aspect_ratios, req.aspect_ratio);
console.log(problems.length ? problems.join("\n") : "request fits the catalog");
}
main().catch((e) => { console.error(e.message); process.exit(1); });What the example does
With duration: 12 against gemini-omni-flash-1.1, the script reports that 12 is outside the 3 to 10 second list, and nothing is submitted. Change the model to seedance-2.5 and 12 passes. The point is not the specific verdict, which comes from live data, but that the check costs one free GET.
Fields to know when you port
| Old-style idea | Sume field | Checked against |
|---|---|---|
| Length in seconds | duration (integer) | supported_durations |
| Output size | resolution + aspect_ratio | supported_resolutions, supported_aspect_ratios |
| Exact pixel size | size | Not accepted: 400 unsupported_parameter on v1 models |
| Starting image | frame_images with frame_type | supported_frame_images |
| Style references | input_references | supported_input_references |
| Audio on or off | generate_audio | generate_audio flag per model |
Keep it honest
A catalog check does not replace reading the error field on a failed job. Reference images must be public HTTPS URLs in supported formats, and prompts can be refused by a model's guidelines; neither shows up in the catalog. What the check removes is the cheap class of failure: a number that was never going to fit.
Cache the response briefly rather than fetching it per request, and re-read it when you add a model to a fallback list.
Wire it into a build
Run the script as a CI step with a read-only key kept in the environment, and fail the build when the problem list is non-empty. If you keep a fallback model, loop the same check over each model in the chain, so a config change that breaks the second backend fails before it reaches production.
Extend req to match what your app really sends. A request that adds frame_images can also be checked: confirm each frame_type you use appears in supported_frame_images, and each reference type appears in supported_input_references. The docs say a model accepts a reference type only if the list includes it, so this is a direct test, not a guess.
One more field is worth reading: generate_audio. If your pipeline needs a silent clip and the model reports no way to control it, the check should flag that rather than let a track appear in your output.
Sources
Related posts
More in Developers
- communication.webhook_url 400: HTTPS, public host, 2048 chars
A communication.webhook_url that is not public HTTPS, is over 2048 characters, or points at localhost or a private network returns 400 invalid_request.
- Connect a new MCP client to Sume: five calls that prove it works
After you add https://mcp.sume.com/mcp to a new client, run mcp_health, tools_list, tools_schema, account_me and catalog_list. What each result should show.
- Convert an SRT file to Sume caption cues in Python
Sume captions take no SRT upload, but cues carry the same start, end and text. A 26-line Python script turns an SRT into cues and posts them for $0.20.
- Cost per ad variant: build a ledger from usage.cost on Sume
Every completed /v1/videos poll carries usage.cost. Sum it by hook and ending to get the cost per ad variant before media spend. Node script and the caveats.
Written by Sume