Node https.request: POST /v1/images on Sume, no fetch, no packages
Call Sume's image API from Node using only node:https: JSON body, bearer key, 40-second timeout that actually aborts, and 200 versus 202 handling.

Use node:https on runtimes or old code bases where fetch is not available.
The request is the same in every language: POST https://api.sume.com/v1/images with a bearer key from SUME_API_KEY, a JSON body with model, prompt and aspect_ratio, and a client timeout above the route's 30-second wait. The route defaults to mode: "sync", so the status code decides what you do next (docs read 2026-10-07):
Status codes to branch on
| Status | Meaning | What the code below does |
|---|---|---|
| 200 | Image finished inside the wait; data[].url holds the file | Prints the result |
| 202 | Wait expired (or mode is async or webhook); body is a job envelope with status_url and result_url | Prints the envelope; poll status_url and read result_url |
| 502 | The job failed inside the wait; error has code, retryable, next_action | Prints the error |
| 400, 404 | unsupported_parameter, or model_not_found | Prints the error |
Node.js code
Save as nh.mjs and run SUME_API_KEY=... node nh.mjs. It uses only the built-in node:https module.
import https from "node:https";
const key = process.env.SUME_API_KEY ?? "";
const body = JSON.stringify({
model: "bytedance-seed/seedream-5-lite",
prompt: "matte ceramic mug on a white sweep, soft shadow",
aspect_ratio: "16:9",
});
const req = https.request(
"https://api.sume.com/v1/images",
{
method: "POST",
timeout: 40000,
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
},
},
(res) => {
let text = "";
res.on("data", (c) => (text += c));
res.on("end", () => {
if (res.statusCode === 200) console.log("done:", JSON.parse(text).data[0].url);
else if (res.statusCode === 202) console.log("queued, follow status_url:", text);
else console.log("error", res.statusCode, text);
});
},
);
req.on("timeout", () => req.destroy(new Error("client timeout")));
req.end(body);Notes
A timeout option on https.request only emits an event. It does not abort the request, so the handler calls req.destroy().
Branch on res.statusCode. A 202 carries a job envelope; read the finished image from its result_url once the job completes.
One bytedance-seed/seedream-5-lite image is $0.04375 billed (list $0.035 x 1.25). Prices here are Sume's list-times-1.25 figures. The catalog states the billable formula as "list × 1.25 → ceil usd cents", so treat the dollar amounts as the pre-rounding value and read the exact charge from billable_amount_usd_micros in the submit envelope. Failed or cancelled generations are not billed.
Sources: Sume Image API docs and Jobs and results (read 2026-10-07).
Sources
Related posts
More in Developers
- Omni draft grid: four 360p variants, then one final. What it costs
Google's Draft Room idea, run through the Sume API: four 8-second 360p drafts that change one thing each, then a 1080p final. Total $2.70, with a script.
- One Sume webhook signature, three languages: a shared test vector
A fixed secret, timestamp and body that must sign to the same sume-v1 value in Python, Node and Go. Use it to test a verifier in any language you add.
- Agents SDK cache_tools_list: stale tools after you grant Sume Write
With cache_tools_list on, an OpenAI Agents SDK MCP server can keep an old tool list. After a Sume Write grant, call invalidate_tools_cache() to see paid tools.
- X-OpenRouter-Idempotency-Key vs Sume webhook dedupe on job_id
OpenRouter's webhook dedupe key is job_id plus status. Sume says dedupe on job_id. A SQLite sample builds a job_id plus event key that skips replays.
Written by Sume