Skip to content

Image generation

MirAPI offers two OpenAI-compatible endpoints for working with images:

  • POST /v1/images/generations: text-to-image.
  • POST /v1/images/edits: image editing (send an image plus an edit instruction).

Both endpoints also have a trailing-slash variant (/v1/images/generations/, /v1/images/edits/), and both are billed per generated image: one charge per image in the response, never per token. They use standard Authorization: Bearer authentication. Google’s Gemini image models (the Nano Banana family) are multimodal chat models and use the Gemini-native POST /v1beta/models/{model}:generateContent instead (see the Gemini section below). Anthropic has no image-generation endpoint.

/v1/images/generations takes a chat-style input.messages payload with a parameters object:

Terminal window
curl https://api.mirapi.ai/v1/images/generations \
-H "Authorization: Bearer $MIRAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"input": {
"messages": [
{
"role": "user",
"content": [
{"text": "A neon-lit street in the rain, cinematic"}
]
}
]
},
"parameters": {
"size": "1024*1024",
"negative_prompt": "blurry, low quality",
"prompt_extend": true,
"watermark": false
}
}'
import os
import requests
resp = requests.post(
"https://api.mirapi.ai/v1/images/generations",
headers={"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"},
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},
},
)
print(resp.json()["data"][0])

/v1/images/edits takes the source image and the instruction in the same message-style shape:

Terminal window
curl https://api.mirapi.ai/v1/images/edits \
-H "Authorization: Bearer $MIRAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-chat",
"input": {
"messages": [
{
"role": "user",
"content": [
{"image": "https://example.com/photo.png"},
{"text": "Turn the sky into a starry night"}
]
}
]
},
"parameters": {"n": 2, "prompt_extend": true, "watermark": false}
}'

The image field accepts a public HTTP(S) URL or a base64 data URI (data:image/png;base64,…).

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 (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.

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.

Google’s Gemini image models (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. They do not use the image endpoints above: call them through the Gemini-native POST /v1beta/models/{model}:generateContent, request image output with generationConfig.responseModalities: ["TEXT", "IMAGE"], and read it back as base64 from candidates[0].content.parts[].inlineData. Authenticate with the x-goog-api-key header (or the ?key= query parameter).

Terminal window
curl https://api.mirapi.ai/v1beta/models/gemini-3.1-flash-image-preview:generateContent \
-H "x-goog-api-key: $MIRAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"role": "user", "parts": [{"text": "A neon-lit street in the rain, cinematic"}]}],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"],
"imageConfig": {"aspectRatio": "16:9"}
}
}'
import base64
import os
import requests
resp = requests.post(
"https://api.mirapi.ai/v1beta/models/gemini-3.1-flash-image-preview: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"},
},
},
)
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"]))
elif "text" in part:
print(part["text"])
Parameter Purpose
contents[].parts[].text The generation or edit instruction
contents[].parts[].inlineData A source or reference image for edits: {"mimeType": "image/png", "data": "<base64>"}, the same shape as in Vision inputs
generationConfig.responseModalities Must include "IMAGE"; use ["TEXT", "IMAGE"]
generationConfig.imageConfig.aspectRatio Aspect ratio, e.g. 1:1, 16:9, 9:16, 4:3, 3:4
generationConfig.imageConfig.imageSize Output resolution 1K / 2K / 4K; optional, Gemini 3 image models only

The response uses the standard Gemini shape, with the image as one of the parts:

{
"candidates": [
{
"content": {
"role": "model",
"parts": [
{"text": "…"},
{"inlineData": {"mimeType": "image/png", "data": "<base64>"}}
]
}
}
],
"usageMetadata": {"promptTokenCount": …, "candidatesTokenCount": …, "totalTokenCount": …}
}

Base64-decode inlineData.data to save the file. The same models are also available through POST /v1/chat/completions (OpenAI format), with the image options under extra_body.google.image_config (aspect_ratio, image_size).

Image generation is separate from vision input. To have a model read an image (caption it, answer questions, extract text), send it through a chat endpoint, where it is billed as tokens, not per image.

With the OpenAI-compatible protocol, add an image_url part to the content array:

import os
from openai import OpenAI
client = OpenAI(base_url="https://api.mirapi.ai/v1", api_key=os.environ["MIRAPI_API_KEY"])
response = client.chat.completions.create(
model="deepseek-chat",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What is in this photo?"},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
],
}],
)
print(response.choices[0].message.content)

The url can be an HTTP(S) URL or a base64 data URI. The same capability is available on the Anthropic (/v1/messages) and Gemini (/v1beta/models/{model}:generateContent) chat protocols. See Vision inputs for the full formats.

Billing. Generation and editing are charged per image and deducted from your prepaid balance. 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: back off exponentially with jitter; there is no Retry-After header), and 500 (upstream failure; idempotent requests are safe to retry). Error responses use the OpenAI envelope, and each message ends with a request ID. Include it in any support ticket.

  • 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}:generateContent with responseModalities: ["TEXT", "IMAGE"], not /v1/images/generations; the image is in inlineData.data.
  • 403 on a valid key. Check your balance first, then the model whitelist, quota, and IP allowlist on the key.
  • url no longer works. Download it immediately after the request, or use b64_json.
  • 413 with base64. Large images exceed the size limit. Downscale them before encoding.
  • n ignored or rejected. Some models return a single image regardless; check the model card.