Python image generation API: generate and save AI images
Generate images from Python with Requests: POST a prompt to an image API, read the URLs from a 200 or poll the 202 job, then save each file.

To generate images with Python, POST a JSON body with a model and a prompt to an image generation API using the Requests library, then download each image URL in the response and write it to disk. With Sume's POST /v1/images, the call waits up to 30 seconds and returns 200 with the image URLs; if the image is not ready by then, it returns 202 with a job you poll at /v1/jobs/{id}/status and read at /v1/jobs/{id}/result.
Sume facts come from the Image API and Jobs and results docs, and Requests behavior from its Quickstart, all read on 2026-09-27. Sume's SDK is TypeScript, so from Python you call the REST API directly, as the Image API docs' own Requests example does. For video, see text-to-video API in Python.
How do I generate an image with Python?
Read the key from an environment variable, send model and prompt, and set a timeout longer than the 30-second wait: Requests does not time out unless you set one.
json=encodes the body for you and sets the JSON content type, andraise_for_status()raises anHTTPErroron any unsuccessful status code.- Send an
Idempotency-Keyon the POST, so a retry with the same key returns the original job instead of billing a second one. - Each item in
datahas aurland amedia_type. The URLs are Sume-hosted and signed, so download the files you keep;response.contentgives the bytes.
import os
import requests
API = "https://api.sume.com/v1/images"
HEADERS = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
payload = {
"model": "bytedance-seed/seedream-4.5",
"prompt": "a red panda astronaut floating in space, studio lighting",
"aspect_ratio": "1:1",
"output_format": "png",
}
response = requests.post(
API,
headers={**HEADERS, "Idempotency-Key": "red-panda-001"},
json=payload,
timeout=60,
)
response.raise_for_status()
if response.status_code == 200:
for i, image in enumerate(response.json()["data"]):
file = requests.get(image["url"], timeout=60)
with open(f"image-{i}.png", "wb") as f:
f.write(file.content)What if the API returns 202 instead of 200?
Check the status code, not the body shape: 200 is the image response, 202 is the job envelope. Slow configurations, such as 4K, high quality, and a large n, are the most likely to return 202. Poll the job's status_url until terminal is true, honoring next_poll_after_seconds, then read the image URLs from the result's artifacts. Do not resubmit: a client timeout does not cancel the job, which keeps running and still bills.
import time
job = response.json()["data"] # the 202 job envelope
while True:
status = requests.get(job["status_url"], headers=HEADERS, timeout=30).json()["data"]
if status["terminal"]:
break
time.sleep(status["next_poll_after_seconds"] or 5)
if status["sume_status"] == "completed":
result = requests.get(job["result_url"], headers=HEADERS, timeout=30).json()["data"]
urls = [a["url"] for a in result["result"]["artifacts"] if a["type"] == "image"]Which responses does my Python code need to handle?
Only model and prompt are required, and any other field must be one the model lists; image generation with reference images walks through the optional ones. After a client-side timeout or a dropped connection, resend with the same Idempotency-Key header, which returns the original job instead of billing a second one.
Errors are JSON with an error object holding code, message, and request_id, and the example's raise_for_status() raises an HTTPError for every unsuccessful status below, so catch it where you handle them:
| Status | What it means | What the code does |
|---|---|---|
200 | The image response, with URLs in data. | Download each file. |
202 | The job envelope: the image was not ready within the wait. | Poll status_url, then read result_url. |
400 unsupported_parameter | The model does not list a field you sent. | Fix the body; the same request fails again. |
402 insufficient_credits | The balance is not sufficient for the generation. | Stop and top up in the dashboard; the API has no top-up call. |
429 queue_full or rate_limited | The workspace queue is full, or too many requests. | Back off, using retry-after when present. |
502 | The generation failed inside the wait. It is not billed. | In current code the body carries the job's error code and its status_url. |
Sources
Related posts
More in Developers
- Speech to text API in JavaScript: audio to text in Node.js
Call a speech to text API from JavaScript on your server: send the audio file's URL with the Sume SDK in Node.js, wait for the job, read the text.
- Speech to text in Python: transcribe audio with timestamps
Speech to text in Python with Requests: send the audio URL, poll the job, then read the transcript and word timestamps. A script for Sume STT 1.0.
- CORS error calling the Sume API from a browser: the fix
Browsers block direct calls from your site to api.sume.com, and API keys must never ship in frontend code. Call Sume from your server and proxy it.
- Sume API endpoints list: routes, scopes, idempotency
An index of the Sume API's public routes by family: which need no key, which scope each needs, where Idempotency-Key applies, and the post on each.
Written by Sume