Skip to content

Image generation

Deliverable: a generate.py and an edit.py that write a PNG to disk.

Prerequisites: pip install requests, a MIRAPI_API_KEY, and an image-capable model enabled for your key.

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 an image from a source image plus an instruction.

There is no separate Anthropic or Gemini image-generation endpoint — those protocols only read images back into chat models (see Vision inputs).

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

import base64
import os
import 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")

/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 base64
import os
import 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")

The image endpoints accept two payload formats:

  • Message-style/v1/images/generations and /v1/images/edits (no trailing slash). Uses input.messages plus parameters, as in the scripts above.
  • OpenAI classic — the trailing-slash variants /v1/images/generations/ and /v1/images/edits/. Uses a top-level prompt for OpenAI SDK compatibility; edits send image + prompt as multipart form data.
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",
"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).

Message-style requests put everything under parameters:

Parameter Purpose
model Model name exactly as shown in the catalogue
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 catalogue 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 — it is temporary.

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 console billing log, or via GET /api/usage/token (account summary) and GET /api/log/token (per-request detail).

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.

  • Wrong model. Image endpoints reject models without image-generation capability. Filter the catalogue by output type and use the model’s exact id.
  • 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 before encoding.
  • n ignored or rejected. Some models return a single image regardless; check the model card.