MiniMax H3 Max in TypeScript: submit, poll and download with fetch

A TypeScript script under 30 lines: submit a minimax-h3-max job on Sume, poll until completed, save the MP4. Status values and the 409 on failed jobs.

5 min readSume
All posts

MiniMax H3 Max, id minimax-h3-max, takes 5 to 15 seconds at 480p, 768p or 1080p, with the 1080p tier a latent refinement of native 768p. On Sume it is an asynchronous job: submit returns a 202 with a polling URL, you poll until the status is completed, and then you download the file. This post is the whole loop in TypeScript, using only the built-in fetch in Node 18 or later.

What does the script look like?

Set SUME_API_KEY in the environment. The script submits a 5-second 768p job, polls every five seconds, and writes h3max.mp4. It checks the status and stops on failed instead of requesting content.

import { writeFileSync } from "node:fs";
const base = "https://api.sume.com/v1/videos";
const headers = {
  Authorization: `Bearer ${process.env.SUME_API_KEY}`,
  "Content-Type": "application/json",
};
async function main() {
  const body = JSON.stringify({
    model: "minimax-h3-max",
    prompt: "A paper boat drifting down a rainy street at dusk",
    duration: 5,
    resolution: "768p",
  });
  const res = await fetch(base, { method: "POST", headers, body });
  const { id } = await res.json();
  for (;;) {
    const r = await fetch(`${base}/${id}`, { headers });
    const job = await r.json();
    if (job.status === "failed") throw new Error("job failed");
    if (job.status === "completed") break;
    await new Promise((ok) => setTimeout(ok, 5000));
  }
  const file = await fetch(`${base}/${id}/content?index=0`, { headers });
  writeFileSync("h3max.mp4", Buffer.from(await file.arrayBuffer()));
}
main();

What do the statuses mean?

The poll response uses OpenRouter's names: pending for a queued job, in_progress for one that is processing, then completed or failed. The finished object lists unsigned_urls, and the usage.cost field is the Sume billable amount. A content request on a failed job is a 409 job_failed, which is why the loop stops first.

What would a real client add?

A timeout on the loop, a check of res.ok on the submit so a 402 insufficient_credits or a 400 is shown with its body, and an Idempotency-Key header on the submit so a retry returns the original job instead of billing a second. At 768p a 5-second H3 Max clip is $0.50 billable, so a retry bug is cheap, but at 1080p it is $1.00 and it adds up.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume