Skip to main content

AICU Image API

Alpha. One key, one endpoint — switch between GPT-Image-2 and Nano Banana 2 (Gemini) with a single parameter.

See what it does before you read the reference​

Everything below is the reference. If you would rather see it working first, these three came out of a single request each, through the curl on this page.

Six characters in one image

Six named characters in one image, each keeping their own design. The hard part is not drawing six people — it is stopping them from blending into each other.

A four-panel comic

A four-panel comic in one request. Same character in all four, with the panels laid out by the model rather than composited afterwards.

A nine-pose sprite sheet

A nine-pose sprite sheet on a flat background, ready to cut up for a game or a VTuber rig.

The walkthroughs​

These are written to be read, not looked up — start here if the reference below is heavy going.

What it shows
GPT-Image-2.5 — the API manual, translatedEvery parameter, in Japanese, with what each one actually changes
The prompting guide, translatedHow to write prompts for this model specifically, plus a batch-generation sample
Image generation guide, Part 1Choosing an API, multi-turn editing, streaming
Trying it on our own charactersThe three images above, with the exact prompts that produced them
AiCutyBench — does it actually reproduce a character?Measured hit rate over repeated runs, not a demo cherry-pick

Base URL​

https://api.aicu.ai/v1

Authentication​

You need an API key with the images scope. Issue one from the dashboard.

Authorization: Bearer aicu_live_xxx

GET /v1/images/models​

The image models available to you (no auth required).

curl https://api.aicu.ai/v1/images/models
modelProviderReference imageAspect ratioNotes
gpt-image-2OpenAI○-High quality, strong character consistency
nano-bananaGemini○○Fast, supports aspect ratios
sd3.5-largeStability--Stable Diffusion 3.5 Large

Model names from wherever you are migrating (gemini-3.1-flash-image-preview, for example) are accepted as-is.

POST /v1/images/generations​

curl -X POST https://api.aicu.ai/v1/images/generations \
-H "Authorization: Bearer aicu_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana",
"prompt": "a red apple on a white table, product photo",
"aspect_ratio": "16:9"
}'

Request​

ParameterTypeDescription
promptstringRequired. What to generate
modelstringDefaults to gpt-image-2
aspect_ratiostring16:9 and similar (nano-banana only)
sizestring1024x1024, 1536x1024, etc.
qualitystringlow / medium / high
reference_imagestringReference image as base64 (data URLs accepted)
characterstringReference-image preset. Takes precedence over model
seednumberFor reproducible output (sd3.5-large)
forcebooleantrue bypasses the cache and regenerates

Response​

Comes back in the OpenAI-compatible shape.

{
"data": [{ "b64_json": "iVBORw0KG..." }],
"cached": false
}

Regenerating with identical inputs is served from the R2 cache, and cache hits are free (cached: true).

⏱ Generations that take more than 60 seconds are delivered by email​

Some generations take longer than 60 seconds — high quality settings (xhigh / max), requests with a reference image, large sizes, and gpt-image-2 (measured at 113–171 seconds).

These are not meant to be awaited inside an app. Most HTTP clients give up after 30–60 seconds, and Cloudflare's edge cuts the connection at 100 seconds. When that happens it looks like a failure, but the generation finishes on our side and you are billed exactly once. Do not retry because the request timed out — every retry throws away a few thousand AP.

The result of a generation that runs past 60 seconds is delivered by email, with the image attached, to the key owner (and to notify_email if you set it). Decline with "notify_on_timeout": false — but then you have one fewer way to receive the result.

Other ways to get the image back:

  1. GET /v1/images/status/{id} — the id from the response body, or the X-AICU-Image-Id header (sent as soon as the connection opens when streaming). Use this one. It fetches exactly the generation you asked for
  2. GET /v1/images/recent — lists your recent generations with their ids. ⚠️ With concurrent generations on the same key this returns someone else's image; prefer status/{id}
  3. The usage history in the dashboard

If you are building an app that shows the image right away:

  • Use quality low / medium / high at 1024x1024. GET /v1/images/models reports typical_latency_s_by_quality, so you can check the expected time before you call
  • Pass "stream": true — partial images keep flowing over SSE, so the connection never goes idle (not compatible with character / reference_image / mask; plain generation only)
  • Pass "notify_email" (plus "app_name") to notify your own end user directly when the image is ready
  • If you really want it synchronously, raise your client timeout to 240 seconds or more (--max-time 240 for curl, timeout=240 for requests)

Video generation is asynchronous from the start: POST /v1/video/generate returns 202 with an id, and you poll GET /v1/video/status/{id}.

Keeping a character consistent with reference images​

Option 1: registered character presets (character) — the official AiCuty characters are already registered, so you get a consistent look without preparing any reference image.

# List them (no auth): returns slug, model, rate and rights notices
curl https://api.aicu.ai/v1/images/characters

# Use one: just pass the slug in character (case-insensitive)
curl -X POST https://api.aicu.ai/v1/images/generations \
-H "Authorization: Bearer aicu_live_xxx" \
-H "Content-Type: application/json" \
-d '{"character": "SakiNoir", "prompt": "カフェで読書している、午後の光"}'

Registered presets (reference images included, gpt-image-2): SakiNoir / NaoVerde / MeiSoleil / ElenaBloom / MinaAzure / elec_sheep and more. The rights field in the listing API carries the credit line and the rights holder — follow it when you publish.

Option 2: your own reference image (reference_image) — nothing has to be uploaded to R2 beforehand. Just send the image you have as base64.

import base64, requests

ref = base64.b64encode(open("character.png", "rb").read()).decode()
res = requests.post(
"https://api.aicu.ai/v1/images/generations",
headers={"Authorization": "Bearer aicu_live_xxx"},
json={
"model": "nano-banana",
"prompt": "Using the reference image as a character guide, draw the same character waving hello",
"reference_image": ref,
"aspect_ratio": "16:9",
},
timeout=180,
)
open("out.png", "wb").write(base64.b64decode(res.json()["data"][0]["b64_json"]))

Migration guide: swapping out OpenAI / Gemini​

Migrating from Gemini (google-genai)​

- client = genai.Client(api_key=GEMINI_API_KEY)
- response = client.models.generate_content(
- model="gemini-3.1-flash-image-preview",
- contents=prompt,
- config=types.GenerateContentConfig(
- response_modalities=["IMAGE", "TEXT"],
- image_config=types.ImageConfig(aspect_ratio="16:9"),
- ),
- )
+ res = requests.post(
+ "https://api.aicu.ai/v1/images/generations",
+ headers={"Authorization": f"Bearer {AICU_API_KEY}"},
+ json={"model": "nano-banana", "prompt": prompt, "aspect_ratio": "16:9"},
+ )

Migrating from the OpenAI SDK​

- client = OpenAI(api_key=OPENAI_API_KEY)
- res = client.images.generate(model="gpt-image-2", prompt=prompt, size="1024x1024")
+ res = requests.post(
+ "https://api.aicu.ai/v1/images/generations",
+ headers={"Authorization": f"Bearer {AICU_API_KEY}"},
+ json={"model": "gpt-image-2", "prompt": prompt, "size": "1024x1024"},
+ )

Once you switch, there are no more per-provider keys to manage, and usage and cost land in a single dashboard.

Errors​

StatusMeaning
401Invalid API key
403Key does not have the images scope
402Insufficient credits
404Unknown model
503Provider key not configured / generation failed

Checking usage​

curl https://api.aicu.ai/v1/usage -H "Authorization: Bearer aicu_live_xxx"

The dashboard's usage history can be filtered down to images.

Support​

© 2026 AICU Inc.