Kolbo.AIKolbo.AI Docs
Developer API

Image Generation

Generate images from text prompts using the Kolbo API.

Generate images from text prompts using Kolbo's catalog of image models. Browse the model catalog for what is currently available, or fetch the live list at any time with GET /api/v1/models?type=text_to_img.

Smart Select: omit the model field and Kolbo picks a model for your prompt. The aliases "", "auto", "auto-select", "smart-select", "smart_select", "smartselect", and "kolbo_smart_select_router" all resolve to the same router. Send a specific identifier when you need a deterministic model choice.

Model identifiers are Kolbo-specific — they do not match upstream provider names. Always fetch available models from GET /api/v1/models?type=image first. Never guess or hardcode identifiers.

Endpoint

POST /api/v1/generate/image

Request Body

FieldTypeRequiredDescription
promptstringYesText description of the image. Must be a non-empty string — anything else is a 400 INVALID_PROMPT.
modelstringNoModel identifier from GET /api/v1/models?type=image (alias of type=text_to_img). Must be a string — arrays/objects are a 400 INVALID_MODEL. Omit, or send any Smart Select alias ("", "auto", "auto-select", "smart-select", "smart_select", "smartselect", "kolbo_smart_select_router"), to use Smart Select. An identifier that is not in the catalog is rejected with 400 MODEL_NOT_FOUND (the message suggests near matches).
aspect_ratiostringNo"1:1", "16:9", "9:16", etc. Send a value from the model's supported_aspect_ratios on GET /api/v1/models. The value is not validated server-side — an unsupported ratio is forwarded to the provider and falls back to one the model does support, so the output may not have the shape you asked for. Default: "1:1".
enhance_promptbooleanNoLet Kolbo rewrite the prompt before generation. Default: false — true (or the string "true" / "1") enables it; omitting the field sends your prompt as written.
num_imagesnumberNoNumber of images in one call. Clamped server-side to 1–4 — values outside that range are silently clamped, and a non-numeric value falls back to 1. Default: 1. Models with a fixed images_per_request (see GET /api/v1/models) ignore this.
reference_imagesarray of stringsNoPublic image URLs to condition the generation on — send several to combine multiple references. Items may also be { "url": "..." } objects. What they do depends on the model — if the chosen model has an editing variant (editing_model_identifier on GET /api/v1/models), the request is auto-routed to that editor and the images are used pixel-accurately; if it has none (e.g. midjourney), they act as style/composition inspiration only. See the callout below. Cap: the chosen model's max_reference_images, further limited by the provider's own ceiling where that is lower (e.g. flux-2/edit takes 4). Extra URLs beyond the cap are silently dropped in order, not rejected. Visual DNA stills share the same image budget and are packed first. GPT Image 2.5 is the exception: more than 16 references are not trimmed — the generation fails (INVALID_IMAGE_SETTINGS). The aliases source_images, image, images and uploadedImages are also read and merged into this list.
visual_dna_idsarray of stringsNoVisual DNA IDs for character/style/product consistency. Cap: at most max_visual_dna IDs for the chosen model; models with supports_visual_dna: false (or max_visual_dna null/0) do not apply DNA. Both fields come from GET /api/v1/models. The DNA's description is injected into the prompt as plaintext regardless of enhance_prompt.
moodboard_idstringNoMoodboard ObjectId whose master prompt and style guide are applied. Must be a valid ObjectId — otherwise 400 Invalid moodboard_id format.
preset_idstringNoSaved image style preset to apply. Discover IDs with GET /api/v1/presets?type=image.
enable_web_searchbooleanNoGround the prompt with web search (current events, brand references, real-world accuracy). Default: false.
cinematicobjectNoCinematic Presets — an optional "Cinema mode" object mapping dimensions (camera, lens, focal_length, aperture, angle, shot_type, color_palette, lighting) to preset ids from GET /api/v1/cinematic-presets. Include only the dimensions you want; omitted or null ones are Auto. Ids are validated against their dimension server-side. Must be a plain object — arrays are ignored. Omit the object entirely for a non-cinematic generation.
skip_color_palettebooleanNoOpt this single call out of the account's active Color DNA palette. By default an active palette grades every generation. Only the exact value true skips it. Default: palette applied.
resolutionstringNoResolution tier, e.g. "1K" (~1024px), "2K", "3K", "4K". Model-dependent — send a value from the chosen model's supported_resolutions on GET /api/v1/models?type=image. Higher tiers multiply the credit cost via resolution_multipliers on the same model — see Credit Multipliers. Omit to use the model's default tier.
qualitystringNoQuality tier. Model-dependent, and the vocabulary differs per model — read supported_qualities on GET /api/v1/models?type=image and send one of those exact values (e.g. GPT Image 2 uses "low" | "medium" | "high" | "auto", GPT Image 2.5 uses "low" | "medium" | "high" | "xhigh" | "max", Ideogram v3/v4 use "TURBO" | "BALANCED" | "QUALITY"). Models with an empty supported_qualities have no quality tier — omit the field. "auto" is rewritten server-side to "medium" (so it renders and bills at the medium tier) on every model except GPT Image 2.5, which keeps its own auto tier. The tier multiplies the credit cost on top of resolution via quality_multipliers — see Credit Multipliers. Omit to use the model's default_quality.
backgroundstringNo"auto", "opaque" or "transparent". Any other value is ignored. "transparent" requires a specific model with supports_transparent_background: true on GET /api/v1/models — Smart Select or an unsupported model is a 422 TRANSPARENT_BACKGROUND_UNSUPPORTED. A transparent request defaults output_format to "png" and appends "no background" to the prompt.
output_formatstringNo"png", "jpeg" or "webp", forwarded to models that expose a format choice (other models use their own format). On GPT Image 2.5 it is validated (default "png"), and "jpeg" cannot be combined with a transparent background.
output_compressionintegerNo0–100, JPEG/WebP only. Used by GPT Image 2.5 only; there a value with png output, a non-integer or one outside the range is a 422 INVALID_IMAGE_SETTINGS.
moderationstringNo"auto" or "low" — provider moderation level on GPT Image 2.5. Default: "auto".
mask_image_urlstringNoPublic PNG mask URL (under 4 MB) for masked edits on GPT Image 2.5. Transparent mask regions mark the area to edit; the mask needs an alpha channel and the same dimensions as the first reference image. Requires reference_images — otherwise the generation fails.
font_idsarray of stringsNoUp to 3 ready font family IDs from Personal Fonts. Each must be a 24-hex ObjectId (otherwise 400 INVALID_FONT_IDS), and a specific model must have supports_custom_fonts: true (otherwise 400 CUSTOM_FONTS_UNSUPPORTED). With Smart Select, routing is limited to font-capable models. State the exact text, language and weight in the prompt.
project_idstringNoProject ObjectId to drop the generation into. Call GET /v1/projects to discover IDs. Omit to use the auto-created "API Generations" project. Requires at least edit permission on the project. See Projects.
session_idstringNoAn image session_id returned by an earlier /v1/generate/image or /v1/generate/image-edit call. When sent it wins over project_id: the generation lands in that exact session (and its project), which is how you group a related batch instead of getting a new session per call. An unknown ID or one from another generation type is a 404 SDK_SESSION_NOT_FOUND; a malformed ID is 400 SDK_SESSION_INVALID_ID. Write access is checked on the session's project, exactly as for project_id.

Reference images auto-route to the editing model. You never have to pick an editing identifier yourself. Send reference_images with a text-to-image model and Kolbo swaps in that model's editing variant automatically — gpt-image-2 → gpt-image-2/edit, nano-banana → nano-banana/edit, flux-2 → flux-2/edit. The same swap happens on POST /v1/generate/image-edit, so both endpoints accept either identifier and land on the right model.

The swap happens before pricing, so credits and the result.model returned by GET /v1/generate/{id}/status reflect the editing model, not the one you sent.

If editing_model_identifier is null (Flux.1, Imagen 4, Ideogram v3/v4, Recraft, Krea 2, Soul, Midjourney and others) that model has no editor, and reference_images behave differently: they are analysed into a text description that is appended to your prompt — the pixels are never sent to the provider. This is intended, not a failure, but it means you cannot reproduce an asset with these models. Check editing_model_identifier before sending references, and pick a model that has one when you need the reference honoured pixel-accurately.

Use /v1/generate/image when you want a new image informed by references, and /v1/generate/image-edit when you want the source images preserved and modified.

Rate limit: image endpoints (/v1/generate/image, /v1/generate/image-edit, /v1/edit/image) allow 30 requests/minute in their own bucket, separate from the 30/minute bucket shared by the other generation endpoints, so parallel image calls do not eat into your video or chat budget.

Insufficient credits fail synchronously. The credit gate runs before the generation is queued, so the initial POST returns 403 with "code": "INSUFFICIENT_CREDITS" (plus balance, required and pricing_url) rather than failing later during polling.

Errors

Errors on the POST return { "success": false, "error": "...", "code": "..." } and nothing is queued.

StatuscodeCause
400INVALID_PROMPTprompt missing, empty or not a string
400INVALID_MODELmodel sent as an array or object
400—moodboard_id is not a valid ObjectId ("Invalid moodboard_id format")
400SDK_PROJECT_INVALID_ID / SDK_SESSION_INVALID_IDMalformed project_id / session_id
400NSFW_BLOCKED / NSFW_BLOCKED_LOCKEDThe prompt was refused by the account's content policy
400INVALID_FONT_IDS / CUSTOM_FONTS_UNSUPPORTEDBad font_ids, or the chosen model does not support custom fonts
403INSUFFICIENT_CREDITSBalance below the minimum for an image generation
403SDK_PROJECT_ACCESS_DENIEDView-only access to the project (or to the session_id's project)
404SDK_PROJECT_NOT_FOUND / SDK_SESSION_NOT_FOUNDUnknown or inaccessible project / session
422TRANSPARENT_BACKGROUND_UNSUPPORTEDbackground: "transparent" without a specific model that supports it
422INVALID_IMAGE_SETTINGSGPT Image 2.5 rejected quality, resolution, aspect_ratio, output_format, output_compression, background or moderation
429—Rate limit exceeded

INVALID_FONT_IDS is raised before the generation wrapper runs, so its body carries the platform fields (status: false, message) alongside success: false, error and code.

Examples

Basic (Smart Select)

The simplest approach — just send a prompt and Kolbo picks the best model:

curl -X POST https://api.kolbo.ai/api/v1/generate/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A futuristic cityscape at night"}'

With Aspect Ratio and Options

curl -X POST https://api.kolbo.ai/api/v1/generate/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Product photo of a sneaker on white background",
    "aspect_ratio": "16:9",
    "enhance_prompt": false
  }'

Choosing a Specific Model

If you need a specific model, fetch the list first, then use the identifier value:

# Step 1: List available image models
curl "https://api.kolbo.ai/api/v1/models?type=image" \
  -H "X-API-Key: kolbo_live_..."

# Step 2: Use an identifier from the response
# Example identifiers: "nano-banana-2", "nano-banana-pro", "gpt-image-2". Credit cost is on each model's `credit` field.
curl -X POST https://api.kolbo.ai/api/v1/generate/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Product photo of a sneaker on white background",
    "model": "nano-banana-2",
    "aspect_ratio": "16:9"
  }'

With Reference Images

curl -X POST https://api.kolbo.ai/api/v1/generate/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "The character from @Image 1 wearing the jacket from @Image 2",
    "model": "nano-banana-2",
    "reference_images": [
      "https://example.com/character.jpg",
      "https://example.com/jacket.jpg"
    ],
    "aspect_ratio": "1:1"
  }'

Transparent Background

Pick a model whose supports_transparent_background is true on GET /api/v1/models?type=image:

curl -X POST https://api.kolbo.ai/api/v1/generate/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A flat vector sticker of a coffee cup",
    "model": "gpt-image-2",
    "background": "transparent",
    "output_format": "png"
  }'

JavaScript

const KOLBO_API_KEY = "kolbo_live_..."; // Replace with your API key
const BASE_URL = "https://api.kolbo.ai/api";

const TERMINAL = new Set(["completed", "failed", "cancelled"]);

// Minimal loop. A production client should also handle transient HTTP errors and
// an overall timeout — see /docs/developer-api/polling-and-cancellation.
async function pollUntilDone(generationId, intervalSeconds) {
  while (true) {
    await new Promise((r) => setTimeout(r, intervalSeconds * 1000));
    const status = await fetch(`${BASE_URL}/v1/generate/${generationId}/status`, {
      headers: { "X-API-Key": KOLBO_API_KEY }
    }).then((r) => r.json());
    if (TERMINAL.has(status.state)) return status;
  }
}

// Smart Select (recommended) — omit model, Kolbo picks the best one
async function main() {
  const response = await fetch(`${BASE_URL}/v1/generate/image`, {
    method: "POST",
    headers: {
      "X-API-Key": KOLBO_API_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ prompt: "A cat wearing a space suit", aspect_ratio: "1:1" })
  });

  const started = await response.json();
  if (!started.success) throw new Error(started.error);

  const result = await pollUntilDone(started.generation_id, started.poll_interval_hint);
  if (result.state !== "completed") throw new Error(result.error || result.state);

  console.log("Image URLs:", result.result.urls); // always an array
}
main();

JavaScript — Choosing a Specific Model

const KOLBO_API_KEY = "kolbo_live_..."; // Replace with your API key
const BASE_URL = "https://api.kolbo.ai/api";

async function main() {
  // Step 1: Fetch available image models
  const modelsRes = await fetch(`${BASE_URL}/v1/models?type=image`, {
    headers: { "X-API-Key": KOLBO_API_KEY }
  });
  const { models } = await modelsRes.json();
  console.log("Available models:", models.map(m => `${m.identifier} (${m.credit} credits)`));
  // Example identifiers: "nano-banana-2", "nano-banana-pro", "gpt-image-2"

  // Step 2: Use a specific model identifier (nano-banana-2 is recommended for most use cases)
  const response = await fetch(`${BASE_URL}/v1/generate/image`, {
    method: "POST",
    headers: {
      "X-API-Key": KOLBO_API_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      prompt: "A cat wearing a space suit",
      model: "nano-banana-2",
      aspect_ratio: "1:1"
    })
  });

  const started = await response.json();

  // pollUntilDone() from the example above
  const result = await pollUntilDone(started.generation_id, started.poll_interval_hint);
  console.log("Image URLs:", result.result.urls);
}
main();

Python

import requests
import time

KOLBO_API_KEY = "kolbo_live_..."  # Replace with your API key
BASE_URL = "https://api.kolbo.ai/api"
HEADERS = {"X-API-Key": KOLBO_API_KEY}

TERMINAL = {"completed", "failed", "cancelled"}


def poll_until_done(generation_id, interval_seconds):
    """Minimal loop. Add transient-error retries and an overall timeout for
    production — see /docs/developer-api/polling-and-cancellation."""
    while True:
        time.sleep(interval_seconds)
        status = requests.get(
            f"{BASE_URL}/v1/generate/{generation_id}/status",
            headers=HEADERS,
        ).json()
        if status["state"] in TERMINAL:
            return status


# Smart Select (recommended) — omit model, Kolbo picks the best one
started = requests.post(
    f"{BASE_URL}/v1/generate/image",
    headers=HEADERS,
    json={
        "prompt": "A mountain landscape at sunset",
        "aspect_ratio": "16:9"
    }
).json()

result = poll_until_done(started["generation_id"], started["poll_interval_hint"])
if result["state"] != "completed":
    raise Exception(result.get("error", result["state"]))

print("Image URLs:", result["result"]["urls"])  # always a list

Python — Choosing a Specific Model

import requests

KOLBO_API_KEY = "kolbo_live_..."  # Replace with your API key
BASE_URL = "https://api.kolbo.ai/api"
HEADERS = {"X-API-Key": KOLBO_API_KEY}

# Step 1: Fetch available image models
models = requests.get(
    f"{BASE_URL}/v1/models?type=image",
    headers=HEADERS
).json()["models"]

# Step 2: Pick a model (e.g. the first one)
chosen_model = models[0]["identifier"]
print(f"Using model: {chosen_model}")

# Step 3: Generate with that model
started = requests.post(
    f"{BASE_URL}/v1/generate/image",
    headers=HEADERS,
    json={
        "prompt": "A mountain landscape at sunset",
        "model": chosen_model,
        "aspect_ratio": "16:9"
    }
).json()

# poll_until_done() from the example above
result = poll_until_done(started["generation_id"], started["poll_interval_hint"])
print("Image URLs:", result["result"]["urls"])

Response

This endpoint is asynchronous and fire-and-forget. The POST returns 200 as soon as the job is queued — it never contains an image. The result is read from the status endpoint.

Polling is the only completion mechanism. Kolbo never calls you back: there is no webhook, no callback_url field on this request body, and no route delivers outbound notifications. Socket.IO events are the web app's internal transport and are not part of the API contract — API-key generations are registered so the shared progress emitter drops their events, and the few emit sites that bypass that check are undocumented, unversioned and unsafe to build on. After the POST, loop against GET /api/v1/generate/{generation_id}/status until state is completed, failed or cancelled. Full contract: Polling & Cancellation.

Generation Started

{
  "success": true,
  "generation_id": "6890f1a2c3d4e5f60718293a",
  "type": "image",
  "model": "auto",
  "credits_charged": null,
  "poll_url": "/v1/generate/6890f1a2c3d4e5f60718293a/status",
  "poll_interval_hint": 3,
  "session_id": "6601a1b2c3d4e5f6a7b8c9d0",
  "project_id": "6601a1b2c3d4e5f6a7b8c9d1"
}
FieldTypeNotes
generation_idstringMongo ObjectId. This is what you poll and cancel with.
typestringAlways "image" for this endpoint
modelstringEchoes "auto" when Smart Select was used, otherwise the identifier you sent
credits_chargednullAlways null on this endpoint — the image pipeline reports no estimate at submit time. Read credits_used from the completed status.
poll_urlstringThe status path without the /api prefix. Prepend https://api.kolbo.ai/api, or just build GET /api/v1/generate/{generation_id}/status yourself.
poll_interval_hintnumberSuggested seconds between polls — 3 for image generation
session_id / project_idstringWhere the generation lives in the Kolbo app

Completed Status

GET /api/v1/generate/{generation_id}/status

{
  "success": true,
  "generation_id": "6890f1a2c3d4e5f60718293a",
  "type": "image",
  "state": "completed",
  "progress": 100,
  "result": {
    "urls": [
      "https://media.kolbo.ai/kolbo/images/6890f1a2c3d4e5f60718293a-0.png",
      "https://media.kolbo.ai/kolbo/images/6890f1a2c3d4e5f60718293a-1.png"
    ],
    "thumbnail_url": "https://media.kolbo.ai/kolbo/images/6890f1a2c3d4e5f60718293a-0.png",
    "prompt_used": "a neon-lit ramen bar in the rain, shallow depth of field",
    "model": "nano-banana-2",
    "created_at": "2026-07-27T09:14:02.113Z"
  },
  "session_id": "6601a1b2c3d4e5f6a7b8c9d0",
  "project_id": "6601a1b2c3d4e5f6a7b8c9d1",
  "credits_used": 24,
  "credits_breakdown": [
    { "model": "nano-banana-2", "amount": 24 }
  ]
}

The output lives in result.urls — always an array of strings, even for num_images: 1. Every image you asked for lands in it together at completion; there is no partial read. result.thumbnail_url is literally urls[0], not a separate smaller asset.

result (and credits_used / credits_breakdown) appear only under state: "completed". A processing body carries success, generation_id, type, state and progress (plus session_id / project_id) and no result at all. Each credits_breakdown entry is { model, amount }. credits_used is the authoritative, multiplier-adjusted charge — see Credits and Billing.

result.prompt_used is the enhanced prompt when enhancement ran, otherwise your original. result.reference_images lists the reference URLs actually used (present only when there were any), and result.selected_fonts ([{ id, name, styles }]) appears when font_ids were applied. The completed result also echoes the creative inputs that shaped the image, each key present only when you actually sent that input: visual_dna ([{ id, name, thumbnail_url }]), moodboard ({ id, name }), preset ({ id, name }) and cinematic_presets. model_name is added only when Smart Select recorded a display name.

result.model is the model that actually ran — never "auto". When Smart Select routed the request, the status response resolves and returns the chosen identifier, so this is how you find out what Smart Select picked. (The model field on the initial POST response is different: it echoes "auto" because routing has not happened yet.)

Failure and cancellation

A failed generation is still an HTTP 200 with success: true — the failure is in state, alongside an error string and a best-effort failure object. cancelled carries neither result nor error, so a loop that only tests for completed and failed will spin forever. Treat all three terminal states explicitly. Failed generations are not charged. See Polling & Cancellation.

With Moodboard

Apply a moodboard to guide the visual style of your generation:

curl -X POST https://api.kolbo.ai/api/v1/generate/image \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A product photo of a sneaker",
    "moodboard_id": "6601a1b2c3d4e5f6a7b8c9d0",
    "aspect_ratio": "1:1"
  }'

Use GET /api/v1/moodboards to list available moodboards. See Moodboards for details.

Finding Models

Use the Models endpoint to discover available image models:

curl "https://api.kolbo.ai/api/v1/models?type=image" \
  -H "X-API-Key: kolbo_live_..."

type=image is an alias for the underlying model type text_to_img; both work. Read supported_aspect_ratios, supported_resolutions, supported_qualities, max_reference_images, max_visual_dna, images_per_request, supports_transparent_background and supports_custom_fonts off each model to know exactly which fields above it accepts. editing_model_identifier tells you which editor the model auto-routes to when you send reference images (null = it has none).