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.

Put a 15 s AbortSignal.timeout on every poll of GET /v1/videos/{id}, and treat an abort as one missed poll, not as a failed job. A polling loop with no per-request timeout can hang on one stalled socket while a 30 s clip finished minutes ago.
What a timeout does and does not mean
An aborted poll tells you nothing about the job. The job keeps running on Sume's side and your reservation stays in place, so the right reaction is to wait and poll again, never to resubmit. The docs say the same for submits: do not resubmit a paid job because a local client timed out.
Polls are reads, and reads have the large budget: 4,800 a minute on Free and 12,000 on Pro. Cadence is a latency choice, not a budget problem, for a handful of jobs.
| Interval | Reads per hour | Reads per minute | Worst-case lag after completion |
|---|---|---|---|
| 5 s | 720 | 12 | 5 s |
| 10 s | 360 | 6 | 10 s |
| 30 s (docs sample) | 120 | 2 | 30 s |
The loop
Stop on any terminal status: completed, failed or cancelled. The loop below also caps total wait at 20 minutes and returns the last status rather than throwing.
const API = "https://api.sume.com";
const headers = { Authorization: `Bearer ${process.env.SUME_API_KEY}` };
const DONE = new Set(["completed", "failed", "cancelled"]);
export async function waitForVideo(id: string, everyMs = 10_000) {
const deadline = Date.now() + 20 * 60_000;
let last = "unknown";
while (Date.now() < deadline) {
try {
const res = await fetch(`${API}/v1/videos/${id}`, {
headers,
signal: AbortSignal.timeout(15_000),
});
if (res.ok) {
const job = await res.json();
last = job.status;
if (DONE.has(last)) return job;
}
} catch {
// aborted or network error: one missed poll, not a failed job
}
await new Promise((r) => setTimeout(r, everyMs));
}
return { id, status: last, timedOut: true };
}Gotchas
A 429 on a poll names the budget in error.details.scope and carries a retry-after header; honor it instead of the fixed interval. The OpenAPI status enum also lists expired, which the docs poll table does not describe, so add it to your terminal set only if you want to be defensive.
Returning timedOut leaves the job running. Look it up again by id later, or move to a webhook for clips this long.
Sources
Related posts
More in Developers
- Agent Completion webhook retries: 10 attempts over about 3 hours
Sume tries an agent.run.terminal webhook up to 10 times. By the documented formula the nine waits add up to about 3 h 3 min, before jitter and Retry-After.
- 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.
- Video fields Sume rejects: size, seed, provider.options, audio off
Sume's /v1/videos refuses size, seed and non-empty provider.options on every model, and Omni refuses generate_audio false. What to send in their place.
- AI voiceover too loud: three Sume gain knobs and what each one costs
TTS generation_config.volume (0.5-2), Timeline audio.gain_db (-60 to 12) and soundtrack.duck_db (0-20). Which to change, and which means paying for new audio.
Written by Sume