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_idbody field that routes the output into a specific project. See Projects.
Endpoint
POST /api/v1/generate/3dRate 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
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes in text mode | Text 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. |
mode | string | No | "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_images | array of string | No | Public 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_prompt | string | No | Text 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. |
model | string | No | Model 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_id | string | No | Route the model into a specific project. Defaults to your auto-created "API Generations" project. |
Mesh and texture fields (Meshy and the generic 3D path)
| Field | Type | Default | Description |
|---|---|---|---|
topology | string | "triangle" | Mesh topology preset. Not sent to the Trellis families. |
target_polycount | number | 30000 | Target polygon count. Not sent to the Trellis families. |
symmetry_mode | string | "auto" | Symmetry control, passed through unvalidated (the Kolbo MCP documents "off", "auto", "on"). Sent to every Meshy model and the generic path. |
enable_tpose | boolean | false | Force 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_pbr | boolean | true | Generate PBR textures. Enabled unless you send an explicit false. Not sent in Meshy v6 text mode, and dropped when should_texture is false. |
art_style | string | "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_expansion | boolean | false | Text 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_remesh | boolean | true | Rebuild the mesh with clean topology. Generic path only (Meshy v7 and other non-legacy models); Meshy v5/v6 always remesh. |
should_texture | boolean | true | Generate 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_url | string | — | URL of a 2D image that guides texturing. Meshy v7 only, and only while should_texture is true. |
enable_safety_checker | boolean | true | Screen the input images before generation. Meshy v7 only. |
enable_rigging | boolean | false | Auto-rig the model as a humanoid character. Meshy v7 only. |
rigging_height_meters | number | 1.7 | Approximate character height. Meshy v7 only, used only when enable_rigging is true. |
enable_animation | boolean | false | Apply an animation preset to the rigged model. Meshy v7 only, and silently ignored unless enable_rigging is also true. |
animation_action_id | number | 92 | Animation 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).
| Field | Type | Family | Default | Description |
|---|---|---|---|---|
seed | number | string | Trellis v1 and Trellis 2 | random | Parsed as an integer for reproducible output. |
ss_sampling_steps | number | Trellis v1 and Trellis 2 | 12 | Sparse-structure sampling steps, clamped to 1–50. |
slat_sampling_steps | number | Trellis v1 and Trellis 2 | 12 | Structured-latent (Trellis 2: shape) sampling steps, clamped to 1–50. |
ss_guidance_strength | number | Trellis v1 | 7.5 | Sparse-structure guidance strength. 0 falls back to the default. |
slat_guidance_strength | number | Trellis v1 | 3 | Structured-latent guidance strength. 0 falls back to the default. |
mesh_simplify | number | Trellis v1 | 0.95 | Mesh simplification ratio, clamped to 0.9–0.98. |
texture_size | number | string | Trellis v1 | 1024 | 512, 1024 or 2048; anything else silently becomes 1024. Trellis 2 ignores it — use t2_texture_size. |
multiimage_algo | string | Trellis v1, multi mode | "stochastic" | Multi-view fusion algorithm, passed through unvalidated ("stochastic" or "multidiffusion"). |
resolution | number | string | Trellis 2 | 1024 | 512, 1024 or 1536; anything else silently becomes 1024. |
t2_texture_size | number | string | Trellis 2 | 2048 | 1024, 2048 or 4096; anything else silently becomes 2048. |
tex_sampling_steps | number | Trellis 2 | 12 | Texture sampling steps, clamped to 1–50. |
decimation_target | number | Trellis 2 | 500000 | Target face count after decimation, clamped to 5,000–2,000,000. |
remesh | boolean | Trellis 2 | true | Remesh 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:
| Mode | Reference Images | Description |
|---|---|---|
text | 0 (none) | Generate from text prompt only |
single | 1 | Generate from one reference image, optionally steered by texture_prompt |
multi | 2+ | 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"
}| Field | Type | Description |
|---|---|---|
generation_id | string | Poll and cancel with this |
model | string | The 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_charged | number | null | Always 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_url | string | Status path without the /api prefix — prepend https://api.kolbo.ai/api |
poll_interval_hint | number | Suggested seconds between polls (8 for 3D) |
session_id / project_id | string | Both 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:
| Key | Shape | Contents |
|---|---|---|
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_files | array 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:
| Status | Body / code | Cause |
|---|---|---|
| 400 | prompt is required for text mode | mode resolved to text with a missing or empty prompt |
| 400 | INVALID_PROMPT | prompt present but not a string |
| 400 | INVALID_MODEL | model sent as an array/object instead of a string identifier |
| 400 | reference_images required for image modes | mode is single or multi with an empty or missing array |
| 400 | GENERATION_ERROR — Invalid mode. Must be single, multi, or text | Unrecognised mode value |
| 400 | GENERATION_ERROR — Multi-image mode supports maximum N images | More than 4 images (6 on Trellis identifiers) in multi mode |
| 400 | GENERATION_ERROR — Model not found | The built-in per-mode default identifier could not be loaded |
| 400 | MODEL_NOT_FOUND | The model you sent is not in the catalogue |
| 403 | INSUFFICIENT_CREDITS | Balance below the 20-credit SDK floor |
| 402 | INSUFFICIENT_AVAILABLE_CREDITS | Available credits (balance minus holds from in-flight generations) do not cover the model's cost |
| 429 | rate limit | More than 30 accepted generation requests in a minute across the shared bucket |
| 400 | SDK_PROJECT_INVALID_ID | project_id is not a valid ObjectId |
| 404 | SDK_PROJECT_NOT_FOUND | No project with that id is visible to you |
| 403 | SDK_PROJECT_ACCESS_DENIED | You 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
textmode (Unsupported mode 'text' for Trellis/for Trellis 2); - a
3d_worldmodel (the explorable-world models listed undertype=three_d). This endpoint only builds object meshes, so pick from3d_text_to_model,3d_image_to_modelor3d_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_hintof 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 thanprompt— the image modes do not sendpromptto the model. - PBR textures are on by default — send
"enable_pbr": falseif 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.urlsentries by extension. - Use
result.thumbnail_urlfor 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_..."