Retry hints in the body or a header: reading Sume's Retry-After
Notion repeats Retry-After in response bodies. Sume can send a retry-after header on 429s. Here is how to retry each Sume error code safely.

Sume's public API responses can include a retry-after header alongside ratelimit-limit, ratelimit-remaining and ratelimit-reset. When you get a 429, wait for retry-after if it is present. Retry only calls that are safe, which for paid submits means sending the same Idempotency-Key.
What Notion changed
The Notion changelog entry for September 24, 2026 says any 429 or 529 response with a Retry-After header now repeats the wait in the body as additional_data.retry_after, so clients that cannot read headers, such as MCP clients, still see it. It also says page write timeouts now return 504 instead of 500. The lesson for any client is that the retry hint can live in a body or a header depending on the service, so read both.
Where Sume puts it
Sume's error body is a JSON envelope with code, message, request_id and details. The wait hint for HTTP rate limits is in the response headers. Failed jobs also expose retry metadata in their public error: category, stage, retryability, retry-after seconds, public reason and next action.
What to retry
Retrying everything is wrong, and so is retrying nothing.
| Status and code | Retry? | Rule |
|---|---|---|
400 invalid_request | No | Fix the request |
402 insufficient_credits | No | Add funds or lower cost |
429 rate_limited | Yes | Back off, use retry-after when present |
429 queue_full | Later | Wait for an existing job to finish or cancel |
503 provider_capacity_exceeded | Yes | Retry later with the same idempotency key |
503 provider_not_configured | Not aggressively | Check catalog and runtime status |
A retry helper
This helper retries only rate_limited and provider_capacity_exceeded, honors a numeric retry-after, and falls back to exponential backoff. It reuses one idempotency key so a replay returns the original job. queue_full and provider_not_configured are returned to the caller.
import os, time, uuid
import requests
def submit(payload, tries=5):
key = str(uuid.uuid4())
headers = {
"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Idempotency-Key": key,
}
for attempt in range(tries):
r = requests.post("https://api.sume.com/v1/videos", json=payload, headers=headers, timeout=60)
try:
code = r.json()["error"]["code"]
except (ValueError, KeyError, TypeError):
code = None
if code not in ("rate_limited", "provider_capacity_exceeded"):
return r
ra = r.headers.get("retry-after", "")
wait = float(ra) if ra.replace(".", "", 1).isdigit() else 2 ** attempt
time.sleep(wait)
return rDo not resubmit a paid job on a timeout
If your own process times out, poll the job instead of submitting again. A queue_full response is not a rate limit: it means the workspace cannot take another paid job until a queued or processing one finishes or is canceled.
Sources
Related posts
More in Developers
- Expiring API keys: Sume key metadata and rotation habits
OpenAI added enforced key lifetimes in Sep 2026. Sume's docs list key id, name, prefix, scopes and last-used time, so rotate on a schedule you keep.
- What to save from a Sume run when batch results expire at 30 days
OpenAI keeps batch output 30 days, Anthropic 29, Gemini 6 weeks. Which Sume run receipt fields to store so your records outlive any vendor retention window.
- isTerminalJobStatus vs isTerminalRunStatus: skipped only ends runs
The Sume SDK has two terminal checks. Jobs end on completed, failed or canceled; runs also end on skipped. Reusing one for both breaks a custom poll loop.
- Seedance 1.5 Pro retires Nov 11: pin a live Sume model id
ElevenLabs says ByteDance retires Seedance 1.5 Pro on Nov 11, 2026. Pin an id Sume's video catalog lists today and verify its limits first.
Written by Sume