Kolbo.AIKolbo.AI Docs
Developer API

3D Generation

Generate 3D models from text prompts or reference images using the Kolbo API.

Generate 3D models (.glb, .fbx, .obj, .usdz) from text descriptions or reference images.

No Smart Select for 3D — unlike image and video generation, omitting the model field does not trigger Smart Select. Built-in per-mode defaults are used instead: meshy/v6-preview/text-to-3d for text mode, meshy/v6-preview/image-to-3d for single-image mode, and meshy/v5/multi-image-to-3d for multi-image mode. Use GET /api/v1/models?type=three_d to discover and pin a model.

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

All generation endpoints accept an optional project_id body field that routes the output into a specific project. See Projects.

Endpoint

POST /api/v1/generate/3d

Rate limited to 30 accepted requests/minute in production. The bucket is keyed by account (every API key you own shares it) and shared with the other non-image generation endpoints — video, music (including import/extend/cover), speech, sound, Creative Director, video-to-video, lipsync, elements, first/last frame, chat, edit/video, media upload, Visual DNA writes, voice cloning and the cancel route all draw from the same 30/min counter. Requests the API rejects do not count against it. Image generation, image editing and edit/image use a separate 30/min bucket. Status polling is not rate limited.

Request Body

JSON only — there is no multipart upload on this route. Every image field takes a public URL.

Core fields

FieldTypeRequiredDescription
promptstringYes in text modeText description of the 3D model. Missing or empty in text mode → 400 prompt is required for text mode; a non-string value in any mode → 400 INVALID_PROMPT. Only text mode sends it to the model — in single / multi mode it is stored (and echoed as result.prompt_used) but no 3D family reads it; use texture_prompt to steer an image-mode Meshy job. Meshy truncates prompts longer than 600 characters at a word boundary.
modestringNo"text", "single", or "multi". Auto-resolved from reference_images when omitted (0 images = text, 1 = single, 2+ = multi). Any other value returns 400 Invalid mode. Must be single, multi, or text.
reference_imagesarray of stringNoPublic image URLs. Required in single / multi mode — those modes return 400 without at least one URL. single mode uses only the first URL even if you send more. In multi mode the array is capped at 4 images, or 6 on Trellis identifiers (starting with fal-trellis or trellis); more returns 400 Multi-image mode supports maximum N images. The catalogue's max_reference_images is not enforced by this route — the 4/6 cap is.
texture_promptstringNoText that guides texturing. Read by the Meshy models (all modes) and the generic 3D path; ignored by the Trellis families. Truncated at 600 characters for Meshy. Dropped when should_texture is false.
modelstringNoModel identifier from GET /api/v1/models?type=three_d. Must be a string, otherwise 400 INVALID_MODEL. Omit — or send one of the auto-select aliases "auto", "smart-select", "smart_select", "smartselect", "auto-select" — to use the built-in per-mode defaults. An identifier the catalogue does not recognise is rejected with 400 MODEL_NOT_FOUND.
project_idstringNoRoute the model into a specific project. Defaults to your auto-created "API Generations" project.

Mesh and texture fields (Meshy and the generic 3D path)

FieldTypeDefaultDescription
topologystring"triangle"Mesh topology preset. Not sent to the Trellis families.
target_polycountnumber30000Target polygon count. Not sent to the Trellis families.
symmetry_modestring"auto"Symmetry control, passed through unvalidated (the Kolbo MCP documents "off", "auto", "on"). Sent to every Meshy model and the generic path.
enable_tposebooleanfalseForce a T-pose for character models. Meshy v5/v6 image modes and the generic path send it as enable_t_pose; Meshy v7 translates it to pose_mode: "t-pose". Not sent in Meshy v6 text mode.
enable_pbrbooleantrueGenerate PBR textures. Enabled unless you send an explicit false. Not sent in Meshy v6 text mode, and dropped when should_texture is false.
art_stylestring"realistic"Text mode only, passed through unvalidated (the Kolbo MCP documents "realistic" and "sculpture"). Forwarded only on the legacy Meshy v6 text path; a text model on the generic path always receives the default.
enable_prompt_expansionbooleanfalseText mode only — let the model expand the prompt before generating. Forwarded only on the legacy Meshy v6 text path; the generic path always sends false.
should_remeshbooleantrueRebuild the mesh with clean topology. Generic path only (Meshy v7 and other non-legacy models); Meshy v5/v6 always remesh.
should_texturebooleantrueGenerate textures. false produces an untextured mesh and drops enable_pbr, texture_prompt and texture_image_url. Generic path only; Meshy v5/v6 always texture.
texture_image_urlstring—URL of a 2D image that guides texturing. Meshy v7 only, and only while should_texture is true.
enable_safety_checkerbooleantrueScreen the input images before generation. Meshy v7 only.
enable_riggingbooleanfalseAuto-rig the model as a humanoid character. Meshy v7 only.
rigging_height_metersnumber1.7Approximate character height. Meshy v7 only, used only when enable_rigging is true.
enable_animationbooleanfalseApply an animation preset to the rigged model. Meshy v7 only, and silently ignored unless enable_rigging is also true.
animation_action_idnumber92Animation preset id. Meshy v7 only, used only when enable_animation is in effect.

Trellis fields

The Trellis families are image-only: a Trellis model in text mode is accepted at start time and then fails asynchronously with Unsupported mode 'text' for Trellis (or for Trellis 2).

FieldTypeFamilyDefaultDescription
seednumber | stringTrellis v1 and Trellis 2randomParsed as an integer for reproducible output.
ss_sampling_stepsnumberTrellis v1 and Trellis 212Sparse-structure sampling steps, clamped to 1–50.
slat_sampling_stepsnumberTrellis v1 and Trellis 212Structured-latent (Trellis 2: shape) sampling steps, clamped to 1–50.
ss_guidance_strengthnumberTrellis v17.5Sparse-structure guidance strength. 0 falls back to the default.
slat_guidance_strengthnumberTrellis v13Structured-latent guidance strength. 0 falls back to the default.
mesh_simplifynumberTrellis v10.95Mesh simplification ratio, clamped to 0.9–0.98.
texture_sizenumber | stringTrellis v11024512, 1024 or 2048; anything else silently becomes 1024. Trellis 2 ignores it — use t2_texture_size.
multiimage_algostringTrellis v1, multi mode"stochastic"Multi-view fusion algorithm, passed through unvalidated ("stochastic" or "multidiffusion").
resolutionnumber | stringTrellis 21024512, 1024 or 1536; anything else silently becomes 1024.
t2_texture_sizenumber | stringTrellis 220481024, 2048 or 4096; anything else silently becomes 2048.
tex_sampling_stepsnumberTrellis 212Texture sampling steps, clamped to 1–50.
decimation_targetnumberTrellis 2500000Target face count after decimation, clamped to 5,000–2,000,000.
remeshbooleanTrellis 2trueRemesh the output. Only an explicit false turns it off.

Routing is by identifier: meshy/v5… and meshy/v6… use the legacy Meshy path, identifiers starting with fal-trellis-2 / trellis-2 use Trellis 2, other fal-trellis / trellis identifiers use Trellis v1, and everything else (including Meshy v7) uses the generic 3D path. Every tuning field is optional and an ignored field fails silently rather than erroring, so check the model's entry in GET /api/v1/models?type=three_d before relying on one.

Two credit checks run before anything is queued. First a flat SDK floor of 20 credits — below that the request fails immediately with 403 INSUFFICIENT_CREDITS (the body also carries balance, required and pricing_url). Then the chosen model's cost is reserved as a hold against your balance; if your available credits (balance minus holds from your other in-flight generations) do not cover it, the request fails with 402 INSUFFICIENT_AVAILABLE_CREDITS. The hold is only charged after the model generates successfully, and released if it fails or you cancel. If the model declares credit toggles, the cost also depends on should_texture, enable_rigging and enable_animation — read the real charge from credits_used on the completed status.

Unlike the session-based generation endpoints, 3D is project-scoped — there is no session bucket. The start response's session_id and project_id both carry the resolved project id.

Three Modes

3D generation supports three modes, determined automatically from the number of reference images:

ModeReference ImagesDescription
text0 (none)Generate from text prompt only
single1Generate from one reference image, optionally steered by texture_prompt
multi2+Generate from multiple reference images (up to 4, or 6 on fal-trellis… identifiers) for more accurate reconstruction

Examples

Text Mode (Prompt Only)

curl -X POST https://api.kolbo.ai/api/v1/generate/3d \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{"prompt": "A medieval sword with ornate handle"}'

Single-Image Mode

curl -X POST https://api.kolbo.ai/api/v1/generate/3d \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "reference_images": ["https://example.com/shoe-front.jpg"],
    "texture_prompt": "Clean white running shoe",
    "enable_pbr": true
  }'

Multi-Image Mode

curl -X POST https://api.kolbo.ai/api/v1/generate/3d \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "reference_images": [
      "https://example.com/shoe-front.jpg",
      "https://example.com/shoe-side.jpg",
      "https://example.com/shoe-back.jpg"
    ],
    "texture_prompt": "Photorealistic sneaker texture"
  }'

With Specific Model

To choose a specific model, first fetch identifiers from GET /api/v1/models?type=three_d, then pass the identifier value:

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

# Step 2: Use an identifier from the response
curl -X POST https://api.kolbo.ai/api/v1/generate/3d \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A medieval sword with ornate handle",
    "model": "MODEL_IDENTIFIER",
    "enable_pbr": true,
    "target_polycount": 30000
  }'

Replace MODEL_IDENTIFIER with a real identifier from step 1. A text prompt needs a text-to-3D model — narrow the list with type=3d_text_to_model (see Finding Models).

Rigged and Animated Character (Meshy v7)

Rigging and animation are Meshy v7 options. Fetch an image-to-3D identifier with GET /api/v1/models?type=3d_image_to_model and pick the Meshy v7 entry:

curl -X POST https://api.kolbo.ai/api/v1/generate/3d \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "reference_images": ["https://example.com/knight-full-body.png"],
    "model": "MESHY_V7_IMAGE_TO_3D_IDENTIFIER",
    "enable_rigging": true,
    "rigging_height_meters": 1.8,
    "enable_animation": true,
    "animation_action_id": 92
  }'

Rigging and animation add a credit surcharge on models that declare one. result.urls lists only the base mesh formats; the rigged and animated files come back in result.rigging and result.animation (see below).

Trellis 2 Tuning

curl -X POST https://api.kolbo.ai/api/v1/generate/3d \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "reference_images": ["https://example.com/vase.png"],
    "model": "TRELLIS_2_IDENTIFIER",
    "resolution": 1536,
    "t2_texture_size": 4096,
    "decimation_target": 200000,
    "seed": 42
  }'

Response

This endpoint is asynchronous and fire-and-forget: the POST returns 200 as soon as the job is queued and never contains a mesh.

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": "6612f0a1b2c3d4e5f6a7b8c9",
  "type": "three_d",
  "model": "auto",
  "credits_charged": null,
  "poll_url": "/v1/generate/6612f0a1b2c3d4e5f6a7b8c9/status",
  "poll_interval_hint": 8,
  "session_id": "6601a1b2c3d4e5f6a7b8c9d0",
  "project_id": "6601a1b2c3d4e5f6a7b8c9d0"
}
FieldTypeDescription
generation_idstringPoll and cancel with this
modelstringThe resolved identifier you sent, or "auto" when you omitted it, sent an auto-select alias, or sent an identifier the catalogue did not recognise — in all three cases the built-in per-mode default runs
credits_chargednumber | nullAlways null for 3D. The 3D controller reports its estimate under a field the shared SDK envelope does not map, and at start time the cost is only held, not charged. Read the authoritative credits_used from the completed status response instead.
poll_urlstringStatus path without the /api prefix — prepend https://api.kolbo.ai/api
poll_interval_hintnumberSuggested seconds between polls (8 for 3D)
session_id / project_idstringBoth carry the resolved project id — 3D has no session bucket

Completed Status

{
  "success": true,
  "generation_id": "6612f0a1b2c3d4e5f6a7b8c9",
  "type": "three_d",
  "state": "completed",
  "progress": 100,
  "session_id": "6601a1b2c3d4e5f6a7b8c9d0",
  "project_id": "6601a1b2c3d4e5f6a7b8c9d0",
  "result": {
    "urls": [
      "https://media.kolbo.ai/3d/model.glb",
      "https://media.kolbo.ai/3d/model.fbx",
      "https://media.kolbo.ai/3d/model.obj",
      "https://media.kolbo.ai/3d/model.usdz"
    ],
    "thumbnail_url": "https://media.kolbo.ai/3d/preview.png",
    "mode": "text",
    "prompt_used": "A medieval sword with ornate handle",
    "model": null,
    "created_at": "2026-04-10T14:20:00Z"
  },
  "credits_used": 60,
  "credits_breakdown": [
    { "model": "MODEL_IDENTIFIER", "amount": 60 }
  ]
}

result.model is always null for 3D — the 3D generation record does not persist the resolved identifier. Read the identifier from credits_breakdown[].model instead, or keep the one you sent. The credit figures above are illustrative: the real cost is the chosen model's credit value from GET /api/v1/models?type=three_d, and credits_used reports what was actually charged.

The output lives in result.urls — always an array of strings. One 3D generation produces every format in that single array; there is no separate generation per format. It starts with the primary model file (the GLB used for preview) and is followed by whichever per-format files the model produced — glb, fbx, obj, usdz. Entries are not de-duplicated (the GLB commonly appears twice) and shorter arrays are normal, so match by file extension rather than indexing by position.

Other outputs come back in their own keys, each present only when the model produced it:

KeyShapeContents
files{ blend?, stl? }Extra-format files (.blend, .stl)
rigging{ glb?, fbx? }Rigged character (Meshy v7 with enable_rigging)
animation{ glb?, fbx? }Animated character (Meshy v7 with enable_animation)
texture_filesarray of { base_color?, metallic?, normal?, roughness? }PBR texture maps, one object per texture set

result (and credits_used / credits_breakdown) appear only under state: "completed"; a processing body carries just success, generation_id, type, state, progress, session_id and project_id — no result at all. There is no partial read — and progress sits at 0 for the entire run before jumping to 100, so never treat it as a completion signal.

A failed status carries error plus a failure object (message, category, code, retryable, severity). For 3D, category, code, retryable and severity are always null — the 3D record stores its error category under a field the status projection does not read. Branch on state plus the error string. The credit hold is released when the model fails, so a failed 3D job costs nothing.

Polling, failure envelopes and cancellation are otherwise identical to every other async generation — see Polling & Cancellation.

Errors

Returned by POST /api/v1/generate/3d before anything is queued:

StatusBody / codeCause
400prompt is required for text modemode resolved to text with a missing or empty prompt
400INVALID_PROMPTprompt present but not a string
400INVALID_MODELmodel sent as an array/object instead of a string identifier
400reference_images required for image modesmode is single or multi with an empty or missing array
400GENERATION_ERROR — Invalid mode. Must be single, multi, or textUnrecognised mode value
400GENERATION_ERROR — Multi-image mode supports maximum N imagesMore than 4 images (6 on Trellis identifiers) in multi mode
400GENERATION_ERROR — Model not foundThe built-in per-mode default identifier could not be loaded
400MODEL_NOT_FOUNDThe model you sent is not in the catalogue
403INSUFFICIENT_CREDITSBalance below the 20-credit SDK floor
402INSUFFICIENT_AVAILABLE_CREDITSAvailable credits (balance minus holds from in-flight generations) do not cover the model's cost
429rate limitMore than 30 accepted generation requests in a minute across the shared bucket
400SDK_PROJECT_INVALID_IDproject_id is not a valid ObjectId
404SDK_PROJECT_NOT_FOUNDNo project with that id is visible to you
403SDK_PROJECT_ACCESS_DENIEDYou hold less than edit permission on that project

Downstream failures from the 3D controller are re-wrapped by the SDK as { "success": false, "error": "...", "code": "GENERATION_ERROR" } (keeping the controller's HTTP status) unless the controller supplied its own code. Status-endpoint errors are documented in Polling & Cancellation.

Some mismatches are only caught asynchronously — the POST succeeds and the status endpoint later reports state: "failed":

  • a Trellis model in text mode (Unsupported mode 'text' for Trellis / for Trellis 2);
  • a 3d_world model (the explorable-world models listed under type=three_d). This endpoint only builds object meshes, so pick from 3d_text_to_model, 3d_image_to_model or 3d_multi_image_to_model;
  • any model this endpoint has no 3D runner for, which fails with Provider '<name>' does not support 3D generation yet.

The provider field in the models list is a public product-family label, not the routing key, so it cannot tell you which runner a model uses. Match the model's types to your mode instead.

Cancel

curl -X POST https://api.kolbo.ai/api/v1/generate/6612f0a1b2c3d4e5f6a7b8c9/cancel \
  -H "X-API-Key: kolbo_live_..."

Returns state: "cancelled" and credits_refunded; the credit hold is released. A generation that already finished returns 409 (CANNOT_CANCEL or NOT_IN_PROGRESS, with current_status). See Polling & Cancellation for the full cancel contract.

JavaScript Example

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

async function generate3D(body) {
  const response = await fetch(`${BASE_URL}/api/v1/generate/3d`, {
    method: "POST",
    headers: {
      "X-API-Key": KOLBO_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

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

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

  console.log("3D model URLs:", status.result.urls); // every format, one array
  console.log("Thumbnail:", status.result.thumbnail_url);
  return status.result;
}

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}/api/v1/generate/${generationId}/status`,
      { headers: { "X-API-Key": KOLBO_API_KEY } }
    ).then((r) => r.json());
    if (TERMINAL.has(status.state)) return status;
  }
}

async function main() {
  // Text mode — prompt only
  const textResult = await generate3D({
    prompt: "A low-poly tree with autumn leaves",
    enable_pbr: true,
  });
  console.log("Text mode result:", textResult.urls);

  // Single-image mode — one reference image
  const singleResult = await generate3D({
    reference_images: ["https://example.com/chair-photo.jpg"],
    texture_prompt: "Oiled walnut wood grain",
  });
  console.log("Single-image result:", singleResult.urls);

  // Multi-image mode — multiple reference images
  const multiResult = await generate3D({
    reference_images: [
      "https://example.com/mug-front.jpg",
      "https://example.com/mug-side.jpg",
    ],
    texture_prompt: "Ceramic mug with glossy finish",
  });
  console.log("Multi-image result:", multiResult.urls);
}

main().catch(console.error);

JavaScript — Choosing a Specific Model

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

async function main() {
  // Step 1: Fetch text-to-3D models (a text prompt needs one)
  const modelsRes = await fetch(`${BASE_URL}/api/v1/models?type=3d_text_to_model`, {
    headers: { "X-API-Key": KOLBO_API_KEY },
  });
  const { models } = await modelsRes.json();
  console.log("Text-to-3D models:", models.map(m => `${m.identifier} (${m.credit} credits)`));

  // Step 2: Generate with a specific model
  const response = await fetch(`${BASE_URL}/api/v1/generate/3d`, {
    method: "POST",
    headers: {
      "X-API-Key": KOLBO_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      prompt: "A futuristic helmet with visor",
      model: models[0].identifier,
      enable_pbr: true,
    }),
  });

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

  // pollUntilDone() from the example above
  const status = await pollUntilDone(started.generation_id, started.poll_interval_hint);
  if (status.state !== "completed") throw new Error(status.error || status.state);

  console.log("3D model URLs:", status.result.urls);
}

main().catch(console.error);

Python Example

import requests
import time

KOLBO_API_KEY = "kolbo_live_..."  # Replace with your API key
BASE_URL = "https://api.kolbo.ai"
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}/api/v1/generate/{generation_id}/status",
            headers=HEADERS,
        ).json()
        if status["state"] in TERMINAL:
            return status


def generate_3d(body):
    """Generate a 3D model and poll until complete."""
    started = requests.post(
        f"{BASE_URL}/api/v1/generate/3d",
        headers=HEADERS,
        json=body,
    ).json()
    if not started.get("success"):
        raise Exception(started.get("error", "Request failed"))

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


# Text mode — prompt only
result = generate_3d({"prompt": "A low-poly tree with autumn leaves", "enable_pbr": True})
print("3D model URLs:", result["urls"])
print("Thumbnail:", result["thumbnail_url"])

# Single-image mode
result = generate_3d({
    "reference_images": ["https://example.com/chair-photo.jpg"],
    "texture_prompt": "Oiled walnut wood grain",
})
print("Single-image URLs:", result["urls"])

# Multi-image mode
result = generate_3d({
    "reference_images": [
        "https://example.com/mug-front.jpg",
        "https://example.com/mug-side.jpg",
    ],
    "texture_prompt": "Ceramic mug with glossy finish",
})
print("Multi-image URLs:", result["urls"])

Python — Choosing a Specific Model

import requests

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

# Step 1: Fetch text-to-3D models (a text prompt needs one)
models = requests.get(
    f"{BASE_URL}/api/v1/models?type=3d_text_to_model", headers=HEADERS
).json()["models"]
print("Text-to-3D models:")
for m in models:
    print(f"  {m['identifier']} ({m['credit']} credits)")

# Step 2: Generate with a specific model — generate_3d() from the example above
# already handles the submit-then-poll round trip.
result = generate_3d({
    "prompt": "A futuristic helmet with visor",
    "model": models[0]["identifier"],
    "enable_pbr": True,
})
print("3D model URLs:", result["urls"])
print("Thumbnail:", result["thumbnail_url"])

Tips

  • 3D is the slowest generation type on the API. The server gives a job 15 minutes before it times out, so size your poll loop accordingly and use the poll_interval_hint of 8 seconds rather than tight polling.
  • Multi-image mode reconstructs from several views, so it needs more input preparation than single-image mode. Send at most 4 views (6 on Trellis identifiers).
  • In image modes, steer the result with texture_prompt (Meshy) rather than prompt — the image modes do not send prompt to the model.
  • PBR textures are on by default — send "enable_pbr": false if you want an unlit mesh.
  • Output formats can include .glb (universal), .fbx (game engines), .obj (legacy) and .usdz (Apple AR). Which are produced depends on the model, so match result.urls entries by extension.
  • Use result.thumbnail_url for quick previews without loading the full mesh.

Finding Models

Use the Models endpoint to discover available 3D models. The type=three_d filter covers all four sub-types (3d_text_to_model, 3d_image_to_model, 3d_multi_image_to_model, 3d_world), and you can pass any of those sub-types as type directly to narrow further. Pick the sub-type that matches your mode; 3d_world models are not run by this endpoint:

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