Node 22.23.3: pace Sume status polls with ratelimit headers
Read ratelimit-remaining, ratelimit-reset and retry-after from every Sume response in Node 22.23.3 and slow a wave of job polls before a 429 appears.

Short answer
Read ratelimit-remaining and ratelimit-reset on every Sume response, sleep for retry-after on a 429, and slow down when the remaining count gets low. Sume's authentication page says to read the header rather than count requests yourself, because the headers describe whichever budget the current request spent from.
In Node 22.23.3, which the release notes date to 23 September 2026 and which bundles Undici 6.28.1, fetch exposes those headers through res.headers.get(...) with no extra library.
The budgets
Reads and writes have separate budgets, so a tight status-poll loop cannot 429 your own submits. A read is any GET or HEAD; the plan number is the write number, and reads get forty times that in their own bucket.
| Plan | Writes per minute | Reads per minute |
|---|---|---|
| Free | 120 | 4800 |
| Pro | 300 | 12000 |
| Startup | 600 | 24000 |
| Scale | 1200 | 48000 |
What the headers mean
A 429 names the exhausted budget in error.details.scope, either read or write. A 429 with code queue_full is a different thing: workspace generation concurrency plus queue capacity is full, which a faster or slower poll does not change.
| Header | Meaning |
|---|---|
| ratelimit-limit | requests allowed in the current window |
| ratelimit-remaining | requests left in the current window |
| ratelimit-reset | seconds until the window resets |
| retry-after | seconds to wait, sent on 429 |
A paced wave poller
Run node wave.mjs job_a job_b job_c. It reads each unfinished job once per round, sleeps two seconds between rounds, and sleeps until the window resets if fewer than 20 requests remain. The 20 is a safety margin you can tune.
const key = process.env.SUME_API_KEY;
const sleep = (s) => new Promise((r) => setTimeout(r, s * 1000));
async function read(id) {
for (;;) {
const res = await fetch(`https://api.sume.com/v1/jobs/${id}/status`, {
headers: { Authorization: `Bearer ${key}` },
});
if (res.status === 429) { await sleep(Number(res.headers.get("retry-after")) || 1); continue; }
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
if (Number(res.headers.get("ratelimit-remaining")) < 20) {
await sleep(Number(res.headers.get("ratelimit-reset")) || 1);
}
return (await res.json()).data;
}
}
const ids = process.argv.slice(2);
const done = new Map();
while (done.size < ids.length) {
for (const id of ids.filter((i) => !done.has(i))) {
const s = await read(id);
if (s.terminal) done.set(id, s.sume_status);
}
await sleep(2);
}
console.log(Object.fromEntries(done));What Sume does and does not do
Sume gives polls their own, larger budget and reports it in headers. Over MCP, a jobs_status poll spends no write budget at all, and a tool call spends the write budget once for the run it creates.
Sume does not raise generation concurrency when you raise your request rate; that is governed by the plan's concurrency limit, reported on the generation_limits object. Unauthenticated requests are limited per client IP, with a read bucket of four times the write rate, so always send the key.
Sources
Related posts
More in Developers
- Node 22.23.3 fetch: retry a Sume submit with one Idempotency-Key
Node 22.23.3 LTS bundles Undici 6.28.1. A fetch submit loop that retries 429 and 5xx with one Idempotency-Key so a retry never bills twice.
- Node-RED http request: node headers overwrite msg.headers (Sume key)
Node-RED's http request node lets node-configured headers overwrite msg.headers. Keep Sume's one credential header in the node, per-call headers in the msg.
- Node-RED http request node: submit a Sume job and branch on statusCode
Node-RED's http request node returns payload, statusCode and headers. Wire a Sume submit, branch on 202 vs errors, and keep the job id for the status read.
- og:image width, height and alt tags for an AI image from Sume
Open Graph has optional og:image:width, og:image:height and og:image:alt. Read the real size from a Sume render with Pillow and print all three tags.
Written by Sume