502 from the Sume Images API: read code, next_action and status_url
A 502 on POST /v1/images is a failed job, not an outage. Parse error.code, retryable and next_action, then fetch status_url for the job. Python handler.

A 502 from POST /v1/images on Sume means the synchronous job reached a terminal failed state inside the wait window; it is the job's verdict, not a gateway outage. Read error.code, error.retryable and error.next_action from the body, then use error.details.status_url to read the job. If retryable is false and next_action is fix_input, change the request, since sending it again will fail the same way.
Branch on the status code first. 200 has the images, 202 is a job that outlived the 30 second sync wait and must be read from the job result URL, and 502 is a failed job. Treating all three as one case is the common bug.
Which fields does the error envelope carry?
From the Sume Image API page (read 2026-10-04):
| Field | Example | Use |
|---|---|---|
code | input_media_unreachable | Stable machine-readable reason |
message | Could not download an input media URL | Show to a developer, not a customer |
retryable | false | Decide whether to retry |
next_action | fix_input | What to change |
details.request_id | A job id | Log it and quote it to support |
details.status_url | /v1/jobs/{id}/status | Read the job after failure |
What does a handler look like?
The function returns an image URL, a job to poll or raises with the fields you need to log.
import os, requests
def generate(prompt, model="google/nano-banana-2"):
key = os.environ["SUME_API_KEY"]
r = requests.post(
"https://api.sume.com/v1/images",
headers={"Authorization": f"Bearer {key}"},
json={"model": model, "prompt": prompt},
timeout=90,
)
if r.status_code == 200:
return r.json()["data"][0]["url"]
if r.status_code == 202:
job = r.json()["data"]
raise RuntimeError(f"still running: {job['status_url']}")
err = r.json().get("error", {})
d = err.get("details", {})
raise RuntimeError(
f"{err.get('code')} retryable={err.get('retryable')} "
f"next={err.get('next_action')} job={d.get('request_id')} "
f"status={d.get('status_url')}"
)
if __name__ == "__main__":
print(generate("A red bicycle against a white wall"))Will the retry bill me twice?
Sume does not bill failed or cancelled generations, and bills completed ones in full. A 502 is a failure, so a retry is not a double charge for that job. The risk is the opposite case: a timeout on your side after the job actually completed, followed by a second call. Use an idempotency key on retries, as covered in the failed batch retry post.
What do I do with the status URL?
The job still exists after a failure and stays readable at status_url. Fetch it with the same bearer key to confirm the state and log it with the request id. The shared meaning of job states is in the jobs and results docs.
Sources
Related posts
More in Developers
- MiniMax H3 on Sume: "720p is not supported; use 768p" explained
MiniMax H3 and H3 Max render natively at 480p and 768p. Send resolution 720p and Sume answers 400. Why, what to send, and what it costs per second.
- A/B test two reference sets on Seedance 2.5: six takes for about $16
Which reference images work better on Seedance 2.5? Run two sets, three takes each, 10 s at 480p on Sume for about $16, and score blind.
- Idempotency-Key for Agent Completions: reuse the model's tool call id
Retries after a timeout must not start a second paid Sume run. Derive Idempotency-Key from the tool call id your model returned, such as the OpenAI call_id.
- Agent Completion messages[]: system and user turns become one prompt
How Sume joins messages[] into one prompt, what a system turn can and cannot do, and why a GPT-6.1 Sol or Sonnet 5.5 chat history cannot be replayed as is.
Written by Sume