Python requests Retry on POST: safe for Sume only with a key
urllib3 Retry skips POST by default. Allow it for a Sume submit only when every request carries an Idempotency-Key, and retry just 429 and 503.

The default protects you
In urllib3 the Retry class retries only methods it considers idempotent: DELETE, GET, HEAD, OPTIONS, PUT and TRACE. POST is left out on purpose, since replaying a POST can do the work twice. status_forcelist is off by default, and respect_retry_after_header is on.
A Sume paid submit is a POST that bills. That is exactly why Sume gives it an Idempotency-Key: a retry with the same key returns the original job and does not bill another.
Which statuses to retry
| Status | Code | Retry the submit |
|---|---|---|
| 429 | rate_limited | Yes, after retry-after, same key |
| 503 | provider_capacity_exceeded | Yes, later, same key |
| 402 | insufficient_credits | No, add funds first |
| 400 | invalid_request | No, fix the request |
Session setup
Allow POST in the Retry, but force the key on every call so the allowance is safe. raise_on_status is set to False so you get the final Sume error body instead of a RetryError. It needs the requests package.
import os, uuid, requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=3, backoff_factor=1, status_forcelist=(429, 503),
allowed_methods=frozenset({"GET", "POST"}),
respect_retry_after_header=True, raise_on_status=False,
)
s = requests.Session()
s.mount("https://", HTTPAdapter(max_retries=retry))
s.headers["Authorization"] = "Bearer " + os.environ["SUME_API_KEY"]
def submit(body, key=None):
r = s.post(
"https://api.sume.com/v1/image-1.0/generate",
json={**body, "mode": "async"},
headers={"Idempotency-Key": key or str(uuid.uuid4())},
timeout=30,
)
return r.status_code, r.json()The one rule
A fresh uuid per call, as in the default above, protects only within that call's own retries. If your process may crash and run the submit again, derive the key from your business item (an order id plus a version) so the second run lands on the same job. Reusing a key with a different payload returns 409 idempotency_conflict, so build the key and the body from the same inputs.
Sources
Related posts
More in Developers
- Flag near-identical openings in your last 20 Short scripts
A short Python script that compares the first sentence of each Short script and flags pairs that read alike, a cheap check against templated AI Shorts.
- Python and SQLite: log every video job's usage.cost by model
A 13-line stdlib script stores each completed job's usage.cost in SQLite, ignores duplicates by job id and prints spend per model. Seedance 2.5 5 s is $2.89.
- Python receiver for a Sume /v1/videos callback_url, signature checked
A standard-library Python webhook receiver for Sume video jobs: checks x-sume-webhook-signature, refuses an empty secret, rejects stale timestamps.
- Python Sume webhook handler that accepts the webhook.test event
Verify the sume-v1 signature over timestamp.body, refuse an empty secret, and accept webhook.test, which has no job_id. Stdlib Python, runs offline.
Written by Sume