Sora download_content vs Sume /content: the 302 and two 409 errors

OpenAI's content endpoint took variant=video, thumbnail or spritesheet. Sume's /content redirects with a 302 and returns two different 409s. How to branch.

5 min readSume
All posts

On Sume, GET /v1/videos/{id}/content does not stream bytes: it answers 302 with a Location pointing at the artifact, accepts ?index=0 to pick one output, and needs your API key on the request to /content itself. If the job is not done it answers 409 job_not_completed, which you retry; if the job died it answers 409 job_failed, which you do not.

That replaces OpenAI's download_content(video_id, variant=...), which the guide (read 2026-10-08) describes with three variants: video, thumbnail as webp, and spritesheet as jpg, with download URLs valid for at most one hour.

Side by side

The practical difference is that Sume has one output per index, not three variants of one asset. Stills and sprite sheets are a separate job, covered in the thumbnail and spritesheet replacement post.

Content endpoints compared, vendor docs read 2026-10-08
QuestionOpenAI guideSume docs
Which filevariant: video, thumbnail, spritesheetindex: 0 by default
Response shapebytes through the SDK helper302 redirect to the artifact URL
Not ready yetpoll status until completed409 job_not_completed, retryable
Job failedstatus failed409 job_failed, not retryable
Auth on the content callAPI keyAPI key (Bearer or x-api-key, one of them)

A fetch that branches correctly

With Node 18 or later, set redirect to manual so you see the 302 and can decide what to do with the Location. Run it as an ES module with SUME_API_KEY set and a real job id in JOB_ID.

const base = "https://api.sume.com";
const headers = { authorization: `Bearer ${process.env.SUME_API_KEY}` };

async function artifactUrl(jobId) {
  const res = await fetch(`${base}/v1/videos/${jobId}/content?index=0`, {
    headers,
    redirect: "manual",
  });
  if (res.status === 302) return res.headers.get("location");
  if (res.status === 409) {
    const body = await res.json();
    const code = body?.error?.code;
    if (code === "job_not_completed") return null;
    throw new Error(`job failed: ${code}`);
  }
  throw new Error(`unexpected ${res.status}`);
}

const url = await artifactUrl(process.env.JOB_ID);
console.log(url ?? "not ready, poll again");

Notes on the error body

The snippet reads error.code, the field the API's OpenAPI text names for both 409 answers, and error.message carries the same public reason the poll response reports. A job_failed answer also carries retryable false. In Node, manual redirect mode returns the 302 response with its headers readable, unlike a browser, where the response is opaque.

Do not hand the Location URL to your users as a permanent link. Copy the file to your own storage after the first successful fetch, as the post on one-hour URLs argues.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume