Rerun an image script without paying twice: prompt-hash keys
Derive the Idempotency-Key from the request body so a crashed script that reruns cannot create a second paid image job. Includes the 409 case when you edit.

Hash the request body and send the hash as the Idempotency-Key header, so running the same script twice sends the same key for the same image. Sume's OpenAPI describes the header as optional retry safety for paid job creation: the same key with the same operation and normalized payload returns the original job, and a different payload returns 409 idempotency_conflict.
That makes a crash-and-rerun loop safe without a database of what you already sent.
Why derive the key from the body?
A random key per run protects only a single retry inside one run. A key derived from the body protects across runs: loop item 37 of 60 gets the same key tomorrow, so the rerun reuses the job it already created for items 1 to 36 instead of paying for them again.
What does the code look like?
Serialize the payload with sorted keys, hash it, and keep the result inside the documented limits: printable ASCII, 255 characters at most.
import hashlib, json, os, requests
def image_key(payload):
blob = json.dumps(payload, sort_keys=True, separators=(",", ":"))
return "img-" + hashlib.sha256(blob.encode()).hexdigest()[:40]
payload = {
"model": "openai/gpt-image-2",
"prompt": "Product photo of a ceramic mug on a wooden table",
}
r = requests.post(
"https://api.sume.com/v1/images",
headers={
"Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
"Idempotency-Key": image_key(payload),
},
json=payload,
timeout=60,
)
print(r.status_code, r.json())What happens when I edit the prompt?
A new body gives a new hash and so a new key, which is what you want: a new image. The 409 appears only when you reuse one key by hand with a changed body; the error details name the job that holds the key so you can adopt it instead of recreating it.
What does this not cover?
It does not stop you generating the same prompt on purpose for a fresh variant. Add a counter to the payload, for example in metadata, when you want another take. The jobs docs cover retry rules in more detail.
Sources
Related posts
More in Developers
- Pub/Sub push subscription for Sume events: ack codes and dedupe
Pub/Sub push redelivers on any code outside 102, 200, 201, 202 and 204. Fan Sume completions through Pub/Sub safely with run_id and job_id dedupe.
- Python 3.15 TaskGroup.cancel: stop at the first Sume job done
Python 3.15 adds TaskGroup.cancel. Watch several Sume jobs and stop the other watchers when the first one completes, without cancelling the paid jobs.
- Python asyncio.timeout around a Sume job poll: a hard budget
Wrap a Sume status loop in asyncio.timeout so it stops at a fixed budget and returns still_running, leaving the job alone. A short version, run against a mock.
- Python httpx and asyncio: submit and poll a Sume image job
A runnable Python recipe: submit a Sume image job in async mode with an Idempotency-Key, poll with httpx and asyncio, honor retry-after, fetch the artifacts.
Written by Sume