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.

4 min readSume
All posts

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.

VideoRouterCatalogItem fields from the OpenAPI spec, read 2026-10-08
FieldWhat it holds
capabilitiesBooleans 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_basisper_second, per_second_by_resolution or per_1000_video_tokens
pricing.list_usd_micros_per_second_by_resolutionProvider list price per output second, keyed by resolution
pricing.billable_marginThe 1.25 house margin
gatedWhen 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

All Developers posts

Written by Sume