Sume 429: retry-after header first, then the body field, then backoff
How long to wait after a Sume 429? Use the retry-after header, then error.retry_after_seconds, then jittered backoff. A Python stdlib function with checks.

After a Sume 429, wait for the retry-after response header first. If it is missing, use error.retry_after_seconds from the JSON body when it holds a number. If neither is usable, fall back to exponential backoff with jitter and a cap. The authentication docs define retry-after as the seconds to wait, sent on a 429, and the errors page describes retry_after_seconds as how long to wait before repeating a request that can succeed.
Reading both sources matters because they are not always both present. The rate limiter documents the header. Other retryable errors, such as a run that has not finished, document the hint in the error envelope, and that field can be present with a null value. A client that reads only one of them either hammers the API or waits for no reason.
The order and why
The header is the most direct signal because it comes from the rate limiter itself, so it wins. The body value is the next best, since it is a server hint tied to that specific error. Backoff is the last resort and should be jittered so that many workers do not retry in lockstep. Cap whatever you pick, because a hint in the minutes is not a reason to block a worker thread.
| Source | What it carries | Use when |
|---|---|---|
| retry-after header | Seconds to wait, sent on 429 | Present: always use first |
| error.retry_after_seconds | Recommended wait before repeating | Header missing and value is a number |
| error.retryable | Whether the same request can succeed later | Check before any retry |
| ratelimit-reset header | Rate-limit window reset | Pacing, not the 429 wait |
| Exponential backoff with jitter | Your own schedule | Neither hint is usable |
Steps
- Check
error.retryablefirst. A 402 or a validation error should not be retried at all. - Call the function below with the response headers, the parsed body and the attempt count.
- Sleep for the returned time, then repeat the identical request with the same
Idempotency-Keyon a submit. - Stop after a fixed number of attempts and surface the error with its
request_idto your logs.
Function and checks
The cases at the bottom cover a header in mixed case, a body hint with a useless reset header, an HTTP date that cannot be parsed as a number, and an empty response. It uses only the standard library, so it runs on current Python 3 releases; we ran it on 3.14.7, not on the 3.15 release candidate.
import random
def retry_delay(headers: dict, body: dict, attempt: int, cap: float = 60.0) -> float:
"""Seconds to wait before repeating a 429/503. Header first, then body, then backoff."""
lowered = {k.lower(): v for k, v in headers.items()}
for raw in (lowered.get("retry-after"), (body.get("error") or {}).get("retry_after_seconds")):
try:
if raw is not None and float(raw) >= 0:
return min(float(raw), cap)
except (TypeError, ValueError):
continue # an HTTP-date or junk value: fall through to the next source
return min(cap, 2.0 ** attempt) * random.uniform(0.5, 1.0)
cases = [
({"Retry-After": "7"}, {}, 1),
({"ratelimit-reset": "30"}, {"error": {"retry_after_seconds": 12}}, 1),
({"retry-after": "Wed, 21 Oct 2026 07:28:00 GMT"}, {"error": {"retry_after_seconds": None}}, 3),
({}, {}, 10),
]
for headers, body, attempt in cases:
print(round(retry_delay(headers, body, attempt), 1))What Sume does not do
Sume does not promise that a retry will succeed after the hinted wait; if a queue is still full, you will get another 429. It also does not send an HTTP date in retry-after in anything we read, which is why the function treats an unparsable value as missing instead of trying to parse dates.
Sources
Related posts
More in Developers
- How many images can I attach to a Sume agent run? 30, up to 500 MB
An Agent Completion takes at most 30 images, each up to 30 MB, 500 MB total. So 30 images of 30 MB cannot all fit; 16 can. Error codes inside.
- Which Sume key scope reads or rotates the webhook signing secret?
Reading the webhook signing secret needs account:read; rotating needs account:write. Scopes are fixed at key creation, so use a separate key.
- sume/auto defaults to 720p and 8s: 250 old prompts, pinned vs Auto
With model sume/auto a request defaults to 720p and 8 seconds. Pinned, 250 such clips cost $150 to $605 on Sume. Auto does not disclose the family.
- sume/auto and idempotent retries: same price, same route on replay
New video models land weekly. With model sume/auto and an Idempotency-Key, a retry of a Sume video submit gets the original job, price and route.
Written by Sume