urllib3 Retry on POST: retry a Sume submit only with a key
A urllib3 Retry config that retries 429, 502, 503 and 504 on a Sume submit, and a wrapper that refuses to send a POST without an Idempotency-Key.

urllib3 does not retry POST by default, and that default protects you from double charges. To retry a Sume submit, add POST to allowed_methods on the Retry object and make your own wrapper refuse any POST that has no Idempotency-Key. With a key, a retry that reaches Sume returns the original job with idempotency_hit true instead of creating a second paid job.
Which statuses to retry
Retry only the statuses that mean try again. Do not put 402 or 400 in the list: those need a human or a code change.
| Status | Meaning | Retry with the same key? |
|---|---|---|
| 402 | insufficient_credits | No, add credits first |
| 409 | idempotency_conflict (same key, different body) | No, fix the key or the body |
| 409 | idempotency_key_in_use | Yes, after about 1 second |
| 429 | rate_limited or queue_full | Yes, wait for retry-after |
| 503 | provider_capacity_exceeded | Yes, the failed create released the key |
The client
The sample submits a 3-second Gemini Omni Flash 1.1 draft at 360p through the Video Router. At the billed 360p rate of $0.0375 per second (provider list $0.03 x 1.25), 3 seconds cost $0.1125. Set raise_on_status to False so you can read the final error body instead of catching MaxRetryError. The Retry-After header is honored by default.
import json, os, uuid
import urllib3
from urllib3.util import Retry
retry = Retry(total=3, backoff_factor=1.0, raise_on_status=False,
status_forcelist=[429, 502, 503, 504],
allowed_methods=frozenset({"GET", "POST"}))
http = urllib3.PoolManager(retries=retry, timeout=urllib3.Timeout(connect=5, read=60))
def submit(path, body, key):
if not key:
raise ValueError("refusing a POST without an Idempotency-Key")
r = http.request("POST", "https://api.sume.com/v1" + path,
body=json.dumps(body).encode(),
headers={"x-api-key": os.environ["SUME_API_KEY"],
"Content-Type": "application/json", "Idempotency-Key": key})
return r.status, json.loads(r.data)
status, out = submit("/video-router/generate", {
"model": "gemini-omni-flash-1.1", "prompt": "A slow pan over a ceramic mug",
"resolution": "360p", "duration": 3, "mode": "async"}, str(uuid.uuid4()))
print(status, out.get("data", out).get("request_id"))Keep one key per intent
Generate the key once per piece of work, before the first attempt, and store it with your own record. The wrapper above receives it as an argument for that reason: if a fresh uuid were made inside the retry loop, every retry would be a new paid job. When the process restarts, read the stored key and resubmit with it, or look the job up before you resubmit. The same rule covers a worker that crashes after the request leaves but before the response arrives: the key is what lets the second attempt find the first job instead of paying for another.
- Retry-After is read by urllib3 on 429 and 503 and overrides the backoff for that attempt.
- A 4xx other than 409 idempotency_key_in_use and 429 is not retryable.
- If all retries fail, the last response is still in your hands: log error.code and error.request_id.
- A client timeout does not cancel the job, so a read timeout can still leave a running job behind the key.
Sources
Related posts
More in Developers
- Sume video poll usage.cost: reserved while running, captured at end
The usage.cost number is the Sume billable amount: the reservation while a job runs and the captured amount once it settles. wan-3.0 at 720p, 10 s, as math.
- Validate a Gemini Omni Flash 1.1 request in Python before sending
A 28-line Python check for Sume's gemini-omni-flash-1.1 rules: 3-10 s, 10 reference images, 3 reference videos, no audio off, and edit mode exclusions.
- A Veo 3.1 call becomes a Sume Omni job in under 30 lines of Python
Replace a Veo 3.1 request with a Sume gemini-omni-flash-1.1 job: submit, poll every 30 seconds, download the mp4 and read usage.cost. Standard library only.
- Veo 3.1 previews end in 14 days: a dated checklist, Oct 8 to Oct 22
Google's three Veo 3.1 preview ids shut down on October 22, 2026. A day-by-day checklist from today, with the Sume model id and limits to test against.
Written by Sume