Nano Banana 2.1 on Sume in Python: handle the 200 and 202 replies
Call google/nano-banana-2.1 on Sume's /v1/images in Python and handle both the 200 image reply and the 202 job envelope, with polling to the result URL.

POST /v1/images waits up to 30 seconds. If the image finishes it returns 200 with data[].url. If it does not, Sume returns 202 with a job envelope, and you poll status_url until terminal is true, then read result_url. The function below handles both and checks the status code rather than guessing from the body, as the Sume Image API docs advise.
The code
It needs requests and a SUME_API_KEY environment variable. The model id is google/nano-banana-2.1. 2K and 16:9 are examples; read the model's supported_parameters first.
import os
import time
import requests
BASE = "https://api.sume.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
def generate(prompt):
body = {"model": "google/nano-banana-2.1", "prompt": prompt,
"resolution": "2K", "aspect_ratio": "16:9"}
r = requests.post(f"{BASE}/v1/images", headers=HEADERS, json=body, timeout=60)
r.raise_for_status()
if r.status_code == 200: # finished inside the 30 s budget
return [item["url"] for item in r.json()["data"]]
envelope = r.json()["data"] # 202: a job, not an image body
while True:
s = requests.get(envelope["status_url"], headers=HEADERS, timeout=30)
s.raise_for_status()
status = s.json()["data"]
if status["terminal"]:
break
time.sleep(status.get("next_poll_after_seconds") or 2)
if status["sume_status"] != "completed":
raise RuntimeError(f"job ended as {status['sume_status']}")
res = requests.get(envelope["result_url"], headers=HEADERS, timeout=30)
return [a["url"] for a in res.json()["data"]["result"]["artifacts"]]
if __name__ == "__main__":
print(generate("matte black bottle on marble, soft window light"))What each reply looks like
The two replies have different shapes, so a single parser for both will fail.
| Status | Body | What to read |
|---|---|---|
| 200 | created, model, data (a list), usage | data[].url and usage.cost |
| 202 | data.job, data.status_url, data.result_url | Poll status_url, then result_url |
| Job result | data.result.artifacts | artifacts[].url, a Sume-hosted URL |
Notes that matter
- Do not resubmit on a timeout. A client timeout does not cancel the job, which keeps running and bills.
- Slow settings such as 4K, high quality or a large
nare the likeliest to return202. usage.costis the billed USD amount on a200. Token counts there are always 0.- Failed and cancelled generations are not charged, per the Image API docs.
Sources
Related posts
More in Developers
- Next.js route handler: submit a Seedance 2.5 job, keep the key private
Keep your Sume API key on the server: a Next.js route handler that submits a seedance-2.5 job and returns the job id, plus a second route that polls it.
- Nightly video batch after Sora: 6, 24, 48 or 120 jobs by Sume plan
A cron that submitted 30 Sora renders at once needs a new ceiling. Sume accepts 6 jobs on Free, 24 on Pro, 48 on Startup, 120 on Scale before queue_full.
- No-code HTTP step: submit a Wan 3.0 job and get a callback_url
Any automation tool with an HTTP request step and a webhook trigger can run wan-3.0 on Sume. The request body, the HTTPS callback_url, and what to store.
- Node 22 script: create a Sume bulk queue and poll it to the end
Dependency-free Node 22 ESM script: POST a bulk queue from items.json, back off the poll, survive 429 and 503, and exit non-zero when any item failed.
Written by Sume