Image API returned 202 at 4K: handle the job envelope in Python
A slow 4K, high-quality or large-n request can miss the 30 second wait and return 202 with a job, not images. Branch on the status code, then poll.

POST /v1/images waits up to 30 seconds and returns 200 with images if the job finishes in that time; otherwise it returns 202 with the standard job envelope. The docs name the slow cases: 4K output, high quality and a large n. Your code must branch on the status code, because the 202 body has no data[].url.
What each status means
A failed job inside the wait is a 502 with an error envelope, not a 202. Failed or cancelled generations use no credits.
| Status | Meaning | Next step |
|---|---|---|
| 200 | Images are ready in data[] | Use data[].url |
| 202 | Job accepted, not finished | Poll status_url, then read result_url |
| 502 | Job failed inside the wait | Read error.code and next_action |
| 400 | Parameter the model does not list | Fix the request; no job was created |
A handler that works for all outcomes
The function below returns the image list on 200 and polls the job on 202. It stops on the terminal flag that the status endpoint returns and only fetches the result when the job is completed. Client-side timeouts do not cancel the job, so store the job id if you give up.
Call it with generate({"model": "google/nano-banana-2.1", "prompt": "city skyline", "resolution": "4K"}).
import os, time, requests
H = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
BASE = "https://api.sume.com"
def unwrap(body):
return body.get("data", body)
def generate(payload):
r = requests.post(f"{BASE}/v1/images", headers=H, json=payload, timeout=60)
if r.status_code == 200:
return r.json()["data"]
if r.status_code != 202:
raise RuntimeError(f"{r.status_code} {r.text}")
job = unwrap(r.json())
while True:
s = unwrap(requests.get(job["status_url"], headers=H, timeout=30).json())
if s.get("terminal"):
break
time.sleep(s.get("next_poll_after_seconds") or 3)
if s.get("sume_status") != "completed":
raise RuntimeError(f"job ended as {s.get('sume_status')}")
return requests.get(job["result_url"], headers=H, timeout=30).json()
Avoid the wait
If you already know a request is heavy, send mode: "async" and skip the 30 second hold. Use a webhook if you do not want to poll. Do not resubmit a paid request because your own timer fired.
Sources
Related posts
More in Developers
- Omni Flash edit: test at 360p, then run the final at 1080p
Prove an Omni Flash edit prompt at 360p ($0.0375 a second on Sume) before paying for 1080p ($0.1875 a second). Use a new Idempotency-Key for each resolution.
- Omni Flash edit in sync mode: it waits 30 seconds, then you poll
Sync mode on Sume waits at most 30 seconds. An Omni edit may not finish in that time, so read the job id from the reply and poll it. Never resubmit.
- Omni Flash image-to-video: frame_images or input_references?
frame_images with first_frame pins your photo as the opening frame. A single input_references image conditions the whole clip instead. Pick by what you want.
- One prompt across video models: what to rewrite on Sume
A prompt that works on one Sume video row may need edits on another: Omni uses IMAGE_REF tokens, Kling takes no references, Veo is text-only. Read 2026-10-13.
Written by Sume