Sume 429: read error.details.scope, and rate_limit_unavailable
A Sume 429 names the budget in error.details.scope and gives retry_after_seconds. A degraded rate_limit_unavailable 429 is a different case. Python handler.

Three different 429s
A Sume 429 is not one thing. rate_limited means your key spent its per-minute budget. queue_full means the workspace has no accepted generation capacity left. And rate_limit_unavailable means the rate limiter itself is degraded and Sume cannot count your requests right now.
Each wants a different reaction, and the error body tells you which: error.code first, then error.details.
What rate_limited carries
From the API source, a rate_limited body has details.limit, remaining (0), scope, window_seconds and retry_after_seconds. The scope is read or write. The retry-after header carries the same wait in seconds. Because reads and writes have separate budgets, a read-scope 429 on a status poll tells you your submits are untouched.
| error.code | Meaning | Reaction |
|---|---|---|
| rate_limited | Per-key budget spent, scope read or write | Sleep retry-after, then resume |
| queue_full | Workspace accepted-job capacity full | Wait for jobs to finish or cancel queued ones |
| rate_limit_unavailable | Rate limiting degraded | Retry later; nothing you did caused it |
A handler you can run
This function takes the status and the raw body and returns the decision. It sleeps nothing itself, so you can unit-test it.
import json
def decide(status, body):
if status != 429:
return ("other", 0)
err = json.loads(body).get("error", {})
d = err.get("details") or {}
wait = d.get("retry_after_seconds") or 5
code = err.get("code")
if code == "queue_full":
return ("drain-queue", 0)
if code == "rate_limit_unavailable":
return ("retry-later", wait)
return ("sleep-" + str(d.get("scope", "unknown")), wait)
sample = '{"error":{"code":"rate_limited","details":{"scope":"read","retry_after_seconds":12}}}'
print(decide(429, sample))Retry safety
Do not retry a paid submit after a 429 without an Idempotency-Key. With the same key, a retry returns the original job instead of billing a second one. For queue_full, the docs say to retry with the same key after capacity opens.
Sources
Related posts
More in Developers
- Sume schedule run 403: a service-account key cannot start action runs
Starting a Sume schedule run with a service-account key fails with 403 insufficient_scope and service_account_action_runs_unsupported. Use a user API key.
- Why sume/auto returns 400 for 21:9, not a Seedance route
sume/auto accepts 3 to 10 s in 16:9 or 9:16. Ask for 21:9 or 15 s and you get 400 unsupported_capability, not a quiet reroute to Seedance.
- sume/auto vs a pinned video model: three jobs where each wins
sume/auto is right for an 8-second 720p clip at $1.00. A 12-second or 20-second clip needs a pinned id, because auto resolves to Omni with a 10-second cap.
- Sume CLI avatar-videos batch: plan, create, watch, result
The Sume CLI batches avatar videos in four steps against local state files. What each step does, and why the per-item idempotency key makes reruns safe.
Written by Sume