Bulk queue, empty wallet: failed items, fresh key, check balance
If the wallet runs out during a bulk run, later child runs fail admission and become failed items. Check the balance, then resubmit with a fresh key.

What happens
A Sume bulk run returns 202 once the queue is accepted. After that, each child run still has to pass admission, including the wallet check. If the wallet runs short mid-run, those children become failed items that carry the create-run error, and the queue's window refills with the next items. The queue ends completed once every item is terminal, and counts.failed tells you how many fell out.
Nothing about this is silent if you read the counts. A queue marked completed with failed items is an expected outcome, not a contradiction.
Read, top up, resubmit
The table gives the sequence.
| Step | Call | Why |
|---|---|---|
| 1. Read the queue | GET /v1/format-run-queues/{id} | See counts.failed and which items failed |
| 2. Check funds | GET /v1/balance | Confirm there is enough before retrying |
| 3. Pick failed rows | From the queue items | Do not resend the ones that succeeded |
| 4. New queue | POST .../bulk-runs with a new Idempotency-Key | A replayed key returns the old queue |
The fresh key rule
Reusing the original Idempotency-Key for the resubmit is the common mistake. A replayed key returns 202 with the old queue, so you get no new work and may think the retry succeeded. Build the new key from the batch and the retry round, for example launch-batch-7-retry-1.
Resubmit only the failed items. Items that completed already produced their output and their charge.
curl -sS https://api.sume.com/v1/balance \
-H "Authorization: Bearer $SUME_API_KEY" | jq .Avoid it next time
Compare the worst-case ceiling to the balance before you start: items times the per-item cap. For 100 items at a $20 cap that is $2,000, an upper bound, not a forecast. Top up to the level you are comfortable with, or lower generation_spend_cap_usd per item.
Failed items have a generic format_run_failed on the queue, so read each child receipt for the true reason before you retry. A retry for an error that is not about funds will fail again.
- Check
GET /v1/balancebefore big queues. - Keep concurrency modest, so a short wallet stops fewer runs in flight.
- Alert on
counts.failedgreater than zero.
Worked example
Say you queued 150 rows as two queues, and the wallet ran dry after 120 children finished. The first queue completes with counts.failed at zero. The second shows 30 failed, each with the create-run error. Check the balance, top up, build a new batch from those 30 rows and send it with a key like spring-retry-1. You pay for 30 runs, not 150.
- Never retry the whole queue.
- Record which rows came from which attempt.
- Treat these as working notes you can adapt: the figures are from the Sume docs read on 2026-10-08, and the arithmetic is yours to rerun with your own numbers.
Make it routine
Add this check to your runbook and run it on a schedule, not only after an incident. The cost is a few read requests, and the rate limits are far above what it needs: even the Free plan allows 120 writes and 4,800 reads per minute. Keep the output with the date, so you can show later what the system looked like when a question came up.
Sources
Related posts
More in Formats
- Bulk queue worst case: 100 items x per-item cap, $300 not $40,000
Each child in a Sume bulk queue is a run with its own spend cap. 100 items at $3 cap at most $300; the default $400 cap would allow $40,000. Set caps per item.
- Cancel a Sume bulk queue: there is no queue cancel, so cancel children
Sume's API has no cancel-queue endpoint. Cancel each child with POST /v1/format-runs/{run_id}/cancel; the item frees its slot, and you pay for what already ran.
- Edited one ad hook and resent the bulk run? Same key gives 409
Reusing an Idempotency-Key with a changed Sume bulk-run payload returns 409 idempotency_conflict. Send only the edited hook under a new key.
- Format run incomplete_assembly: continue it, do not pay twice
incomplete_assembly means the run hit its time limit with generation jobs unfinished. Read pending_job_count, then continue with previous_run_id.
Written by Sume