API reference
Images: generation
POST /v1/images/generations in the shape of OpenAI's Images API: what it accepts, what comes back, what it costs and what is refused.
Last updated: 2026-10-11
One request, one answer: a picture takes seconds, so there is no job to follow as with video. The shape is OpenAI's Images API. Code written for it works once it uses the base URL and the key of AI Tokens, a model of this page instead of OpenAI's, and only the fields below: What works where and OpenAI SDK migration list the differences.
In one request
curl -s https://api.aitokens.ch/v1/images/generations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "z-image-turbo", "prompt": "A cozy fantasy tavern at night, warm candle light, wooden beams, digital painting"}' \
| python3 -c 'import sys, json, base64; open("tavern.png", "wb").write(base64.b64decode(json.load(sys.stdin)["data"][0]["b64_json"]))'
Models
model |
Makes | size |
n |
|---|---|---|---|
z-image-turbo (default) |
text → picture, PNG | 1024x1024 (default), 1024x1536 (portrait), 1536x1024 (landscape) |
1 to 4 |
A square picture takes about 9 seconds, a tall or wide one about 13.
The request
POST /v1/images/generations, JSON:
| Field | ||
|---|---|---|
prompt |
required | What to draw, up to 4000 characters. |
model |
optional | z-image-turbo. |
size |
optional | One of the sizes above, as width x height. |
n |
optional | 1 to 4 pictures, made one after the other. |
response_format |
optional | b64_json (default): the PNG inside the answer. url: a link to it, valid 24 hours. |
seed |
optional | A whole number from 0 to 2147483647. The same prompt and seed give the same picture again. |
Every other field is refused with a 400 that names it, OpenAI's quality,
style, background and output_format included, and a value the model
cannot take is refused, not rounded: ask for "size": "512x512" and you get a
400 that lists the sizes.
The answer
{
"created": 1791297500,
"data": [
{"b64_json": "iVBORw0KGgoAAAANSUhEUgAA…"}
]
}
With "response_format": "url" each picture is a link instead:
{"created": 1791297500, "data": [{"url": "https://api.aitokens.ch/v1/images/file/…?expires=…&signature=…"}]}
The link works without the key, so it can go straight into a web page or a game, for 24 hours. Then the file is deleted: download what you want to keep.
Prompts that work
- Say the subject, the setting, the style, the light and the framing: A low-poly red fox in an autumn forest, side view, soft morning light.
- Italian works. For words written in the picture (a sign, a title, a label), write the prompt in English and put the exact words in quotes: A wooden sign reading "Benvenuti" at the entrance of a medieval village.
- For variants of the same idea, ask
npictures, or keep the prompt and change theseed.
What is refused
Before the picture is made, one of our own models reads the prompt; after, the picture itself is checked before it reaches you. These are refused:
- sexual content or nudity;
- minors in any unsafe, violent or sexual situation;
- real, identifiable people (public figures, people named in the prompt), and any likeness of them;
- blood, gore, torture, self-harm; realistic violence against people or animals;
- hate, harassment, extremist symbols;
- realistic illegal activity;
- well-known copyrighted characters and brand logos.
Fantasy and game scenes with stylised action are fine: knights fighting, explosions without victims, monsters, space battles, landscapes, objects, animals, characters you invent.
- A refused prompt gets a
400with codeprompt_refusedand the reason. - A picture that fails the check after it is made is not delivered and not
charged. If you asked for several, you get those that passed; if none did,
a
400with codeoutput_refused. - If a check cannot answer, nothing goes through: a
503 moderation_unavailable. Try again a minute later.
Which checks run is decided zone by zone by whoever runs the machines. Today both run everywhere for pictures.
Every picture is marked as made by AI inside the PNG, for article 50 of
the EU AI Act: the IPTC field DigitalSourceType says trainedAlgorithmicMedia,
the mark that image sites and editors read, and a text comment says the same.
Editors and social networks can strip metadata, so where you publish a
picture, say it in words too. What is in the file, how to read it and what
removes it: AI-generated media.
What we keep
- The prompt: not kept.
- The pictures: not kept with
b64_json. Withurl, for 24 hours in AI Tokens's storage, then deleted. - The usage: model, number of pictures, cost and time, as for every request.
Price
Per picture delivered, at the price in Pricing: in CHF, or in credits where AI Tokens works with credits. A refused picture, or a failed request, costs nothing.
Errors
| Status | error.code |
When |
|---|---|---|
| 400 | unknown_parameter |
a field this API does not have; error.param names it |
| 400 | missing_required_parameter |
no prompt, or an empty one |
| 400 | string_above_max_length |
a prompt longer than 4000 characters |
| 400 | model_not_found |
a model that does not make pictures here |
| 400 | invalid_value |
a size, n, response_format or seed the model cannot take |
| 400 | prompt_refused |
the prompt goes against the rules |
| 400 | output_refused |
every picture failed the check after it was made |
| 401 | missing or wrong key | |
| 402 | insufficient_credits |
no credits left |
| 429 | more than 30 requests in a minute | |
| 502 | engine_error |
the machine could not make the picture |
| 503 | no_engine_available |
no machine is making this model right now |
| 503 | moderation_unavailable |
a check could not answer |
Errors have OpenAI's shape:
{"error": {"message": "'z-image-turbo' makes pictures of 1024x1024, 1024x1536, 1536x1024.", "type": "invalid_request_error", "param": "size", "code": "invalid_value"}}
Examples
Python, with OpenAI's SDK
import base64
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.aitokens.ch/v1", api_key=os.environ["API_KEY"])
result = client.images.generate(
model="z-image-turbo",
prompt="A low-poly red fox in an autumn forest, side view, soft morning light",
size="1536x1024",
)
with open("fox.png", "wb") as f:
f.write(base64.b64decode(result.data[0].b64_json))
The SDK has no seed argument: pass it with extra_body.
for seed in (1, 2, 3):
result = client.images.generate(
model="z-image-turbo",
prompt="A treasure chest on a beach at sunset, stylised game art",
extra_body={"seed": seed},
)
with open(f"chest-{seed}.png", "wb") as f:
f.write(base64.b64decode(result.data[0].b64_json))
JavaScript (Node 18+)
import { writeFile } from "node:fs/promises";
const res = await fetch("https://api.aitokens.ch/v1/images/generations", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
prompt: "Four flat game icons: a sword, a shield, a potion, a key, white background",
n: 2,
}),
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
for (const [i, picture] of body.data.entries()) {
await writeFile(`icons-${i + 1}.png`, Buffer.from(picture.b64_json, "base64"));
}
Save it as icons.mjs and run it with node icons.mjs.