Generate AI Images with Python: GPT Image, Nano Banana, FLUX
Python code to generate and edit AI images with GPT Image 2.5, Nano Banana Pro and FLUX.2 Pro through one API. Batching, references, seeds and costs.
To generate AI images with Python, send a POST request with a model id and a prompt, then download the returned image URL. With Jantaris that is one call to POST /v1/generate?wait=60 using requests, and the same code works for GPT Image 2.5, Nano Banana Pro and FLUX.2 Pro: you only change the model string. Each image is billed per image at a price you can see before the request runs.
This guide covers text-to-image, batching, editing with reference images, reproducible seeds, and how the three models differ, with code I use in production.
GPT Image 2.5 vs Nano Banana Pro vs FLUX.2 Pro
The three image models I use most, with facts from each vendor's announcement:
| GPT Image 2.5 | Nano Banana Pro | FLUX.2 Pro | |
|---|---|---|---|
| Vendor | OpenAI | Google (Gemini 3 Pro Image) | Black Forest Labs |
| Released | Sept 8, 2026 | Nov 2025 | Nov 25, 2025 |
| Max output | up to 4K | up to 4K | up to 4 MP |
| References | up to 16 images | up to 14 images | multiple references |
| Known for | precise edits, reference fidelity | legible text in images, infographics | photoreal product and editorial shots |
| On Jantaris | GPT Image 2.5 | Nano Banana Pro | FLUX.2 [pro] |
| Price | $0.0432 per image | from $0.0747 per image | from $0.0688 per image |
How I pick in practice:
- Text on the image (posters, thumbnails, labels, infographics): Nano Banana Pro. Google explicitly positions it for correctly rendered text, including longer passages and multiple languages.
- Editing an existing image or putting a known subject into a new scene: GPT Image 2.5. OpenAI's 2.5 release focused on edit precision and staying faithful to reference photos.
- Photoreal product shots with clean light and materials: FLUX.2 Pro.
When I am unsure, I run the same prompt on all three (script below) and let the results decide.
Setup: one key, one function
pip install requests
export AI_API_KEY=sk-jt-...The core helper. It submits a job with ?wait=60, which makes the server hold the request until the image is ready (up to 120 seconds max), and falls back to polling if needed:
import os
import time
import pathlib
import requests
API = "https://jantaris.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['AI_API_KEY']}"}
def generate(model, **input_):
r = requests.post(
f"{API}/generate?wait=60",
headers=HEADERS,
json={"model": model, "input": input_},
timeout=130,
)
task = r.json()
if r.status_code >= 400:
raise RuntimeError(f"{r.status_code}: {task['error']['message']}")
while task["status"] in ("queued", "running"):
time.sleep(3)
task = requests.get(f"{API}/tasks/{task['id']}", headers=HEADERS, timeout=30).json()
if task["status"] == "failed":
raise RuntimeError(f"failed (not charged): {task['error']}")
return task
def save_outputs(task, folder="out"):
pathlib.Path(folder).mkdir(exist_ok=True)
paths = []
for i, item in enumerate(task["output"]):
ext = pathlib.Path(item["url"].split("?")[0]).suffix or ".png"
path = pathlib.Path(folder) / f"{task['id']}_{i}{ext}"
path.write_bytes(requests.get(item["url"], timeout=60).content)
paths.append(path)
return pathsSave files right away. Output links are valid for a limited time, so the API response is a pickup point, not an archive.
Text-to-image in Python
task = generate(
"flux-2-pro",
prompt=(
"Product photo of a matte black espresso machine on a polished concrete counter, "
"morning side light, shallow depth of field, editorial style"
),
aspect_ratio="4:3",
)
print(save_outputs(task), task["cost"])Switching models is the only change:
task = generate(
"nano-banana-pro",
prompt='Minimal concert poster, large headline "NIGHT SHIFT", date "OCT 14", '
"two-color risograph style, cream paper texture",
aspect_ratio="3:4",
)Parameters that work across image models
| Parameter | Values | Use it for |
|---|---|---|
prompt | text | What you want |
image_urls | list of public URLs | Editing and reference images (the maximum count differs per model) |
Everything else depends on the model. aspect_ratio takes ratios like 4:3 in FLUX.2 Pro and Nano Banana Pro, but pixel sizes (1024x1024, 1536x1024, 1024x1536) or auto in GPT Image 2.5. num_images, seed, negative_prompt, resolution and quality exist only in some models. The exact list of fields and allowed values is in input_schema from GET /v1/models/{id}, for example GET /v1/models/gpt-image-2.5. A field or value the model does not accept returns 422 before anything is charged.
Generate several images per request
num_images asks for up to four variations in one task, in models that list it in their schema, such as Nano Banana 2. You pay per image, so four images cost four times one. GPT Image 2.5, Nano Banana Pro and FLUX.2 Pro return one image per task, so with them you submit several tasks instead.
task = generate(
"nano-banana-2",
prompt="Flat vector illustration of a fox reading a newspaper in a cafe, warm palette",
aspect_ratio="1:1",
num_images=4,
)
for p in save_outputs(task):
print(p)Edit an image or use references
Pass one or more public image URLs in image_urls and describe the change. This is how I do background swaps, style transfers and "put this product in that scene" work.
task = generate(
"gpt-image-2.5",
prompt="Place this bottle on a wet rock by a mountain stream, overcast light, keep the label exactly as is",
image_urls=["https://example.com/assets/bottle-packshot.png"],
aspect_ratio="1536x1024", # GPT Image 2.5 takes pixel sizes or "auto", not ratios
)Multi-reference works the same way. List the images and say in the prompt what to take from each:
task = generate(
"nano-banana-pro",
prompt="The person from the first image wearing the jacket from the second image, "
"walking through the street from the third image, golden hour",
image_urls=[
"https://example.com/refs/person.jpg",
"https://example.com/refs/jacket.jpg",
"https://example.com/refs/street.jpg",
],
aspect_ratio="9:16",
)Tips from my own runs:
- Put the most important reference first.
- Keep reference images clean: plain background for products, well-lit faces for people.
- Say explicitly what must not change ("keep the label exactly as is").
Reproducible images with a seed
When a client approves a composition and wants "the same, but blue", a fixed seed gives you a stable starting point:
base = dict(prompt="Isometric tiny house on a floating island, soft clay render", aspect_ratio="1:1", seed=421)
a = generate("flux-2-max", **base)
b = generate("flux-2-max", **{**base, "prompt": base["prompt"] + ", blue roof"})Seeds make results more repeatable, not identical across every change, and only some models accept them: among the image models these include FLUX.2 [max], FLUX.1 Kontext [pro] and Wan 2.7 Image. GPT Image 2.5, Nano Banana Pro and FLUX.2 Pro have no seed field, and sending one returns 422. Check the model's schema for seed before you build a workflow around it.
Compare all three models in parallel
Waiting for three models one after another is slow. A thread pool sends them at once:
from concurrent.futures import ThreadPoolExecutor
PROMPT = "Studio photo of a ceramic mug with the text 'good morning' in hand-lettered script"
MODELS = ["gpt-image-2.5", "nano-banana-pro", "flux-2-pro"]
# GPT Image 2.5 takes pixel sizes, the other two take ratios
ASPECT = {"gpt-image-2.5": "1024x1024", "nano-banana-pro": "1:1", "flux-2-pro": "1:1"}
def run(model):
try:
task = generate(model, prompt=PROMPT, aspect_ratio=ASPECT[model])
return model, save_outputs(task, folder=f"out/{model}"), task["cost"]
except RuntimeError as e:
return model, None, str(e)
with ThreadPoolExecutor(max_workers=3) as pool:
for model, paths, cost in pool.map(run, MODELS):
print(model, paths, cost)This is my standard test for a new client brief. Three images, one prompt, and the difference between models is usually obvious at a glance. If one model's safety filter rejects the prompt, that task fails with cost 0 and the others still come back.
Using the async API for large batches
For hundreds of images, drop ?wait and submit tasks asynchronously, then collect them by polling or with a webhook_url. The pattern is identical to video jobs, which I cover in Building an AI video pipeline with async tasks and webhooks.
def submit(model, **input_):
r = requests.post(f"{API}/generate", headers=HEADERS,
json={"model": model, "input": input_}, timeout=30)
r.raise_for_status()
return r.json()["id"]
prompts = [f"Minimal line icon of a {x}, black on white" for x in ["kettle", "bicycle", "tent", "lamp"]]
ids = [submit("flux-2-pro", prompt=p, aspect_ratio="1:1") for p in prompts]Handle 429 with a short backoff if you submit in large bursts.
What AI image generation costs
Image models are billed per image. Current rates: GPT Image 2.5 at $0.0432 per image, Nano Banana Pro at from $0.0747 per image, FLUX.2 [pro] at from $0.0688 per image. The 202 response carries estimated_cost, the finished task carries cost, and failed tasks cost nothing. The complete price list is on /pricing.
From image to video
A strong still makes a strong first frame. Once you have an image you like, pass its URL as image_url to a video model like Seedance 2.5 and describe the motion. The Seedance 2.5 API guide has the code.
Not writing code? The same image models are in Studio on the same balance.
FAQ
How do I generate an image with Python using an API?
Send a POST request to https://jantaris.com/v1/generate?wait=60 with {"model": "flux-2-pro", "input": {"prompt": "..."}} and your bearer key, using requests. The response contains the finished task with an image URL in output, which you download and save.
Which AI image model is best for text in images?
Nano Banana Pro. Google built it specifically for legible, correctly rendered text in images, from short taglines to longer paragraphs, in multiple languages.
Can I edit an existing image through the API?
Yes. Pass the image as a public URL in image_urls and describe the edit in prompt. GPT Image 2.5 and Nano Banana Pro both handle edits and multiple reference images.
How much does one AI-generated image cost?
It depends on the model. Current per-image prices are $0.0432 per image for GPT Image 2.5, from $0.0747 per image for Nano Banana Pro and from $0.0688 per image for FLUX.2 Pro. Failed generations are not charged.
How many images can I request at once?
Up to four per request with num_images, in models that support it, such as Nano Banana 2. GPT Image 2.5, Nano Banana Pro and FLUX.2 Pro return one image per task. For larger batches, submit many tasks asynchronously and collect them by polling or webhook.











