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.

5 min readSume
All posts

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, and raise_for_status() raises an HTTPError on any unsuccessful status code.
  • Send an Idempotency-Key on the POST, so a retry with the same key returns the original job instead of billing a second one.
  • Each item in data has a url and a media_type. The URLs are Sume-hosted and signed, so download the files you keep; response.content gives 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:

From the Image API, Errors and rate limits, and Credits docs, read 2026-09-27.
StatusWhat it meansWhat the code does
200The image response, with URLs in data.Download each file.
202The job envelope: the image was not ready within the wait.Poll status_url, then read result_url.
400 unsupported_parameterThe model does not list a field you sent.Fix the body; the same request fails again.
402 insufficient_creditsThe 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_limitedThe workspace queue is full, or too many requests.Back off, using retry-after when present.
502The 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

All Developers posts

Written by Sume