Images
Create pictures from a description, then edit, vary and enlarge them. Everything is
priced per picture and drawn asynchronously: a request answers 202 at once with a
generation in rendering, and you poll it until its pictures are drawn — usually 10–40
seconds.
| Quality | Size | Credits per picture |
|---|---|---|
standard | about 1K (e.g. 1024×1024, 1344×768) | 10 |
pro-2k | about 2K | 30 |
pro-4k | about 4K (e.g. 6336×2688 at 21:9) | 50 |
These are the current prices; read them live from GET /v1/images/options. A request is
reserved in full when you send it and charged only for the pictures that are drawn —
a picture that is refused, times out or is interrupted is refunded.
Authorization
Every endpoint here needs the images scope. A key without it gets 403 SCOPE_FORBIDDEN;
add the scope on the API keys page.
Options
/v1/images/options API key The shapes, styles and qualities on offer, the live prices and the limits. available: false on a Pro quality means it cannot be drawn right now (Standard still works).
{
"status": "success",
"aspectRatios": ["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"],
"styles": ["none", "photo", "cinematic", "product", "portrait", "illustration", "anime",
"watercolor", "oil-painting", "3d-render", "flat-vector", "line-art", "pixel-art"],
"varieties": ["low", "normal", "high"],
"qualities": [
{ "id": "standard", "credits": 10, "size": "1K", "available": true },
{ "id": "pro-2k", "credits": 30, "size": "2K", "available": true },
{ "id": "pro-4k", "credits": 50, "size": "4K", "available": true }
],
"presets": [
{ "id": "id-photo", "outfits": ["suit-tie", "suit", "blazer", "own"], "backgrounds": ["white", "blue", "grey"],
"sources": ["auto", "photo"], "aspectRatio": "3:4",
"qualities": [{ "id": "standard", "available": true }, { "id": "pro-2k", "available": true }, { "id": "pro-4k", "available": true }] }
],
"enhance": { "credits": 0, "perMinute": 10, "perDay": 200 },
"limits": { "maxCount": 4, "maxReferences": 4, "maxInlineReferenceBytes": 7340032,
"maxPromptChars": 4000, "maxNegativeChars": 1000, "maxActivePerOrg": 3 }
}Create pictures
/v1/images/generations API key JSON body
prompt * string | What to draw, up to 4,000 characters. Lao, Thai and English all work. |
negativePrompt string | What to leave out, up to 1,000 characters, e.g. "people, text, blur". |
aspectRatio string | One of 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9. Default: 1:1. |
count integer | Pictures to draw, 1–4. Default: 1. Each is billed. |
quality standard | pro-2k | pro-4k | Default: standard (about 1K). Pro draws at 2K or 4K. |
style string | A preset: none, photo, cinematic, product, portrait, illustration, anime, watercolor, oil-painting, 3d-render, flat-vector, line-art, pixel-art. Default: none. |
seed integer | 0–2147483647. Picture n of a request uses seed + n. Best effort — see Seeds. |
variety low | normal | high | How far pictures may stray from the most literal reading. Default: normal. |
allowText boolean | Draw the words you put in quotation marks. Default: false — no lettering at all. |
references Reference[] | Up to 4 reference pictures. See Reference pictures. |
saveToFiles boolean | Also file each picture in the person’s Files. Default: false for an API key. |
curl -X POST https://ai-api.vanguardinitiative.com/v1/images/generations \
-H "authorization: Bearer $API_KEY" \
-H 'content-type: application/json' \
-d '{
"prompt": "Golden temple roofs of Vientiane at dawn, mist, monks walking",
"aspectRatio": "16:9",
"count": 2,
"style": "cinematic"
}'import { writeFile } from 'node:fs/promises';
const API = 'https://ai-api.vanguardinitiative.com';
const headers = { authorization: `Bearer ${process.env.API_KEY}`, 'content-type': 'application/json' };
const res = await fetch(`${API}/v1/images/generations`, {
method: 'POST',
headers,
body: JSON.stringify({ prompt: 'Golden temple roofs of Vientiane at dawn', aspectRatio: '16:9', count: 2, style: 'cinematic' }),
});
let { generation } = await res.json();
// Poll until it is drawn (?wait holds the answer up to 25 s while it renders).
while (generation.state === 'rendering') {
const r = await fetch(`${API}/v1/images/generations/${generation.id}?wait=20`, { headers });
({ generation } = await r.json());
}
for (const img of generation.images.filter((i) => i.state === 'done')) {
const pic = await fetch(`${API}${img.url}?format=png`, { headers }); // follows a redirect for large pictures
await writeFile(`${img.id}.png`, Buffer.from(await pic.arrayBuffer()));
}import requests, time
API = "https://ai-api.vanguardinitiative.com"
H = {"authorization": f"Bearer {API_KEY}"}
r = requests.post(f"{API}/v1/images/generations", headers=H, json={
"prompt": "Golden temple roofs of Vientiane at dawn, mist, monks walking",
"aspectRatio": "16:9", "count": 2, "style": "cinematic",
})
gen = r.json()["generation"]
while gen["state"] == "rendering":
gen = requests.get(f"{API}/v1/images/generations/{gen['id']}?wait=20", headers=H).json()["generation"]
for img in gen["images"]:
if img["state"] == "done":
pic = requests.get(f"{API}{img['url']}?format=png", headers=H) # redirects are followed
open(f"{img['id']}.png", "wb").write(pic.content){
"status": "success",
"credits": 20,
"generation": {
"id": "533ea88f-bccc-4f75-a515-fc13d31bbd0c",
"mode": "generate",
"state": "rendering",
"quality": "standard",
"options": { "aspectRatio": "16:9", "count": 2, "style": "cinematic", "variety": "normal", "allowText": false },
"credits": { "reserved": 20, "charged": 0, "refunded": 0 },
"images": [
{ "id": "9fcb4933-…", "index": 0, "state": "pending", "seed": 1497173827, "url": null },
{ "id": "0ca5785f-…", "index": 1, "state": "pending", "seed": 1497173828, "url": null }
]
}
}Poll a generation
/v1/images/generations/{id} API key ?wait=N (up to 25) holds the answer while the generation is still drawing, so a
script can poll without sleeping. The generation’s state ends as:
| state | Meaning |
|---|---|
rendering | Still drawing. |
done | Every picture was drawn. |
partial | Some were drawn; the others carry an error and were refunded. |
failed | None were drawn; the whole reservation was refunded. |
Each picture’s state is pending, done or failed. A failed picture’s error.code is one of BLOCKED (refused by the safety filter), NO_IMAGE (nothing
drawable — describe the subject, colours and setting), TIMEOUT, BUSY or INTERRUPTED (a server restart).
{
"status": "success",
"generation": {
"id": "533ea88f-bccc-4f75-a515-fc13d31bbd0c",
"mode": "generate",
"state": "partial",
"prompt": "Golden temple roofs of Vientiane at dawn, mist, monks walking",
"negativePrompt": null,
"quality": "standard",
"options": { "aspectRatio": "16:9", "count": 2, "style": "cinematic", "variety": "normal", "allowText": false },
"parentImageId": null,
"references": [],
"credits": { "reserved": 20, "charged": 10, "refunded": 10 },
"projectId": "95cb8d98-…",
"createdBy": { "type": "apiKey", "id": "3d1f…", "name": null },
"mine": true,
"images": [
{
"id": "9fcb4933-…", "index": 0, "state": "done",
"width": 1344, "height": 768, "seed": 1497173827,
"url": "/v1/images/9fcb4933-…/content",
"thumbnailUrl": "/v1/images/9fcb4933-…/content?size=thumb",
"fileId": null, "error": null
},
{
"id": "0ca5785f-…", "index": 1, "state": "failed", "seed": 1497173828,
"url": null, "thumbnailUrl": null, "fileId": null,
"error": { "code": "BLOCKED", "message": "This image was not drawn: the request was refused by the safety filter. Your credits were returned." }
}
],
"createdAt": "2026-10-13T12:50:01.000Z",
"finishedAt": "2026-10-13T12:50:13.000Z"
}
}Download a picture
/v1/images/{imageId}/content API key | Query | Meaning |
|---|---|
size=thumb | A 512px WebP preview. |
format=webp · png · jpeg | The full picture is stored as WebP; PNG and JPEG are converted once and kept. |
download=1 | Sent as an attachment. |
Pictures larger than 8 MB (a 4K PNG, for example) answer 302 with a link that expires
in 5 minutes — let your HTTP client follow redirects. A picture’s bytes never change, so
the response may be cached.
Reference pictures
Up to 4 pictures may guide a generation (an edit’s own picture counts as one). Each
is one of four forms, with an optional role:
Reference
{ "imageId": "…" } object | A picture made here before (any in your organization). |
{ "fileId": "…" } object | A picture in Files, up to 15 MB. Upload large pictures with Files first. |
{ "data": "<base64>", "mediaType": "image/png" } object | Inline PNG, JPEG or WebP, up to 7 MB decoded. The whole request must stay under 9.5 MB. |
{ "url": "https://…" } object | A public https picture, up to 10 MB, fetched once. |
role general | style | subject | Add to any form. style: match its look, not its content. subject: keep this person, product or object recognisable. Default: general. |
The API Gateway in front of us accepts at most 10 MB per request: send large pictures as
a fileId (upload them to Files first) or a url, and shrink inline pictures to about
1,600px before sending.
Text in pictures
By default a picture has no lettering at all. With allowText: true, only the words
you put in quotation marks are drawn — "…", “…”, «…» or 「…」, at most five, each up
to 120 characters — and nothing else is written. A request with allowText and nothing
quoted is refused with NO_QUOTED_TEXT. Phone numbers, emails, addresses, prices and
dates are never invented. Lao and Thai lettering is sometimes misspelled; check it.
Seeds
Every picture reports the seed it was drawn with, and sending the same prompt, options
and seed gives a similar picture — but not always an identical one. When a seed draws
nothing the picture is retried once with the next seed, and reports that one.
Edit a picture
/v1/images/edits API key JSON body
image * Reference | The picture to change (any reference form). |
instruction * string | What to change, up to 2,000 characters. Everything not mentioned stays. Optional with a preset. |
preset Preset | A ready-made edit — see ID photo. A preset takes the one picture only (no references). |
references Reference[] | Up to 3 more pictures, e.g. a product to place in it. |
aspectRatio string | Default: the listed shape nearest to the picture’s own. |
count, quality, style, seed, allowText, negativePrompt, saveToFiles | As for a generation. |
curl -X POST https://ai-api.vanguardinitiative.com/v1/images/edits \
-H "authorization: Bearer $API_KEY" \
-H 'content-type: application/json' \
-d '{
"image": { "imageId": "9fcb4933-…" },
"instruction": "Make it a rainy evening with warm lights inside"
}'ID photo
An edit with preset turns one photo of a person into a formal ID photo. The face, its
shape and the hair are kept as they are — nothing is beautified or restyled. Only the
clothes and the background change, and the photo comes out sharp. The picture is 3:4 (the
3 × 4 cm photo) unless you send aspectRatio, lettering is always off, and instruction becomes an optional note, such as “remove the glare on the glasses”.
preset
id * "id-photo" | The preset. |
outfit suit-tie | suit | blazer | own | A dark suit, white shirt and tie; a suit with an open collar; a dark blazer over a white blouse; or the person’s own clothes, neatened. Default: suit-tie. |
background white | blue | grey | Plain white, light blue or light grey, evenly lit. Default: white. |
source auto | photo | document | auto: we look at `image` ourselves — an ordinary photo is used as it is, a passport or ID card page is refused with DOCUMENT_NOT_ACCEPTED. photo skips that look. document is refused. Default: auto. |
curl -X POST https://ai-api.vanguardinitiative.com/v1/images/edits \
-H "authorization: Bearer $API_KEY" \
-H 'content-type: application/json' \
-d '{
"image": { "data": "<base64 JPEG photo of the person>", "mediaType": "image/jpeg" },
"preset": { "id": "id-photo", "outfit": "suit-tie", "background": "white" },
"quality": "pro-2k"
}'Send an ordinary photo of the person — a phone photo against a plain wall works; sideways
is fine, we turn it upright. We look at it first (source: "auto", the default): a
passport or ID card page is refused with DOCUMENT_NOT_ACCEPTED (the image model will not
edit an identity document), and a picture with no person with NO_PERSON_FOUND — both
before anything is charged. A photo of the person holding a document is cut to the person.
Prices are the usual ones per picture;
which qualities can draw an ID photo right now is listed under presets in GET /v1/images/options.
Variations
/v1/images/variations API key JSON body
imageId * string | The picture to vary. |
strength subtle | strong | subtle keeps the composition and changes details; strong keeps the subject and style and re-composes. Default: subtle. |
count integer | 1–4. Default: 1. |
quality string | Default: the original’s quality. |
seed, saveToFiles | As for a generation. |
Upscale
/v1/images/upscales API key JSON body
imageId * string | The picture to enlarge. |
size * 2k | 4k | Billed as Pro 2K or Pro 4K. Refused with ALREADY_THAT_SIZE when the picture is already that large. |
saveToFiles boolean | As for a generation. |
An upscale redraws the picture at the larger size, keeping its content, colours and any
text; very fine detail may change. Edits, variations and upscales all answer 202 with a
generation (its mode says which) and are polled the same way.
Improve a prompt
/v1/images/enhance-prompt API key Rewrites a short idea — in Lao, Thai or English — as a full English prompt. Quoted words
are kept exactly. Free; limited to 10 per minute per key and 200 per day per organization.
Body: prompt (required), style, mode (generate or edit), allowText.
curl -X POST https://ai-api.vanguardinitiative.com/v1/images/enhance-prompt \
-H "authorization: Bearer $API_KEY" \
-H 'content-type: application/json' \
-d '{"prompt":"ຮູບນາງສາວລາວນຸ່ງສິ້ນ ຢືນຢູ່ພະທາດຫຼວງ ມີປ້າຍ \"ສະບາຍດີ\"","allowText":true}'{
"status": "success",
"prompt": "A graceful young Lao woman in a traditional silk sinh stands before the golden spires of Pha That Luang in Vientiane, holding a wooden sign that reads \"ສະບາຍດີ\" …",
"original": "ຮູບນາງສາວລາວນຸ່ງສິ້ນ ຢືນຢູ່ພະທາດຫຼວງ ມີປ້າຍ \"ສະບາຍດີ\"",
"credits": 0
}List generations
/v1/images/generations API key Newest first. owner=me (default: what this key made) or owner=team (the whole
organization), in=<projectId> to narrow to a project, kind= to one kind (generate, edit, id-photo, variation, upscale — id-photo is the ID photo preset, edit the
other edits; mode= still works and counts both as edit), q= for words in the prompt, limit (up to 50, default 24) and cursor — pass the nextCursor of the previous page.
Delete
/v1/images/generations/{id} API key /v1/images/{imageId} API key Removes a generation, or one picture, from the gallery (a key may delete what it made). Copies in Files stay. Deleted pictures are purged after 30 days.
Limits
| Limit | Value |
|---|---|
| Pictures per request | 4 |
| Generations drawing at once per organization | 3 (more answer 429 TOO_MANY_GENERATIONS) |
| Request body | 9.5 MB |
| Prompt / leave-out / edit instruction | 4,000 / 1,000 / 2,000 characters |
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST | A field is missing or out of range. |
| 400 | NO_QUOTED_TEXT | allowText is on but nothing is in quotation marks. |
| 400 | IMAGE_NOT_READY | A referenced picture has not finished drawing. |
| 400 | ALREADY_THAT_SIZE | An upscale to a size the picture already has. |
| 400 | NO_PERSON_FOUND | An ID photo from a picture with no person in it. |
| 400 | DOCUMENT_NOT_ACCEPTED | An ID photo from a passport or ID card page: send an ordinary photo of the person. |
| 400 | INVALID_REFERENCE · REFERENCE_UNREACHABLE | A reference is not a readable picture, or its URL could not be fetched. |
| 402 | QUOTA_EXCEEDED | Not enough credits for the whole request. |
| 403 | SCOPE_FORBIDDEN · SERVICE_DISABLED · VERIFICATION_REQUIRED | The key lacks images, the service is switched off, or identity verification is required. |
| 404 | NOT_FOUND | No such generation or picture in your organization. |
| 409 | RENDERING | Deleting what is still drawing. |
| 413 | PAYLOAD_TOO_LARGE · STORAGE_QUOTA_EXCEEDED | The body is over 9.5 MB, or your storage is full. |
| 429 | TOO_MANY_GENERATIONS · RATE_LIMITED · USAGE_LIMIT_REACHED | Three already drawing, too many requests, or a spending ceiling. |
| 503 | IMAGES_UNAVAILABLE · PRO_UNAVAILABLE | Drawing is unavailable right now (Pro only, for the second). |
See Errors for the shared envelope.