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.
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_promptbooleanNoEnhance prompt for better results. Default: true (any value other than an explicit false enables it).
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 stringsNoImage URLs (1–8) to condition the generation on. 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: max_reference_images for the chosen model (falls back to 8). Extra URLs beyond the cap are silently dropped, not rejected.
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"). 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. The tier multiplies the credit cost on top of resolution via quality_multipliers — see Credit Multipliers. Omit to use the model's default_quality.
project_idstringNoProject ObjectId to drop the generation into. Call GET /v1/projects to discover IDs. Omit to use the auto-created "API Generations" project. See Projects.

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-2gpt-image-2/edit, nano-banananano-banana/edit, flux-2flux-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, 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 — higher than the 10/minute default on other generation endpoints, so parallel multi-image calls are not throttled.

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

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": "A portrait in the same style as the reference",
    "reference_images": ["https://example.com/style-ref.jpg"],
    "aspect_ratio": "1:1"
  }'

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"
  },
  "credits_used": 24,
  "credits_breakdown": [
    { "model": "nano-banana-2", "amount": 24, "base": 12, "final": 24, "duration_multiplier": null, "pricing": null }
  ]
}

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 has exactly five keys and no result at all. credits_used is the authoritative, multiplier-adjusted charge — see Credits and Billing.

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, and images_per_request 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).