Pick the cheapest Video Router model from the live catalog
Read GET /v1/video-router/models, filter by resolution, duration and capability, and rank by list x 1.25 in integer micros. Node code with an offline test.

Fetch GET /v1/video-router/models, drop the gated models, keep the rows whose resolutions and duration range fit your job, and sort by list price times 1.25. The catalog returns the pricing on every row, so you never hard-code a price that can go stale. Do the math in integer micros of a dollar so the sums stay exact.
Fields the catalog gives you
Token-priced models use a different basis, so the code below skips them. Price those by hand from the documented formula.
| Field | What it holds |
|---|---|
| capabilities | Booleans such as text_to_video, image_to_video, reference_images, video_to_video and audio, plus resolutions, duration_seconds {min,max} and aspect_ratios |
| pricing.list_basis | per_second, per_second_by_resolution or per_1000_video_tokens |
| pricing.list_usd_micros_per_second_by_resolution | Provider list price per output second, keyed by resolution |
| pricing.billable_margin | The 1.25 house margin |
| gated | When true, the model is omitted from public listings until GA |
The picker
Save this as pick.mjs. The function pick() is pure, so you can test it with a small array. As a worked example, a model that lists $0.10 a second at 720p bills 100,000 x 1.25 = 125,000 micros a second, so 8 seconds are 1,000,000 micros, which is $1.00.
export function pick(models, { resolution, seconds, need = [] }) {
const rows = [];
for (const m of models) {
const c = m.capabilities;
if (m.gated || !c.resolutions.includes(resolution)) continue;
if (seconds < c.duration_seconds.min || seconds > c.duration_seconds.max) continue;
if (need.some((k) => !c[k])) continue;
const p = m.pricing;
const list = p.list_basis === "per_second_by_resolution"
? p.list_usd_micros_per_second_by_resolution[resolution]
: p.list_basis === "per_second" ? p.list_usd_micros_per_second : undefined;
if (list === undefined) continue; // token-priced: price it separately
rows.push({ id: m.id, micros: Math.round(list * 1.25) * seconds });
}
return rows.sort((a, b) => a.micros - b.micros);
}
if (process.argv[1].endsWith("pick.mjs")) {
const res = await fetch("https://api.sume.com/v1/video-router/models", {
headers: { "x-api-key": process.env.SUME_API_KEY ?? "" },
});
if (!res.ok) throw new Error(`catalog ${res.status}`);
const { models } = (await res.json()).data;
for (const r of pick(models, { resolution: "720p", seconds: 8, need: ["text_to_video"] }))
console.log(r.id, `$${(r.micros / 1e6).toFixed(4)}`);
}Using the result
Run the picker in a nightly job and log the top three ids. A change in the ranking is a useful signal that the catalog or the prices moved.
Before a large batch, add the per-job reserve to the arithmetic. A 20-clip batch of 8-second 720p clips at 1,000,000 micros each needs 20,000,000 micros, or $20.00, in the balance before the first submit.
- The ranking is price only. Check quality on a draft before you commit a batch to the cheapest row.
- Pass need: ["reference_images"] or another capability key to filter by what the job requires.
- Cache the catalog for a short time. It is a read, and the catalog changes rarely.
- Pin the chosen id in your request. Sume's own auto model hides the family, so pinning is the only way to compare.
Sources
Related posts
More in Developers
- Pin Gemini Omni Flash 1.1 or send sume/auto: six checks
sume/auto picks the model for video and defaults to 720p and 8 s. Pin gemini-omni-flash-1.1 when you need edit mode, 4K or fixed references. A decision table.
- Pinterest video aspect ratio text, quoted, and a master check
Pinterest's video spec says 'shorter than 1:2, taller than 1.91:1'. Quote it, convert the recommended ratios to decimals, and check a master with Sume frames.
- Pointing the OpenAI SDK at api.sume.com: why Sora calls still fail
Changing only the base URL does not carry a Sora call to Sume. Five documented differences: model ids, body format, seconds and size, status words, download.
- Poll a Sume image job in Python: obey next_poll_after_seconds
When POST /v1/images returns 202, keep polling the status_url and sleep for next_poll_after_seconds. A short Python loop that stops on a terminal status.
Written by Sume