Fall back to a second image model after three 502s: a Python breaker
Sume returns 502 when an image job fails inside the wait budget. Count them, switch to a second model id after three, and use a new idempotency key per model.

Count consecutive 502 responses from POST /v1/images, and after three switch to a second Sume model id. Sume returns 502 when a job reaches a terminal failure inside the sync wait; a slow job returns 202 and is not a failure. A swap is a model-id string, so the fallback is a list of ids and an index.
Use a separate Idempotency-Key per model. The same key with a changed payload returns 409 idempotency_conflict, so a key that contains the model id keeps the retry legal.
The breaker
The sample is sticky on purpose: once it moves to the fallback it stays there until a human resets it, so a flapping provider cannot bounce traffic between two styles every few seconds.
import json, os, urllib.error, urllib.request
MODELS = ["openai/gpt-image-2.5-sunburst", "google/nano-banana-2"]
state = {"i": 0, "fails": 0}
def post(model, prompt, key):
req = urllib.request.Request(
"https://api.sume.com/v1/images",
json.dumps({"model": model, "prompt": prompt}).encode(),
{"Authorization": "Bearer " + os.environ["SUME_API_KEY"],
"Content-Type": "application/json", "Idempotency-Key": key})
with urllib.request.urlopen(req, timeout=60) as r:
return r.status, json.load(r)
def render(prompt, job_id):
model = MODELS[state["i"]]
try:
out = post(model, prompt, f"{job_id}:{model}")
state["fails"] = 0
return out
except urllib.error.HTTPError as err:
if err.code == 502:
state["fails"] += 1
if state["fails"] >= 3 and state["i"] < len(MODELS) - 1:
state["i"] += 1
state["fails"] = 0
raiseWhich errors count
| Response | Meaning | Counts toward the breaker |
|---|---|---|
| 200 | Images in data[].url | No, reset the counter |
| 202 | Job envelope, still running | No, poll status_url |
| 502 | Terminal failure inside the wait budget | Yes |
| 400 | Parameter the model does not list | No, fix the request |
| 404 model_not_found | Id unknown to Sume | No, fix the id |
| 409 idempotency_conflict | Same key, different payload | No, change the key |
What the fallback must not hide
A fallback changes the look of the output. Log the model id with each result, and alert when the index moves, so the switch is a decision someone made. Do not use sume/auto as the fallback if you need to know the model: it never discloses it, and job.model stays sume/auto. See the model-map post for the config side.
Sources
Related posts
More in Developers
- Four 16:9 thumbnail options in one call: n=4 cost, and why n=5 fails
Set n to 4 for four 1536x864 options in one GPT Image 2.5 call: about $0.0420 at medium on Sume. n=5 returns a 400, because every model caps at 4.
- Gemini CLI and the hosted Sume server: do not rely on env in headers
Add the hosted Sume server to Gemini CLI with httpUrl and a bearer header. Gemini expands env vars only in the env block; set a timeout above jobs_wait.
- Gemini Omni 4K on Sume: send 4K or 4k, 10 s max, 16:9 or 9:16
Sume's gemini-omni-flash-1.1 takes 4K, and lowercase 4k works as an alias. Requests run 3 to 10 seconds, 16:9 or 9:16. Python body builder.
- GET /v1/video-router/models/wan-3.0: fetch one model's limits
Fetch one Sume video model with GET /v1/video-router/models/{model_id}, such as wan-3.0 or seedance-2.5. Unknown ids return 404. Read limits before you submit.
Written by Sume