Two endpoints in the format of the OpenAI Images API: POST / creates images from a description, and POST / creates them from one or more images. Images come back in the response, in base64: Moon AI keeps no copy and does not provide a hosted URL.
Create an imageLink to the Create an image section
POST https://api.realmoon.ai/v1/images/generations- Authentication: an
sk-moon-…key with theinferencescope (see Authentication). - Body: JSON, 1 MB at most.
- Response: an image object (see Response).
ParametersLink to the Parameters section
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | yes | An image model from GET /. |
prompt | string | yes | Description of the image, 8,000 characters at most. |
n | integer | no | Number of images, from 1 to 4. 1 by default. Billed per image produced. |
size | string | no | auto or WIDTHxHEIGHT, from 64 to 8,192 pixels per side. Used to pick the closest aspect ratio and size the model offers (see Size and aspect ratio). |
aspect_ratio | string | no | One of the model’s aspect ratios (aspect_ratios in /v1/models), for example 1:1, 16:9, 2:3. Takes precedence over the aspect ratio derived from size. |
image_size | string | no | 1K, 2K or 4K, for models that offer several sizes (image_sizes). Takes precedence over the size derived from size. |
quality | string | no | auto, low, medium, high, standard or hd. Accepted for compatibility, with no effect: the model chooses on its own. |
response_format | string | no | b64_json (default) or url. With url, the image comes as a data: URL. |
stream | boolean | no | Only false is accepted. |
The user, style, moderation, background, output_compression, output_format, partial_images and input_fidelity parameters are accepted and have no effect. Any other parameter is rejected with 400 . An image sent in image or images is rejected here: use /.
ExampleLink to the Example section
curl https://api.realmoon.ai/v1/images/generations \
-H "Authorization: Bearer $MOONAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemini-3.1-flash-image",
"prompt": "A retro poster of a rocket above Paris",
"size": "1024x1536"
}' \
| jq -r '.data[0].b64_json' | base64 --decode > poster.pngimport base64
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.realmoon.ai/v1", api_key=os.environ["MOONAI_API_KEY"])
img = client.images.generate(
model="google/gemini-3.1-flash-image",
prompt="A retro poster of a rocket above Paris",
size="1024x1536",
# Moon AI-specific parameter: passed through extra_body.
extra_body={"image_size": "2K"},
)
with open("poster.png", "wb") as f:
f.write(base64.b64decode(img.data[0].b64_json))import fs from 'node:fs';
import OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://api.realmoon.ai/v1', apiKey: process.env.MOONAI_API_KEY });
const img = await client.images.generate({
model: 'google/gemini-3.1-flash-image',
prompt: 'A retro poster of a rocket above Paris',
size: '1024x1536',
// Moon AI-specific parameter, sent as is.
image_size: '2K',
});
fs.writeFileSync('poster.png', Buffer.from(img.data[0].b64_json, 'base64'));Size and aspect ratioLink to the Size and aspect ratio section
When you give size without aspect_ratio or image_size:
- the aspect ratio used is the model’s closest match to the width-to-height ratio;
- the size used is
1Kif the longest side is 1,100 pixels or less,2Kup to 2,200 pixels, and4Kbeyond that (or the model’s largest size).
So the image produced can have slightly different dimensions from size. With no size, aspect_ratio or image_size, the model decides.
Edit an imageLink to the Edit an image section
POST https://api.realmoon.ai/v1/images/edits- Authentication: an
sk-moon-…key with theinferencescope. - Body:
multipart/(with theform-data Content-Lengthheader) or JSON, 90 MB at most. - Response: an image object (see Response).
ParametersLink to the Parameters section
All the generation parameters apply, plus the images:
| Parameter | Type | Required | Description |
|---|---|---|---|
image | file, string or array | yes | In multipart: one or more files in the image or image[] field. In JSON: a string or a list of strings (base64 data: URL, bare base64, or https:// URL). |
images | array | no | In JSON, the OpenAI shape: [{"image_url": . Added to image. |
mask | no | Not accepted (400 ): describe the change in the prompt instead. |
| Rule | Value |
|---|---|
| Image types | PNG, JPEG, WebP, GIF |
| Size of one image | 20 MB and 40 megapixels at most |
| Number of images | The model’s max_input_images in /v1/models |
| Body size | 90 MB at most |
An image given as an https:// URL is fetched by the model provider, never by Moon AI.
ExampleLink to the Example section
curl https://api.realmoon.ai/v1/images/edits \
-H "Authorization: Bearer $MOONAI_API_KEY" \
-F model=google/gemini-3.1-flash-image \
-F prompt="Same photo, in black and white" \
-F image=@photo.pngedit = client.images.edit(
model="google/gemini-3.1-flash-image",
image=open("photo.png", "rb"),
prompt="Same photo, in black and white",
)
with open("photo-bw.png", "wb") as f:
f.write(base64.b64decode(edit.data[0].b64_json))
# Several images: pass a list.
combo = client.images.edit(
model="google/gemini-3.1-flash-image",
image=[open("product.png", "rb"), open("setting.png", "rb")],
prompt="The product from the first image placed in the setting of the second",
)const edit = await client.images.edit({
model: 'google/gemini-3.1-flash-image',
image: fs.createReadStream('photo.png'),
prompt: 'Same photo, in black and white',
});
fs.writeFileSync('photo-bw.png', Buffer.from(edit.data[0].b64_json, 'base64'));The same call in JSON, with one image in base64 and one image at a URL:
{
"model": "google/gemini-3.1-flash-image",
"prompt": "The product from the first image placed in the setting of the second",
"images": [
{ "image_url": "data:image/png;base64,iVBORw0KGgo…" },
{ "image_url": "https://example.com/setting.jpg" }
]
}ResponseLink to the Response section
Both endpoints return the same object.
{
"created": 1793577600,
"model": "google/gemini-3.1-flash-image",
"data": [
{ "b64_json": "iVBORw0KGgoAAAANSUhEUgAA…", "output_format": "png" }
],
"usage": { "images": 1, "credits": 650 }
}| Field | Type | Description |
|---|---|---|
created | integer | Timestamp in Unix seconds. |
model | string | The model you called. |
data[].b64_json | string | The image in base64, with response_format: . |
data[].url | string | The image as a data: URL, with response_format: . |
data[].output_format | string | File format: png, jpeg, webp, gif or svg. |
usage.images | integer | Number of images produced. |
usage.credits | integer | Credits charged for the request. |
When you ask for several images and some of them fail, the response contains only the ones that were produced: compare usage.images with n.
Headers: X-Moon-Request-Id, X-Moon-Prompt-Storage: , X-Moon-Blur: (Moon Blur does not apply to images or to their description) and Cache-Control: .
ModelsLink to the Models section
List as of November 5, 2026; the current list, with prices, comes from GET /.
| Model | Aspect ratios | Sizes | Input images |
|---|---|---|---|
google/ (Nano Banana 2) | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9 | 1K, 2K, 4K | 8 |
google/ (Nano Banana Pro) | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9 | 1K, 2K, 4K | 8 |
google/ | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 | 1K, 2K, 4K | 4 |
openai/, openai/ | 1:1, 3:2, 2:3 | single size | 4 |
black-forest-labs/, flux.2-max, flux.2-klein-4b, flux-3-image | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 | single size | 4 |
bytedance-seed/, seedream-5-0-lite | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 | single size | 4 |
qwen/, x-ai/ | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 | single size | 4 |
recraft/, recraft/ (SVG output) | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 | single size | 4 |
image_size is accepted only by models with several sizes.
CostsLink to the Costs section
- Before the call, the request holds the most it can cost:
max_credits_per_unit(or the value for the requested size) times the number of images, plus the images you send. - Afterward, you pay the actual cost reported by the provider (or, failing that, the list price), and the rest of the hold is released right away.
- A request that fails, is refused before anything is created, or is cut off by our timeout costs nothing.
- A model that answers with text instead of an image (safety refusal, misunderstood prompt) charges for the text it wrote, and the API responds with
400: rephrase the prompt.content_policy_violation
See Costs and wallet for holds and settlement.
LimitsLink to the Limits section
- All the images in a request are made at the same time; the request responds within about 90 seconds at most.
- At most 8 image requests in flight at once per workspace (beyond that:
429,too_many_concurrent Retry-After:).5
ErrorsLink to the Errors section
| HTTP | code | Meaning |
|---|---|---|
| 400 | missing_required_parameter | model, prompt or image is missing. |
| 400 | unknown_parameter | Unknown parameter (param names it). |
| 400 | unsupported_value | A value the model does not accept (n, size, aspect_ratio, image_size, quality, response_format). |
| 400 | unsupported_parameter | mask, stream, image_size on a single-size model, an image sent to /generations, or a parameter the model rejects (with a hint). |
| 400 | unsupported_model | The model does not do editing. |
| 400 | string_above_max_length | Prompt longer than 8,000 characters. |
| 400 | too_many_images | More images than max_input_images. |
| 400 | invalid_image | Unreadable image, wrong type, or more than 40 megapixels. |
| 400 | content_policy_violation | The model’s safety system rejected the image. |
| 400 | invalid_request_error | The model rejected the request without further detail. |
| 400 | null | Invalid JSON body, invalid base64, unexpected file field or unreadable form. |
| 403 | org_policy | Your organization’s rules refuse this model. |
| 404 | model_not_found | Unknown model, or one that does not serve this endpoint. |
| 411 | length_required | Multipart upload without a Content-Length header. |
| 413 | request_too_large, file_too_large | Body or image too large. |
| 429 | too_many_concurrent | Too many image requests in flight in the workspace. |
| 429 | upstream_rate_limited | The model is overloaded: retry after Retry-After (10 seconds). |
| 502, 504 | upstream_error, upstream_timeout | The model failed or did not respond in time. |
| 503 | upstream_unavailable | The service is temporarily unavailable. |
Key, balance and spend cap errors are in Errors.
See alsoLink to the See also section
- Models: the
aspect_ratios,image_sizesandmax_input_imagesfields. - Balance and request cost: look up the cost again with
X-Moon-Request-Id.