Image generation
Deliverable: generate.py, edit.py, and nano_banana.py, each of which writes a PNG to disk.
Prerequisites: pip install requests, a MIRAPI_API_KEY, and an image-capable model enabled for your key (for the Gemini script, a Nano Banana model such as gemini-3.1-flash-image-preview).
MirAPI exposes two OpenAI-compatible endpoints for images, both billed per generated image:
POST /v1/images/generations: text-to-image.POST /v1/images/edits: edit a source image according to an instruction.
Google’s Gemini image models (the Nano Banana family) are multimodal chat models and use the Gemini-native POST /v1beta/models/{model}:generateContent instead, as the third script below does. Anthropic has no image-generation endpoint; its models only take images as input (see Vision inputs).
Text-to-image
Section titled “Text-to-image”/v1/images/generations takes a chat-style input.messages payload with a parameters object:
import base64import osimport requests
BASE = "https://api.mirapi.ai/v1"HEADERS = {"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"}
resp = requests.post( f"{BASE}/images/generations", headers=HEADERS, json={ "model": "deepseek-chat", "input": { "messages": [ {"role": "user", "content": [{"text": "A neon-lit street in the rain, cinematic"}]} ] }, "parameters": {"size": "1024*1024", "prompt_extend": True, "watermark": False}, }, timeout=120,)resp.raise_for_status()
image = resp.json()["data"][0]if image["b64_json"]: with open("output.png", "wb") as f: f.write(base64.b64decode(image["b64_json"]))else: open("output.png", "wb").write(requests.get(image["url"], timeout=60).content)print("Saved output.png")Edit an image
Section titled “Edit an image”/v1/images/edits sends the source image and the instruction in the same message-style shape. The image field accepts a public HTTP(S) URL or a base64 data URI:
import base64import osimport requests
BASE = "https://api.mirapi.ai/v1"HEADERS = {"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"}
# A base64 data URI of the source image:with open("input.png", "rb") as f: data_uri = "data:image/png;base64," + base64.b64encode(f.read()).decode()
resp = requests.post( f"{BASE}/images/edits", headers=HEADERS, json={ "model": "deepseek-chat", "input": { "messages": [ { "role": "user", "content": [ {"image": data_uri}, {"text": "Turn the sky into a starry night"}, ], } ] }, "parameters": {"n": 1, "watermark": False}, }, timeout=120,)resp.raise_for_status()
image = resp.json()["data"][0]if image["b64_json"]: with open("edited.png", "wb") as f: f.write(base64.b64decode(image["b64_json"]))else: open("edited.png", "wb").write(requests.get(image["url"], timeout=60).content)print("Saved edited.png")Gemini image models (Nano Banana)
Section titled “Gemini image models (Nano Banana)”gemini-2.5-flash-image, gemini-3-pro-image-preview, gemini-3.1-flash-image-preview and the other Nano Banana names in the catalog are multimodal chat models, so they don’t use the two endpoints above. Call POST /v1beta/models/{model}:generateContent with the x-goog-api-key header, ask for a picture with generationConfig.responseModalities: ["TEXT", "IMAGE"], and pick the base64 inlineData part out of the response:
import base64import osimport requests
MODEL = "gemini-3.1-flash-image-preview"
resp = requests.post( f"https://api.mirapi.ai/v1beta/models/{MODEL}:generateContent", headers={"x-goog-api-key": os.environ["MIRAPI_API_KEY"]}, json={ "contents": [{"role": "user", "parts": [{"text": "A neon-lit street in the rain, cinematic"}]}], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": {"aspectRatio": "16:9"}, }, }, timeout=120,)resp.raise_for_status()
for part in resp.json()["candidates"][0]["content"]["parts"]: if "inlineData" in part: with open("nano_banana.png", "wb") as f: f.write(base64.b64decode(part["inlineData"]["data"])) print("Saved nano_banana.png") elif "text" in part: print(part["text"])imageConfig.imageSize (1K / 2K / 4K) is optional and applies only to Gemini 3 image models. To edit or reference an image, add an inlineData part next to the text, in the same shape as Vision inputs. The same models also answer POST /v1/chat/completions (OpenAI format), with the image options under extra_body.google.image_config (aspect_ratio, image_size).
The two request shapes
Section titled “The two request shapes”The image endpoints accept two payload formats:
- Message-style:
/v1/images/generationsand/v1/images/edits(no trailing slash). These useinput.messagesplusparameters, as in the scripts above. - OpenAI classic: the trailing-slash variants
/v1/images/generations/and/v1/images/edits/. These use a top-levelpromptfor OpenAI SDK compatibility; edits sendimage+promptas multipart form data.
curl https://api.mirapi.ai/v1/images/generations/ \ -H "Authorization: Bearer $MIRAPI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-chat", "prompt": "A neon-lit street in the rain, cinematic", "n": 1, "size": "1024x1024" }'The classic edits variant uses multipart/form-data with an image file, a prompt, and response_format (url or b64_json).
Parameters
Section titled “Parameters”Message-style requests put everything under parameters:
| Parameter | Purpose |
|---|---|
model |
Model name exactly as shown in the catalog |
input.messages[].content[].text |
The generation or edit instruction |
input.messages[].content[].image |
Source image for edits (URL or base64 data URI) |
parameters.size |
Output dimensions as a string, e.g. 1024*1024 |
parameters.negative_prompt |
Things to avoid in the output |
parameters.prompt_extend |
Let the model expand the prompt (true / false) |
parameters.watermark |
Request a visible watermark (true / false) |
parameters.n |
Number of images to return (edits; where supported) |
Not every model accepts every parameter, and many models only support n: 1. Check the model card in the catalog for supported values.
Retrieving the image
Section titled “Retrieving the image”The response contains an array of images:
{ "created": 1780000000, "data": [ { "url": "https://…", "b64_json": null, "revised_prompt": "…" } ]}url: a temporary URL to download the image.b64_json: the image as a base64 string.revised_prompt: the prompt actually used when the model rewrites yours.
To save the image to disk, prefer b64_json (no second request) or download url immediately, because it is temporary.
Errors and billing
Section titled “Errors and billing”Billing. Generation and editing are charged per image, deducted from your prepaid balance; parameters.n multiplies the cost, so start with n: 1. When the balance is exhausted, requests return 403; topping up restores the same key immediately. Reconcile spend in the Platform billing log, or via GET /api/usage/token (account-level summary) and GET /api/log/token (per-request detail). Gemini image models called through generateContent are billed per token like any other Gemini call: the generated image counts as output tokens (see usageMetadata in the response), at the rate on the model card.
Errors. The gateway returns standard status codes: 401 (fix your key), 403 (balance, model whitelist, quota, or IP allowlist), 413 (request too large), 429 (rate limited: retry with jittered exponential backoff, 1s → 2s → 4s capped at ~30s; there is no Retry-After header), and 500 (transient upstream failure; idempotent requests are safe to retry). Error responses use the OpenAI envelope {"error":{"message","type","param","code"}}, and every message ends with a request ID; include it in any support ticket.
Troubleshooting
Section titled “Troubleshooting”- Wrong model. Image endpoints reject models without image-generation capability. Filter the catalog by output type and use the model’s exact
id. - Gemini image models (Nano Banana). Call
POST /v1beta/models/{model}:generateContentwithresponseModalities: ["TEXT", "IMAGE"], not/v1/images/generations; the image is ininlineData.data. 403on a valid key. Check your balance first, then the model whitelist, quota, and IP allowlist on the key.urlno longer works. Download it immediately after the request, or useb64_json.413with base64. Large images exceed the size limit, so downscale them before encoding.nignored or rejected. Some models return a single image regardless; check the model card.
Related links
Section titled “Related links”- Image generation guide: the endpoints in full
- Vision inputs: send images to chat models as input
- Text-to-video: the asynchronous video flow
- Image-to-video: first-frame control
- Models & pricing: find image-capable models
- Billing & top-ups: per-image and per-token pricing