Seedance 2.5 estimate vs usage.cost: reconcile a job in Node
Compute the Seedance 2.5 price from width, height and seconds, submit one job to Sume, and compare it with usage.cost on the poll. A runnable Node 18 script.

To check that your Seedance 2.5 price math matches what Sume charges, compute the estimate before you submit, then read usage.cost from the poll response and compare. For an 8-second 16:9 clip at 720p the estimate is $4.63: 172,800 video tokens x $0.0214 per 1,000 x 1.25, rounded up to the cent. The script below does the whole check in Node 18 or newer.
usage.cost is the Sume billable amount on the poll response, per the video docs (read 2026-10-07). Run it once per resolution you plan to use at volume, and keep the output with your billing notes.
The script
Save it as reconcile.mjs (top-level await needs an ES module) and run SUME_API_KEY=... node reconcile.mjs. It submits one real job, so it spends about the estimate.
const key = process.env.SUME_API_KEY;
if (!key) throw new Error("Set SUME_API_KEY");
const headers = { Authorization: `Bearer ${key}`, "Content-Type": "application/json" };
const seconds = 8;
const tokens = Math.ceil((1280 * 720 * seconds * 24) / 1024);
const estimate = Math.ceil((tokens / 1000) * 0.0214 * 1.25 * 100) / 100;
const res = await fetch("https://api.sume.com/v1/videos", {
method: "POST",
headers,
body: JSON.stringify({
model: "seedance-2.5",
prompt: "A paper boat drifts down a rain gutter, camera follows at water level",
duration: seconds, resolution: "720p", aspect_ratio: "16:9",
}),
});
if (!res.ok) throw new Error(await res.text());
const job = await res.json();
const deadline = Date.now() + 20 * 60 * 1000;
while (Date.now() < deadline) {
await new Promise((r) => setTimeout(r, 15000));
const s = await (await fetch(job.polling_url, { headers })).json();
if (s.status === "completed") {
console.log({ estimate, billed: s.usage.cost });
process.exit(0);
}
if (s.status === "failed" || s.status === "canceled" || s.status === "cancelled") throw new Error(s.error ?? s.status);
}
throw new Error("timed out waiting for the job");What to change, and when the numbers differ
The rate in the script (0.0214 per 1,000 tokens) is the provider list rate for seedance-2.5 at 480p and 720p; 1080p uses 0.0234. If you change the resolution, change the pixel size and the rate together: 1920x1080 and 0.0234 for 1080p. The 24 is the pricing frame rate that Sume's estimator uses.
If billed ever differs from estimate, trust billed and compare the model's pricing_skus in GET /v1/videos/models. A reference video in the request changes the math (0.6x over the output plus an assumed 15 seconds), so this script does not apply to those jobs.
Polling and failure
The poll loop waits 15 seconds between calls and stops after 20 minutes. A 30-second clip is not slow enough to need more; if a job passes the deadline, check its status before you submit another, so you do not pay twice.
Sources
Related posts
More in Developers
- Semantic Kernel .NET defaults to gpt-image-1: it fails Oct 23
The .NET OpenAI connector in Semantic Kernel defaults to gpt-image-1, which OpenAI removes Oct 23, 2026. What breaks, and a C# call to Sume that does not.
- SHA-256 the batch body into the Sume idempotency key for bulk chunks
Derive each bulk chunk's Idempotency-Key from a hash of its items so replays reuse the queue and edited rows get a new key instead of a 409 conflict.
- Turn a shot list into Gemini Omni requests: Python on Sume
Submit every line of a shot list as its own Omni job with stable Idempotency-Keys, then poll them in one pass. Runnable Python and the cost of a 3-shot list.
- Should my backend call Sume over hosted MCP or the REST API?
REST from a backend, hosted MCP from an agent client. Where they differ: auth, wait limits, REST-only Image 1.0 and Video 1.0, and write budgets.
Written by Sume