Bulk create returns 429: wait retry-after, resend with the same key
A 429 on a Sume bulk create means the write budget is spent. Wait retry-after, then resend with the same Idempotency-Key; the same payload cannot double-queue.

A 429 rate_limited on a Sume bulk create means your write budget for the minute is spent. Wait the retry-after seconds, then resend the same body with the same Idempotency-Key. If the first request had in fact gone through, the same key and the same payload returns 202 with the existing queue instead of making a second one.
What the API tells you
A 429 names the budget in error.details.scope (read or write) and adds retry-after. Bulk create spends the write budget; polls spend the read budget, which is separate and forty times larger. Each response carries ratelimit-limit, ratelimit-remaining and ratelimit-reset.
| Response | Action |
|---|---|
| 429 rate_limited, scope write | Wait retry-after, resend with the same key |
| 503 studio_agent_upstream_unavailable | Retry later; a Sume-side outage |
| 409 idempotency_conflict | You changed the payload under a spent key; use a new key for a new batch |
| 202 on a replay | The old queue; read its id and carry on |
A safe retry loop
Keep the key stable across retries of one batch, and mint a new one only when you intend a new batch.
- Generate the key from the batch identity, such as
holiday-2026-10-06-batch-3, not fromuuidgeninside the retry loop. - Honor
retry-afterinstead of a fixed sleep, and cap the number of retries. - After a
202, save the queue id before you do anything else.
Why the same key is safe
A replay of the same key with the same { concurrency, items } returns 202 and the queue that already exists. The key is scoped to one Format, and the docs say the queue adds no extra lock, so a second identical request cannot start a second set of children.
A different payload under the same key is 409 idempotency_conflict, with details.queue_id naming the original. That is your signal that the retry loop rebuilt the body with a change, which is usually a bug.
Remember that a 429 can also hit a poll. That one comes from the read budget and does not mean the queue failed; back off and poll again.
Tradeoff
A replay returns 202 and the queue object has no idempotency_hit flag, so you cannot tell a fresh queue from an old one by the status. Compare created_at or your own records if it matters.
Sources
Related posts
More in Developers
- BullMQ delayed job that polls an AI video job and reschedules itself
A BullMQ worker reads Sume's job status once, then adds the next poll with a delay from next_poll_after_seconds, so no worker slot is held while a clip renders.
- A calendar file for AI model shutdown dates: .ics from Python
Generate an .ics file with all-day events and 14-day reminders for the gpt-image-1 and gpt-image-1.5 shutdown dates, then import it into any calendar app.
- Cancel a queued transcription job: what the 409 means
POST /v1/jobs/{id}/cancel works only before work starts. After that it returns 409 job_generation_already_started and the job finishes and bills normally.
- Abort a waitForJob wait with AbortSignal: the Sume job keeps running
Passing signal to waitForJob stops your wait and aborts the in-flight request. It does not cancel the job, which keeps running and billing. Keep the job id.
Written by Sume