Developers
Building on the Sume API: keys and hosts, jobs and runs, polling, webhooks, idempotency, errors and rate limits, the SDK, the CLI, and MCP auth.
Start with Sume API quickstart: your first video generation call in five steps

Size a Sume submit wave: limit 100, 30 processing, 10 queued gives 60
How many new jobs can a Sume workspace take? max(0, limit - active - queued), capped by queue_capacity_remaining. Worked numbers and a TypeScript helper.

TTS speed 1.2 turns a 30-second read into about 25 seconds
Sume TTS accepts generation_config.speed from 0.6 to 1.5. Speed changes length, not the bill: 450 characters cost $0.021375 at any speed. Test the real length.

Vidu: failed and moderated videos use no credits. What Sume does
Vidu's pricing page says failed generations, moderation rejections included, use no credits. Sume reserves at submit and releases on failure. How to check each.

Python cost cap for a mixed media job: round up, then refuse
A 25-line Python estimator with Decimal prices that rounds the total up to the cent, as Sume's reservation does, and exits before submit if it is over your cap.

Face swap API sync mode waits at most 30 s: then poll, do not resubmit
Sume's job modes cap sync and subscribe waits at 30 seconds. A face swap that is not terminal by then returns the envelope: poll the job, never resubmit.

A 5-minute voice-over: 4.8 MB as 128 kbps MP3, 26.5 MB as WAV
Sume TTS defaults to MP3 at 44,100 Hz and 128 kbps. Five minutes is 4.8 MB; 16-bit mono WAV is 26.46 MB at 44.1 kHz and 28.8 MB at 48 kHz.

AI video API billing units: per clip, per second, credits or tokens
Luma bills per generation, LTX per second, Runway and Vidu in credits, Google in tokens, MiniMax per second plus inputs, Sume in USD. Worked examples.

waitForRun or waitForJob? Pick by which Sume endpoint made the id
A Sume run id cannot be read as a job id. Which create endpoint made your id decides waitForRun or waitForJob, with a TypeScript example of each.

Do Sume result URLs expire? media.sume.com vs /videos content
Sume Format artifact URLs on media.sume.com do not expire and are public. The /v1/videos content route needs your key. What to store, plus a Python download.

Sume job next_action: poll_status, fetch_result, inspect_events
The Sume status payload tells your client what to do next. Three next_action values map to five job states; here is the full table and a TypeScript switch.

Sume /v1/videos says cancelled, /v1/jobs says canceled: a status map
Two spellings and two vocabularies for the same Sume job: pending to cancelled on /v1/videos, queued to canceled on /v1/jobs. A table and a TypeScript mapping.

Move an OpenRouter-style video client to Sume: the 400s to expect
Sume /v1/videos matches the OpenRouter video shape, but size, provider.options and seed return 400. The differences table and a Python asyncio polling client.

A Sume job failed: read GET /v1/jobs/:id/events, then act
The events endpoint is the first stop for a failed or canceled Sume job: the eight event names, what stays hidden, error categories, and a TypeScript reader.

Can polling hit the Sume read limit? 24 Pro jobs at 2 s use 6%
Polling every accepted job every 2 seconds uses 3.75% to 7.5% of a Sume plan's read budget. The arithmetic for Free, Pro, Startup and Scale, with the caveats.

Sume TTS: 20,000 characters or 1,200 seconds, which fails first?
At 15 characters a second, 20,000 characters is 1,333 seconds, so the 1,200-second audio cap trips first. Split near 15,000 characters per job.

Format run expires_at: a 90 minute ceiling for your Python poll loop
A Sume Format run receipt has an expires_at deadline: 90 minutes from creation, sooner if the run goes quiet. Use it as your loop ceiling, with Python backoff.

Check a GPT Image 2.5 size before you call it: a Python validator
Sume rejects GPT Image 2.5 sizes that break four rules: multiples of 16, edge 3840, ratio 3:1, 655,360 to 8,294,400 pixels. A Python check, run on 8 sizes.

Sume STT: omit duration_seconds and it reserves 1 minute, not 9
A 9-minute file is $0.09 of Sume STT, but without duration_seconds the reserve is 1 minute, $0.01. Send 540. The field takes 1 to 600 seconds.

Which Sume API routes work without a key? Six public routes
Six Sume routes need no API key, including GET /v1/catalog and GET /v1/health. What they return, how they are limited, and a Python preflight for CI.

Validate a cut list against video-trim's rules before you submit
Video trim has no unbilled check endpoint, so mirror its rules locally: 1800 s source, 0.2 s to 900 s output, one of end or duration, exact with output.

Stable idempotency keys for pause-cut trims: hash source, start, end
Derive each trim's Idempotency-Key from a hash of source, start, end and precision. A retried batch then returns the same jobs and bills 8 trims once.

Timeline body from a cut list in TypeScript: starts and coverage
Turn a cut list of source ranges into Timeline slots: starts are running sums rounded to 3 decimals, and 3 ranges of 3.55, 4.68 and 3.36 s make 11.59 s.

Trim 12 Shorts from one recording with asyncio and a semaphore
Cut 12 Shorts from one recording with asyncio and httpx: 12 trims at $0.02 is $0.24. A semaphore of 4 caps in-flight requests; the limit is your choice.

Inspect transcript words without start or end: skip them in cuts
Inspect transcript words can omit start or end. Treat an untimed word as unknown, and never cut across a gap that contains one: 0 of its time is safe to remove.

Pick the Sume video model in code: seconds, ratio, edit or swap
A 12-line Python function turns duration, aspect ratio, edit and swap needs into the models that can run the job. A 4 s 1:1 clip has none; a 12 s one has two.

The resolution enum has 8 values, but each Sume model takes 3 or 4
The /v1/videos resolution enum lists 360p to 4K. Wan, Seedance, H3, H3 Max and Omni each take a subset. Check supported_resolutions and what each extreme costs.

Build the pricing_skus key from the resolution string: 4K is uppercase
Omni Flash 1.1 rates sit at per-video-second-360p, -720p, -1080p and -4K. Build the key from a template, mind the 4K case, total 10 s from $0.375 to $3.75.

Per-request timeout on Sume video polls: 15 s abort, then poll again
Give every poll GET its own 15 s AbortSignal.timeout so one hung read does not freeze a 30 s Wan or Seedance job loop. Reads per job per hour at 5, 10, 30 s.

TTS 1.0 rejects a model field: use the TTS router to pick sonic-3.6
Sume TTS 1.0 does not accept model. To choose an engine such as sonic-3.6 use /v1/tts-router/generate. Price is the same $0.0475 per 1,000 characters.

CI guard: fail the build when a new Sume pricing_skus key appears
A node:test file fetches GET /v1/videos/models and fails when a model uses a pricing_skus key your cost code cannot total. Wan, Omni and H3 rates in the table.

MCP generate_video max_spend_usd for Wan 3.0: 480p, 720p, 1080p
What to put in max_spend_usd when an MCP client calls generate_video for Wan 3.0: $0.625 to $7.50 for 10 and 30 seconds. Includes the dry_run body.

usage.cost on a Sume video poll is nullable: guard it before you sum
The /v1/videos poll schema allows usage.cost to be null. Sum only completed jobs, keep unknowns apart in a ledger, and compare to the Wan 3.0 $3.75 estimate.

pricing_skus has three key shapes: one Python function for the total
GET /v1/videos/models returns per-video-second-<res>, per-video-second and per-1000-video-tokens. A Decimal function totals a 30 s clip and flags token pricing.

TypeScript: check 8:1 against /v1/images/models before you POST
A 20-line TypeScript guard that reads the aspect_ratio values for a Sume image model and refuses a ratio it does not list, instead of a 400 at request time.

Webhook and poll race on a Sume video: one SQLite insert decides
Keep polling as a backup to the job webhook. Dedupe on job_id with INSERT OR IGNORE so only one path downloads. 10 deliveries 30 s apart cover 270 s.

Alert when a pricing_skus rate moves: a nightly diff of the model list
Save GET /v1/videos/models rates each night and diff them. A one-cent-per-second move costs $0.30 on a 30 s clip and $300 over 1,000 of them. Python, 25 lines.

Compare GET /v1/balance micros with a clip total, in integers
Balance comes as USD micros and cents. A 30 s Seedance 720p clip is 17,334,000 micros, 1,733 cents rounded down. Integer compare, plus the expiring-soon fields.

Contract test for 1:4, 4:1, 1:8, 8:1: pytest on /v1/images/models
A 12-line pytest that fails CI if Sume stops listing the Nano Banana 2.1 strip ratios. It reads GET /v1/images/models, so it generates nothing.

Music router 400 negative_prompt_unsupported: the fix and its cost
A non-empty negative_prompt on Sume's music router returns 400 negative_prompt_unsupported. Move the exclusions into prompt; a rejected call is not billed.

Lyria 3.5 has no duration field: how to ask for a 45-second track
Sume's music router rejects duration and duration_seconds. Write the length into the prompt, with section timestamps, and pay a flat $0.125 per generation.

TTS sentence slices: segmentation, wav output and boundary_lead_ms 70
Sume TTS can return gapless sentence segments. Needs timestamps.words and wav or raw for per-sentence audio_url. A 900-character script costs $0.04275.

Audio job failed: read /events and /status before you retry
A failed TTS, STT or music job is terminal. Read GET /v1/jobs/{id}/events and status, fix the cause, then resubmit under a new key. A new TTS job bills again.

Re-base caption words after a trim: subtract actual_start_seconds
Caption words must use the trimmed file's clock. A word at 12.3-12.7 s becomes 1.9-2.3 s when the trim really started at 10.4, not 0.3-0.7 s.

Read your Sume plan from ratelimit-limit on a GET: 4,800 to 48,000
A GET /v1/balance returns ratelimit-limit. 4800, 12000, 24000 or 48000 reads a minute maps to Free, Pro, Startup or Scale, and a full Wan 720p queue reserve.

Omni reference tokens follow list order: swap list, swap meaning
<IMAGE_REF_0> is the first URL in reference_image_urls, so reordering the list rewires the prompt. A check function and an 8 s body, $1.00 at 720p on Sume.

Find callers still sending nano-banana-2 or gemini-3.1-flash-image
Sume runs the retired nano-banana-2 id as Nano Banana 2.1 and echoes your id back. A repo scan finds stale ids and gemini-3.1-flash-image before you migrate.

A failed Sume video poll has error as a string, not an error object
On /v1/videos, HTTP errors use {error:{code,message}} but a failed poll carries error as a string. Read both without a TypeError in Python and TypeScript.

sume/auto and aspect_ratio 8:1: name Nano Banana 2.1 instead
sume/auto does not tell you which family ran, and the repo gives it the GPT Image ratio list, which has no 4:1 or 8:1. Name google/nano-banana-2.1 for strips.