Skip to content
Zabio is in beta: models, prices and features can change. Tell us what breaks at hello@zabio.ai.

Images

Zabio generates images through POST /v1/images/generations and edits them through POST /v1/images/edits, in the shapes OpenAI’s SDKs already send. Change base_url and your key, keep the rest. A long generation can also run in the background and be fetched when it is done.

For the exact shape of the request and the response, with curl, Python and Node examples, see the API reference.

Send a prompt and get the image back as base64. The key needs the chat scope (keys have it by default).

Terminal window
curl https://api.zabio.ai/v1/images/generations \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "prompt": "A red lighthouse on a rocky shore at dusk, flat illustration", "size": "1024x1024"}'
{
"created": 1793655000,
"data": [{"b64_json": "/9j/4AAQ..."}],
"usage": {"input_tokens": 14, "input_tokens_details": {"text_tokens": 14, "image_tokens": 0}, "output_tokens": 1132, "output_tokens_details": {"image_tokens": 1120, "text_tokens": 12}, "total_tokens": 1146},
"size": "1024x1024",
"output_format": "jpeg",
"zabio": {"call_id": "call_...", "vendor": "google", "model_served": "gemini-3.1-flash-lite-image", "cost_micros": 35389, "units": {"kind": "image", "images": 1, "size": "1024x1024"}}
}

Decode data[0].b64_json and write it to a file with the extension of output_format.

Model Maker Price Sizes Qualities Formats Notes
gpt-image-2.5-flare OpenAI 5.00 per million text input tokens, 8.00 per million image input tokens, 30.00 per million image output tokens (about 0.21 per 1024x1024 image at high quality, OpenAI’s example for GPT Image 2) 1024x1024, 1536x1024, 1024x1536 and any WIDTHxHEIGHT within OpenAI’s limits low, medium, high, xhigh, max png, jpeg, webp OpenAI’s choice for fast everyday generation; streams partial images; transparent backgrounds
gpt-image-2.5-sunburst OpenAI the same the same the same the same OpenAI’s choice where editing precision matters most
gpt-image-2 OpenAI the same the same low, medium, high the same the previous generation
gpt-image-1-mini OpenAI 2.00 text in, 2.50 image in, 8.00 image out per million tokens (about 0.036 per 1024x1024 image at high quality, OpenAI’s example) 1024x1024, 1536x1024, 1024x1536 low, medium, high the same by name only; retires on 2026-12-01 (OpenAI’s date)
gemini-nano-banana-2.1 Google 1.50 per million input tokens, 7.50 per million text and thinking output tokens, 30.00 per million image output tokens (0.0336 per 1K image, 0.0504 per 2K, 0.113 per 4K, Google’s own figures) 42 sizes: 14 aspect ratios at 1K, 2K and 4K takes auto only jpeg Google’s everyday model; up to 4K
gemini-3.1-flash-lite-image Google 0.25 input, 1.50 text and thinking output, 30.00 image output (0.0336 per 1K image) 14 sizes: 14 aspect ratios at 1K takes auto only jpeg the fastest and cheapest Google model
gemini-3-pro-image Google 2.00 input, 12.00 text and thinking output, 120.00 image output (0.134 per 1K or 2K image, 0.24 per 4K) 30 sizes: 10 aspect ratios at 1K, 2K and 4K takes auto only jpeg Google’s premium model; thinks before it draws
flux-2-dev@deepinfra Black Forest Labs 0.017858 per 1024x1024 image, 0.026786 per 1536x1024 or 1024x1536 image 1024x1024, 1536x1024, 1024x1536 takes auto only jpeg FLUX.2 [dev] served by DeepInfra; generations only
grok-imagine-image xAI 0.02 per image at every size; 0.002 per input image on an edit 1024x1024, 1248x832, 832x1248 takes auto only jpeg Grok Imagine; processing location not guaranteed (below)
grok-imagine-image-2.0 xAI 0.04 per image at low, 0.06 at medium, at every size; 0.01 per input image on an edit the same low, medium (auto is served as low) jpeg Grok Imagine 2.0; a medium image takes up to about two minutes; processing location not guaranteed (below)

Edits: the OpenAI models take up to 16 input images and a mask; gemini-nano-banana-2.1 and gemini-3-pro-image take up to 14 input images and no mask; gemini-3.1-flash-lite-image takes one input image; grok-imagine-image takes one input image and grok-imagine-image-2.0 up to five, with no mask; flux-2-dev@deepinfra does not edit. GET /v1/models gives the number as zabio.media.max_input_images.

Prices are the providers’ list prices in USD, per million tokens or per image; the plan fee applies on top (see Billing). On the OpenAI and Google models the per-image figures in brackets are the vendors’ own examples and never what a call is billed: those calls are billed on the tokens the vendor reports, image output tokens included, so a prompt the model thinks longer about costs what the vendor charges for it. flux-2-dev@deepinfra is billed per image at the price of the size made, which is what DeepInfra charges. The xAI models are billed per image, plus the model’s price per input image on an edit (0.002 on grok-imagine-image, 0.01 on grok-imagine-image-2.0), which is what xAI charges. GET /v1/models lists every image model with zabio.kind: "image", its sizes (zabio.media.sizes, plus custom_sizes where any WIDTHxHEIGHT is taken), its qualities and its formats, and the models page shows where each one is processed.

OpenAI’s custom sizes: both sides multiples of 16, an aspect ratio between 1:3 and 3:1, no side above 3840 pixels, between 655,360 and 8,294,400 pixels in total (OpenAI’s limits, read 2026-11-02). Google’s sizes are the exact pixels its models produce for each aspect ratio and tier, for example 1376x768 for 16:9 at 1K and 2752x1536 at 2K; 1536x1024 is an OpenAI size, not a Google one. The xAI sizes are the pixels xAI makes at 1K: 1024x1024 for 1:1, 1248x832 for 3:2 and 832x1248 for 2:3. 1536x1024 and 1024x1536 are not xAI sizes, so a request for them never goes to an xAI model, by name (400 on size) or under auto.

The xAI image models run on xAI’s global endpoint, because xAI serves images on no other: xAI may process those calls in any of its regions, so their processing location is not guaranteed. xAI’s chat models stay on its United States endpoint.

model: auto picks the cheapest image model that produces the size, the quality and the format you ask for, by the vendors’ own price figures for an image of that size and quality. There is no quality bar yet, so auto, auto/cheap, auto/balanced and auto/best all pick the same model on this endpoint. Without a quality, flux-2-dev@deepinfra wins at 1024x1024 and grok-imagine-image at its sizes 1248x832 and 832x1248; a named quality narrows the choice to the models that take it (low and medium to the OpenAI models and grok-imagine-image-2.0, the others to the OpenAI models); a png or webp format, a transparent background, or a custom size do the same. On an edit, auto considers only the models that take the number of images sent, counting a model’s fee per input image, and the OpenAI models when a mask is sent; a one-image edit without a mask goes to grok-imagine-image. If the chosen model fails, Zabio tries the next one at the same vendor, and at other vendors when the key’s fallback setting allows it. An auto call pays the price of automatic model choice (at most 0.00025 USD) like any other auto call.

application/json, as OpenAI.

Field Notes
prompt required; 32,000 characters at most
model an image model or auto (the default when absent)
n 1 to 4 images (default 1)
size auto (default) or WIDTHxHEIGHT; auto is the model’s default, 1024x1024 at 1K on the Google models
quality auto (default), low, medium, high, xhigh, max; the OpenAI models, xhigh and max on the 2.5 models, and low and medium on grok-imagine-image-2.0
output_format png, jpeg or webp; the model’s default when absent (png on OpenAI, jpeg on Google)
output_compression 0 to 100, for jpeg and webp on the OpenAI models
background auto, transparent or opaque; transparent needs png or webp and an OpenAI model
moderation auto or low, the OpenAI models’ content filter level
stream, partial_images stream: true with 0 to 3 partial images, the OpenAI models only, one image per request (see Streaming)
safety_identifier, user your end user’s identifier, passed to the vendor
zabio.background true to run the generation in the background (see Background generation); not with stream
zabio.expires_after on a background generation, {"anchor": "created_at", "seconds": N} with N at least 3600: when its images are deleted, if sooner than your organization’s retention

background, moderation and output_compression are OpenAI settings; the other models ignore them and the answer lists them under zabio.ignored. response_format is b64_json (the default) or url (see Images as URLs).

OpenAI’s shape: created, data[].b64_json, usage (input tokens with their text and image details, output tokens with their image and text details), the size, quality, output_format and background produced, plus the zabio object: call_id, vendor, model_served, cost_micros (what the call cost you, fee included), units (kind: "image", the number of images, their size and quality), ignored where any, and retires_at on a model with a date.

With "response_format": "url" on a generation or an edit, each image comes back as a link instead of base64, the way OpenAI’s DALL-E models answered:

{"created": 1793655000, "data": [{"url": "https://api.zabio.ai/v1/images/content/file_...?exp=1793658600&sig=..."}], "usage": {...}, "zabio": {"url_expires_at": 1793658600, ...}}

The link works for 60 minutes after the image is made, without your API key, so it can go straight into an <img> tag. zabio.url_expires_at is when it stops. After that, or after the image is deleted, the link answers 404, as it does for any link that is not exactly one Zabio made. Download the image within the hour if you want to keep it.

The link is a bearer link: anyone who has it can open the image until it expires. Share it only where you would share the image. Zabio stores each image for those 60 minutes, encrypted with your organization’s key, as a file of purpose image_output that your key can also read at GET /v1/files/{id}/content or delete at once with DELETE /v1/files/{id}, which ends the link too.

On a background generation the 60 minutes start when the images are made, not when you fetch the generation: poll it within the hour. Deleting the generation deletes its images and ends their links. A streamed request sends base64 only, so url with stream is a 400. If Zabio cannot store the images after the call was made, the answer carries them as b64_json as usual and says zabio.url_unavailable: true, so an image you paid for is never lost. The links are served with Cache-Control: private, no-store: no browser cache, proxy or edge keeps them.

A high-quality or large image can take longer than two minutes. Zabio keeps the connection alive: after 90 seconds without an answer it commits the 200 and writes a space every 15 seconds until the body arrives, so neither Zabio’s edge nor a proxy in front of your client closes it. The OpenAI SDKs parse the answer as usual. If the generation fails after that point, the body is {"created": ..., "data": [], "error": {"message", "type", "code"}} with status 200: check error before reading data. If your client cannot hold a connection that long, use background generation.

Add "zabio": {"background": true} to a generation and the answer comes at once, with status 202 and an id:

{"id": "img_...", "object": "image.generation", "status": "queued", "created_at": 1793655000, "model": "auto", "expires_at": 1796247000, "zabio": {"background": true}}

Zabio runs the generation on its side. Fetch it with GET /v1/images/generations/{id}: while it runs the same object comes back with status queued or in_progress; once it is completed the answer is the images themselves, as a direct call returns them (data[0].b64_json, usage, zabio), with the id and the status beside them and zabio.file_id, the file that keeps them (also readable at GET /v1/files/{id}/content). A generation that failed comes back with status failed and an error with its code and message. If the images were made but could not be delivered, the charge is returned to your balance and the error says so. Poll every few seconds; most images are ready within a minute or two.

DELETE /v1/images/generations/{id} cancels a generation that is still queued, and nothing is billed; one already running answers 409 generation_in_progress, since it is at the vendor and will be billed. Once it has ended, DELETE removes it and its images.

GET /v1/images/generations lists your generations, newest first, as the status objects above (never the images: fetch one by its id for those). limit (1 to 100, default 20) sets the page size, after takes the last_id of the previous page, and status keeps one status (queued, in_progress, completed, failed or cancelled). The answer is {"object": "list", "data": [...], "first_id", "last_id", "has_more"}. A key in a project sees its project’s generations.

The images are kept until your organization’s retention runs out. "zabio": {"background": true, "expires_after": {"anchor": "created_at", "seconds": 7200}} deletes them sooner, here two hours after the request; a value longer than the retention is shortened to it, and expires_at in the answer is the time that applies.

These endpoints and fields are Zabio extensions, not part of OpenAI’s API. The rules of a direct call apply: the same models, the same auto, the same bill, charged when the images are made. Background generation needs credit on the account, checked when the request is queued and again when it runs: a generation whose balance reached zero while it waited ends failed with insufficient_credits and bills nothing. The OpenAI SDKs pass the zabio object through extra_body.

An edit cannot run in the background: Zabio never stores the images you send to edit, and a queued edit would have to.

POST /v1/images/edits takes OpenAI’s multipart form: one or more image[] files, an optional mask, and the fields of a generation as form values.

Terminal window
curl https://api.zabio.ai/v1/images/edits \
-H "Authorization: Bearer insert your zabio.ai key here" \
-F model=auto \
-F "image[]=@room.png" \
-F "image[]=@lamp.png" \
-F prompt="Put the lamp on the table in the room"

The same edit can be sent as JSON, the shape of OpenAI’s API reference: images is a list of {"image_url": "data:image/png;base64,..."} or {"file_id": "file_..."}, mask is one of the same, and the other fields are a generation’s.

Terminal window
curl https://api.zabio.ai/v1/images/edits \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "prompt": "Put the lamp on the table in the room", "images": [{"image_url": "data:image/png;base64,iVBORw0..."}, {"image_url": "data:image/png;base64,iVBORw0..."}]}'

An image_url must be a base64 data URL: Zabio does not fetch images from web addresses on this endpoint (400 unsupported_value). A file_id must be a file of purpose user_data that your key can read; Zabio reads it and does not copy it. POST /v1/files takes purpose batch only today, so send data URLs or the multipart form. In JSON the base64 counts toward the size of the request, so the request holds about 75 MB of images.

The images are png, jpeg or webp, 50 MB each at most and 100 MB for the whole request. The mask is a png with an alpha channel, the size of the first image, which it applies to; only the OpenAI models take one. The Google models take about 14 MB of input images in all, the limit Google sets for images sent inline. The answer, the long call and the bill are a generation’s; a streamed edit sends image_edit.partial_image and image_edit.completed events. input_fidelity and markup are not supported.

With stream: true on an OpenAI image model, partial images arrive as they render and the result at the end:

event: image_generation.partial_image
data: {"type": "image_generation.partial_image", "b64_json": "...", "partial_image_index": 0, "output_format": "png", "created_at": 1793655000}
event: image_generation.completed
data: {"type": "image_generation.completed", "b64_json": "...", "usage": {...}, "size": "1024x1024", "quality": "high", "output_format": "png", "background": "opaque", "zabio": {...}}

partial_images (0 to 3) is how many partial images you want before the final one; each adds 100 image output tokens at OpenAI. A comment line keeps the stream alive while the model is silent. A failure after the stream opened is an error event. Only the OpenAI models stream; stream: true on one of them, or under auto when no streaming model can take the request, is a 400 unsupported_value on stream.

On the OpenAI and Google models, the tokens the vendor reports, at the model’s prices: text input tokens, image input tokens (the images you send to edit), image output tokens, and text or thinking output tokens on the Google models. On flux-2-dev@deepinfra, the price of each image of the size made. On the xAI models, the price of each image at the quality made, plus the model’s price per input image on an edit. The plan fee applies on top, and the auto fee when auto picked the model. A failed call bills nothing; a call that failed after its 200 was committed bills nothing either. A moderation block bills nothing, except where the vendor charges for it: xAI charges for an image its moderation refuses (0.02 USD on grok-imagine-image when Zabio tested it on 2026-11-04), so a refused request on an xAI model is billed what xAI charged plus the plan fee, and its error message says how much.

On a direct generation or edit, nothing. The prompt and the images you send go to the vendor that answers and the images come back to you; Zabio keeps none of them, and none appears in any log. With response_format: url, the images made are stored for 60 minutes, encrypted with your organization’s key, and anyone holding a link can open its image until it expires (see Images as URLs); the images you send to edit are still never stored. With per-key body logging on, the prompt and a reference to the images (count, type, bytes, size) are stored under your organization’s retention, never the image bytes.

A background generation is stored until it is fetched and beyond: the request (the prompt included) until the generation ends, and the images until you delete them or your organization’s retention runs out, both encrypted with your organization’s key. The usage views count the images (units.images) and the Usage tab shows “N images generated”.

Google’s images carry an invisible SynthID watermark. Tell your users that the images are AI-generated where you show them, as the vendors’ terms require.

Status code When
400 invalid_value a field is out of range (n above 4, a prompt above 32,000 characters, a size that is not WIDTHxHEIGHT, a quality or output_format outside the list), or a named model does not produce the size or take the quality; param says which
400 invalid_value zabio.expires_after without zabio.background, with another anchor than created_at, or under 3600 seconds; on the list, a limit, after or status outside its values; on a JSON edit, an image_url that is not a base64 data URL, or a file_id your key cannot read or that is not of purpose user_data
400 unsupported_value no model, or the named model, produces the output_format, a transparent background or a stream for this request; or response_format: url on a server where image URLs are not enabled
400 invalid_value response_format other than b64_json or url, or url with stream
400 unsupported_value on an edit: a mask on a model that takes none, zabio.background, or an image_url that is a web address; on a generation, zabio.background on the free plan
400 unsupported_parameter style (a legacy field); on an edit, input_fidelity or markup
400 missing_file, missing_field, invalid_multipart, invalid_json an edit without an image[] file or an images list, or a body that is neither multipart nor JSON
400 moderation_blocked the vendor’s safety system refused the prompt or withheld the image; the message carries the vendor’s categories where it names any and the stage (input or output), never its text; on the xAI models it also says what the refused request was billed, since xAI charges for it
400 unsupported_for_model the named model is not an image model
402 insufficient_credits no credit left; on a background generation, also the error of one whose balance reached zero before it ran
404 model_not_found not a model, not allowed for the key, or retired
404 not_found an image link that expired, was changed, or whose image was deleted
409 generation_in_progress DELETE on a background generation that is running
410 expired a completed background generation whose images were deleted or expired
413 payload_too_large the JSON body is above 256 KiB
413 media_too_large an edit’s image above 50 MB, its body above 100 MB, or its images above a named model’s limit
503 no_provider_available the provider for this model is not configured, or has not cleared Zabio’s account for it yet
503 background_unavailable background generation is not available on this server

The key’s requests per minute limit applies; the token limit does not count image tokens.

import base64
from openai import OpenAI
client = OpenAI(base_url="https://api.zabio.ai/v1", api_key="insert your zabio.ai key here")
res = client.images.generate(model="auto", prompt="A red lighthouse on a rocky shore at dusk, flat illustration", size="1024x1024")
with open("lighthouse.jpg", "wb") as f:
f.write(base64.b64decode(res.data[0].b64_json))
print(res.model_extra["zabio"]["model_served"], res.model_extra["zabio"]["cost_micros"])
# An edit
res = client.images.edit(model="auto", image=[open("room.png", "rb"), open("lamp.png", "rb")], prompt="Put the lamp on the table in the room")
# Your background generations, newest first
page = client.get("/images/generations", options={"params": {"limit": 10, "status": "completed"}}, cast_to=dict)
# In the background
import time
job = client.post("/images/generations", body={"model": "auto", "prompt": "A city at night, detailed, 4K", "size": "4096x4096", "zabio": {"background": True}}, cast_to=dict)
while (got := client.get(f"/images/generations/{job['id']}", cast_to=dict))["status"] in ("queued", "in_progress"):
time.sleep(5)
image = base64.b64decode(got["data"][0]["b64_json"])
Terminal window
zabio image -o lighthouse.jpg "A red lighthouse on a rocky shore at dusk"
zabio image -o poster.png --model gpt-image-2.5-flare --size 1024x1536 --quality high --format png "A poster for a jazz night"
zabio image edit -o room-with-lamp.png "Put the lamp on the table in the room" room.png lamp.png
zabio image edit -o hat.png --mask mask.png "Add a red hat" portrait.png
zabio image --background --size 4096x4096 "A city at night, detailed, 4K" # prints img_...
zabio image status img_...
zabio image get -o city.png img_...