Align the outward-facing skills with docs.meshy.ai. Every change is sourced from the public docs or the public, unauthenticated Animation Library endpoint; nothing internal is used. - Animation was a dead end: `action_id: 1 # from Animation Library` with no way to find a real ID. Now points at GET https://api.meshy.ai/web/public/animations/resources (no key, 680 actions, ?category= to narrow) with a working lookup snippet. IDs are not 1..N — the catalog includes -2, -1, 0 — so the old hardcoded 1 was never "the first animation". - model_type "lowpoly" is deprecated and "smart-topology" was missing: documented on image-to-3d with meshy-t2/meshy-t1 and both mutual-exclusion rules. Scoped correctly — smart-topology is image-to-3d only; text-to-3d keeps standard/lowpoly and multi-image-to-3d has no model_type at all. - hd_texture is deprecated: replaced with texture_resolution (2k/4k/8k) across refine, image-to-3d, multi-image-to-3d, retexture. - Rigging examples fed it untextured meshes: preconditions block (textured input, bipedal humanoid, 300k face cap via input_task_id, +Z for model_url) plus a source -> "rig this task ID" table routing text-to-3d through refine. - multi_view_thumbnails promoted to SKILL.md as the default inspection move, so agents stop pulling a 50-200 MB GLB just to look at a result. - FAILED tasks report consumed_credits: 0 (refunded) — a transient failure is a lost wait, not lost credits. scripts/build.py --check and scripts/validate_skills.py both pass. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
41 KiB
Meshy API
Meshy is an AI-powered 3D model generation platform. The Meshy API is a RESTful API that allows you to programmatically generate 3D models, textures, images, rig characters, and animate them.
Base URL: https://api.meshy.ai
Docs: https://docs.meshy.ai
Instructions for Large Language Models
When generating code that integrates with the Meshy API, follow these guidelines:
1. Authentication
All requests require a Bearer token in the Authorization header:
Authorization: Bearer msy_YOUR_API_KEY
API keys are created at https://www.meshy.ai/settings/api and have the format msy_<random-string>.
2. Asynchronous Task Model
Meshy uses an asynchronous execution model. All generation endpoints return a task ID, not the result directly. You must poll the task status or use SSE streaming to get results.
Correct pattern:
import requests, time, os
headers = {"Authorization": f"Bearer {os.environ['MESHY_API_KEY']}"}
# Step 1: Create a task
response = requests.post(
"https://api.meshy.ai/openapi/v2/text-to-3d",
headers=headers,
json={"mode": "preview", "prompt": "a monster mask"},
)
task_id = response.json()["result"]
# Step 2: Poll until completion
while True:
task = requests.get(
f"https://api.meshy.ai/openapi/v2/text-to-3d/{task_id}",
headers=headers,
).json()
if task["status"] == "SUCCEEDED":
break
if task["status"] == "FAILED":
raise Exception(task["task_error"]["message"])
time.sleep(5)
# Step 3: Download result
model_url = task["model_urls"]["glb"]
WRONG pattern (expecting synchronous result):
# WRONG - the POST does not return a model
response = requests.post("https://api.meshy.ai/openapi/v2/text-to-3d", ...)
model = response.json()["model_urls"] # WRONG
3. SSE Streaming (Alternative to Polling)
All task endpoints support Server-Sent Events streaming at /<endpoint>/:id/stream:
import requests, json
headers = {
"Authorization": f"Bearer {API_KEY}",
"Accept": "text/event-stream"
}
response = requests.get(
f"https://api.meshy.ai/openapi/v2/text-to-3d/{task_id}/stream",
headers=headers,
stream=True
)
for line in response.iter_lines():
if line and line.startswith(b"data:"):
data = json.loads(line.decode("utf-8")[5:])
print(data["status"], data.get("progress"))
if data["status"] in ["SUCCEEDED", "FAILED", "CANCELED"]:
break
response.close()
4. Task Statuses
All tasks follow the same lifecycle: PENDING -> IN_PROGRESS -> SUCCEEDED | FAILED | CANCELED
5. Timestamps
All timestamps in the API are Unix epoch milliseconds (not seconds). For example, 1693569600000 represents September 1, 2023 12:00:00 PM UTC.
6. Model Output Formats
3D model outputs are available in multiple formats: GLB, FBX, OBJ, USDZ, STL (via Remesh), 3MF. Access them via model_urls.glb, model_urls.fbx, model_urls["3mf"], etc. Not all formats are always available; omitted properties mean that format was not generated. Ask the user which format they need before downloading — do not download all formats.
IMPORTANT: 3MF is NOT included by default. To receive a 3MF file, you MUST explicitly include "3mf" in the target_formats parameter when creating the task (e.g. "target_formats": ["glb", "3mf"]). Without this, the default output includes GLB, OBJ, FBX, STL, USDZ but not 3MF. The Multi-Color Print API always outputs 3MF.
All generation endpoints (Text to 3D, Image to 3D, Multi-Image to 3D, Remesh, Retexture) support target_formats. Specifying only the formats you need can reduce task completion time.
7. Asset Retention
Generated assets are retained for a maximum of 3 days for non-Enterprise customers. Download and store models locally if you need them longer.
8. Rate Limits
| Tier | Requests/Second | Queue Tasks | Priority |
|---|---|---|---|
| Pro | 20 | 10 | Default |
| Studio | 20 | 20 | Higher |
| Enterprise | 100 | 50+ | Highest |
Exceeding limits returns 429 Too Many Requests.
9. Choosing the Right AI Model
- Use
"latest"or"meshy-6"for the best quality (default). - Use
"meshy-5"for the previous generation model. "latest"always resolves to the newest model (currently Meshy 6).- Low-poly / clean-topology output: on Image to 3D, use
model_type: "smart-topology"withai_model: "meshy-t2"(default and recommended for that model type).model_type: "lowpoly"is deprecated — the docs recommendsmart-topologyinstead.smart-topologyexists on Image to 3D only; for Text to 3D and Multi-Image to 3D, generate normally and drop the polycount with the Remesh API.
10. Common Mistakes to Avoid
- Don't call CORS-restricted endpoints from browser JavaScript. The API blocks CORS requests. Use a server-side proxy.
- Don't forget
enable_pbr: trueif you need metallic/roughness/normal maps. - Don't set both
texture_promptandtexture_image_url— if both are provided,texture_prompttakes precedence. - Don't assume model format availability. Check that the URL key exists in
model_urlsbefore downloading. - Don't guess an
action_id. Animation action IDs are not a1..Nrange (the catalog includes-2,-1, and0). Fetch the public catalog first — see the Animation API section. - Don't rig an untextured mesh. Rigging requires a textured humanoid model, so texture first (Text to 3D
refine, Image to 3D withshould_texture: true, or Retexture), then rig. - Don't pass
hd_texture. It is deprecated in favour oftexture_resolution("2k"/"4k"/"8k").
Complete Code Example: Text to 3D (Preview + Refine)
import requests, os, time
headers = {"Authorization": f"Bearer {os.environ['MESHY_API_KEY']}"}
# 1. Create preview task
preview_resp = requests.post(
"https://api.meshy.ai/openapi/v2/text-to-3d",
headers=headers,
json={
"mode": "preview",
"prompt": "a monster mask",
"should_remesh": True,
},
)
preview_resp.raise_for_status()
preview_task_id = preview_resp.json()["result"]
# 2. Poll preview task
while True:
task = requests.get(
f"https://api.meshy.ai/openapi/v2/text-to-3d/{preview_task_id}",
headers=headers,
).json()
if task["status"] == "SUCCEEDED":
break
if task["status"] == "FAILED":
raise Exception(task["task_error"]["message"])
time.sleep(5)
# 3. Download preview model
with open("preview_model.glb", "wb") as f:
f.write(requests.get(task["model_urls"]["glb"]).content)
# 4. Create refine task (texturing)
refine_resp = requests.post(
"https://api.meshy.ai/openapi/v2/text-to-3d",
headers=headers,
json={
"mode": "refine",
"preview_task_id": preview_task_id,
"enable_pbr": True,
},
)
refine_resp.raise_for_status()
refine_task_id = refine_resp.json()["result"]
# 5. Poll refine task
while True:
task = requests.get(
f"https://api.meshy.ai/openapi/v2/text-to-3d/{refine_task_id}",
headers=headers,
).json()
if task["status"] == "SUCCEEDED":
break
if task["status"] == "FAILED":
raise Exception(task["task_error"]["message"])
time.sleep(5)
# 6. Download refined (textured) model
with open("refined_model.glb", "wb") as f:
f.write(requests.get(task["model_urls"]["glb"]).content)
Complete Code Example: Image to 3D
import requests, os, time
headers = {"Authorization": f"Bearer {os.environ['MESHY_API_KEY']}"}
response = requests.post(
"https://api.meshy.ai/openapi/v1/image-to-3d",
headers=headers,
json={
"image_url": "https://example.com/photo.jpg",
"should_texture": True,
"enable_pbr": True,
},
)
response.raise_for_status()
task_id = response.json()["result"]
while True:
task = requests.get(
f"https://api.meshy.ai/openapi/v1/image-to-3d/{task_id}",
headers=headers,
).json()
if task["status"] == "SUCCEEDED":
break
if task["status"] == "FAILED":
raise Exception(task["task_error"]["message"])
time.sleep(5)
with open("model.glb", "wb") as f:
f.write(requests.get(task["model_urls"]["glb"]).content)
API Endpoints Reference
Text to 3D API
The Text to 3D workflow has two stages: preview (mesh generation) and refine (texture generation).
POST /openapi/v2/text-to-3d — Create Preview Task
Creates a preview (mesh-only) 3D model from a text prompt.
Required parameters:
mode(string): Must be"preview".prompt(string): Description of the 3D model. Max 600 characters.
Optional parameters:
model_type(string):"standard"(default) or"lowpoly". When"lowpoly",ai_model,topology,target_polycount,should_remeshare ignored. Text to 3D has no"smart-topology"— that model type is Image to 3D only. For clean low-poly output from a text prompt, generate normally and remesh down, or go text-to-image → image-to-3d withsmart-topology.ai_model(string):"meshy-5","meshy-6", or"latest"(default, resolves to Meshy 6).topology(string):"quad"or"triangle"(default).target_polycount(integer): 100–300,000. Default 30,000.should_remesh(boolean): Defaultfalsefor Meshy 6,truefor others.pose_mode(string):"a-pose","t-pose", or""(default). (Replaces the deprecatedis_a_t_poseflag.)decimation_mode(integer): Adaptive polycount mode,1–4. Trades detail vs. polygon budget.moderation(boolean): Screen input for harmful content. Defaultfalse.target_formats(string[]): Output formats:"glb","obj","fbx","stl","usdz","3mf". Default: all except 3mf. 3mf must be explicitly included.auto_size(boolean): Use AI to auto-estimate real-world height. Defaultfalse.origin_at(string):"bottom"or"center". Default"bottom"when auto_size is true.
Deprecated parameters: symmetry_mode — no longer affects output, safe to omit. art_style — ignored by Meshy-6. is_a_t_pose — superseded by pose_mode. (In refine mode, hd_texture is superseded by texture_resolution.)
Retired model:
meshy-4is retired; requests that passai_model: "meshy-4"return 400.
Cost: 20 credits (Meshy-6/lowpoly), 5 credits (other models).
Response: {"result": "<task_id>"}
POST /openapi/v2/text-to-3d — Create Refine Task
Textures a previously generated preview model.
Required parameters:
mode(string): Must be"refine".preview_task_id(string): ID of a succeeded preview task.
Optional parameters:
enable_pbr(boolean): Generate metallic/roughness/normal maps. Defaultfalse.texture_prompt(string): Additional text to guide texturing. Max 600 characters.texture_image_url(string): Image URL or data URI to guide texturing.ai_model(string):"meshy-5","meshy-6", or"latest"(default, resolves to Meshy 6). Refine works withmeshy-5,meshy-6, orlatest— pick the same family as your preview for consistency.texture_resolution(string): Base color texture resolution —"2k"(default, 2048²),"4k"(4096²), or"8k"(8192²)."4k"/"8k"requiremeshy-6/latest; at"8k"no emission map is produced. Refine mode only.hd_texture(boolean): Deprecated — usetexture_resolutioninstead (hd_texture: true≡texture_resolution: "4k"). When both are set,texture_resolutionwins.remove_lighting(boolean): Removes highlights and shadows from the base color texture. Defaulttrue. Meshy-6/latest only.moderation(boolean): Defaultfalse.target_formats(string[]): Output formats:"glb","obj","fbx","stl","usdz","3mf". Default: all except 3mf. 3mf must be explicitly included.auto_size(boolean): Use AI to auto-estimate real-world height. Defaultfalse.origin_at(string):"bottom"or"center". Default"bottom"when auto_size is true.
Cost: 10 credits (regardless of ai_model).
Response: {"result": "<task_id>"}
GET /openapi/v2/text-to-3d/:id — Retrieve Task
Returns the full task object including status, progress, model_urls, texture_urls.
DELETE /openapi/v2/text-to-3d/:id — Delete Task
Permanently deletes a task and all associated data.
GET /openapi/v2/text-to-3d — List Tasks
Query params: page_num (default 1), page_size (default 10, max 50), sort_by (+created_at or -created_at).
GET /openapi/v2/text-to-3d/:id/stream — Stream Task
Server-Sent Events stream for real-time task progress updates.
Text to 3D Task Object
{
"id": "018a210d-8ba4-705c-b111-1f1776f7f578",
"type": "text-to-3d-preview",
"model_urls": {
"glb": "https://assets.meshy.ai/.../model.glb?Expires=...",
"fbx": "https://assets.meshy.ai/.../model.fbx?Expires=...",
"obj": "https://assets.meshy.ai/.../model.obj?Expires=...",
"usdz": "https://assets.meshy.ai/.../model.usdz?Expires=..."
},
"prompt": "a monster mask",
"thumbnail_url": "https://assets.meshy.ai/.../preview.png?Expires=...",
"progress": 100,
"status": "SUCCEEDED",
"created_at": 1692771650657,
"started_at": 1692771667037,
"finished_at": 1692771669037,
"texture_urls": [
{
"base_color": "https://assets.meshy.ai/.../texture_0.png?Expires=...",
"metallic": "https://assets.meshy.ai/.../texture_0_metallic.png?Expires=...",
"normal": "https://assets.meshy.ai/.../texture_0_normal.png?Expires=...",
"roughness": "https://assets.meshy.ai/.../texture_0_roughness.png?Expires=..."
}
],
"preceding_tasks": 0,
"task_error": {"message": ""}
}
Image to 3D API
POST /openapi/v1/image-to-3d — Create Task
Generates a 3D model from a single image.
Required parameters:
image_url(string): Publicly accessible URL or base64 data URI (.jpg, .jpeg, .png).
Optional parameters:
model_type(string):"standard"(default),"smart-topology", or"lowpoly"(deprecated — the docs recommend"smart-topology"instead)."smart-topology"— cleaner topology, natively separated parts, triangle output. Ignorestopology,should_remesh,save_pre_remeshed_model."lowpoly"(deprecated) — ignoresai_model,topology,target_polycount,should_remesh,save_pre_remeshed_model.
ai_model(string): allowed values depend onmodel_type.model_type: "standard"→"meshy-5","meshy-6", or"latest"(default, Meshy 6).model_type: "smart-topology"→"meshy-t2"(default, recommended — supportstarget_polycount) or"meshy-t1"(the old low-poly model; does not support setting a face count viatarget_polycount).
topology(string):"quad"or"triangle"(default).target_polycount(integer): 100–300,000. Default 30,000.should_remesh(boolean): Defaultfalsefor Meshy 6,truefor others.save_pre_remeshed_model(boolean): Store GLB before remeshing. Defaultfalse.input_task_id(string): Chain directly off a succeededtext-to-image/image-to-imagetask — use its generated image as the input without re-uploading. Provide this ORimage_url.should_texture(boolean): Generate textures. Defaulttrue. Without texture: 20 credits (Meshy-6) / 5 credits (others). With texture: +10 credits.enable_pbr(boolean): PBR maps. Defaultfalse.texture_resolution(string):"2k"(default),"4k", or"8k"."4k"/"8k"are not available withmeshy-5; at"8k"no emission map is produced.hd_texture(boolean): Deprecated — usetexture_resolutioninstead (hd_texture: true≡texture_resolution: "4k"). When both are set,texture_resolutionwins.multi_view_thumbnails(boolean): Render four cardinal-view thumbnails (front / right / back / left) and return them underthumbnail_urls. Defaultfalse. Adds ~3s of latency. Use this instead of downloading the GLB when you only need to eyeball the result.alpha_thumbnail(boolean): Also render a transparent-background (RGBA) preview, returned asalpha_thumbnail_url. Defaultfalse.pose_mode(string):"a-pose","t-pose", or""(default). (Replaces the deprecatedis_a_t_poseflag.)decimation_mode(integer): Adaptive polycount mode,1–4.texture_prompt(string): Text to guide texturing. Max 600 characters.texture_image_url(string): Image to guide texturing.image_enhancement(boolean): Optimize input image. Defaulttrue. Meshy-6/latest only.remove_lighting(boolean): Removes highlights and shadows from the base color texture for cleaner results under custom lighting. Defaulttrue. Meshy-6/latest only.moderation(boolean): Defaultfalse.target_formats(string[]): Output formats:"glb","obj","fbx","stl","usdz","3mf". Default: all except 3mf. 3mf must be explicitly included.auto_size(boolean): Use AI to auto-estimate real-world height. Defaultfalse.origin_at(string):"bottom"or"center". Default"bottom"when auto_size is true.
Deprecated parameters: symmetry_mode — no longer affects output. art_style — ignored by Meshy-6. is_a_t_pose — superseded by pose_mode. hd_texture — superseded by texture_resolution. model_type: "lowpoly" — superseded by model_type: "smart-topology". meshy-4 is retired (returns 400).
Response: {"result": "<task_id>"}
GET /openapi/v1/image-to-3d/:id — Retrieve Task
DELETE /openapi/v1/image-to-3d/:id — Delete Task
GET /openapi/v1/image-to-3d — List Tasks
GET /openapi/v1/image-to-3d/:id/stream — Stream Task
Multi-Image to 3D API
POST /openapi/v1/multi-image-to-3d — Create Task
Generates a 3D model from 1–4 images of the same object from different angles.
Required parameters:
image_urls(array): 1–4 images as URLs or data URIs.
Optional parameters: Same as Image to 3D (except image_url → image_urls), including input_task_id chaining off a succeeded text-to-image / image-to-image result, texture_resolution, multi_view_thumbnails, and alpha_thumbnail.
Exception: Multi-Image to 3D has no
model_typeparameter at all — no"lowpoly", no"smart-topology".ai_modelis"meshy-5"/"meshy-6"/"latest"(default). Reduce polycount with the Remesh API instead.
Response: {"result": "<task_id>"}
GET /openapi/v1/multi-image-to-3d/:id — Retrieve Task
DELETE /openapi/v1/multi-image-to-3d/:id — Delete Task
GET /openapi/v1/multi-image-to-3d — List Tasks
GET /openapi/v1/multi-image-to-3d/:id/stream — Stream Task
Remesh API
Remesh and export existing 3D models into various formats.
POST /openapi/v1/remesh — Create Task
Required (one of):
input_task_id(string): ID of a succeeded Text to 3D, Image to 3D, or Retexture task.model_url(string): URL or data URI of a 3D model (.glb, .gltf, .obj, .fbx, .stl).
If both are provided, input_task_id takes precedence.
Optional parameters:
target_formats(array): Formats to export. Values:"glb","fbx","obj","usdz","blend","stl","3mf". Default["glb"]. 3mf must be explicitly included.topology(string):"quad"or"triangle"(default).target_polycount(integer): 100–300,000. Default 30,000.resize_height(number): Height in meters. Default 0 (no resize). Mutually exclusive withauto_size.auto_size(boolean): Use AI to auto-estimate real-world height. Mutually exclusive withresize_height. Defaultfalse.origin_at(string):"bottom"or"center". Default"bottom"when auto_size is true.convert_format_only(boolean): Only convert format, skip remeshing. Defaultfalse.
Cost: 5 credits.
Response: {"result": "<task_id>"}
GET /openapi/v1/remesh/:id — Retrieve Task
DELETE /openapi/v1/remesh/:id — Delete Task
GET /openapi/v1/remesh — List Tasks
GET /openapi/v1/remesh/:id/stream — Stream Task
Retexture API
Apply new AI-generated textures to existing 3D models.
Alias note: The historical "text-to-texture" feature has been renamed to retexture. The public docs URL
/api/text-to-textureredirects to/api/retexture, and there is no separate/openapi/v1/text-to-textureendpoint — use/openapi/v1/retexturefor both text-style-prompt and image-style-url retexturing.
POST /openapi/v1/retexture — Create Task
Required (one of each):
input_task_idormodel_url: The model to retexture.text_style_promptorimage_style_url: The style to apply.
Optional parameters:
ai_model(string):"meshy-5","meshy-6", or"latest"(default, resolves to Meshy 6).enable_original_uv(boolean): Preserve original UV mapping. Defaulttrue.enable_pbr(boolean): PBR maps. Defaultfalse.texture_resolution(string):"2k"(default),"4k", or"8k". At"8k"no emission map is produced.hd_texture(boolean): Deprecated — usetexture_resolutioninstead (hd_texture: true≡texture_resolution: "4k"). When both are set,texture_resolutionwins.remove_lighting(boolean): Removes highlights and shadows from the base color texture. Defaulttrue. Meshy-6/latest only.target_formats(string[]): Output formats:"glb","obj","fbx","stl","usdz","3mf". Default: all except 3mf. 3mf must be explicitly included.
Note: image_style_url takes precedence if both text_style_prompt and image_style_url are provided.
Cost: 10 credits.
Response: {"result": "<task_id>"}
GET /openapi/v1/retexture/:id — Retrieve Task
DELETE /openapi/v1/retexture/:id — Delete Task
GET /openapi/v1/retexture — List Tasks
GET /openapi/v1/retexture/:id/stream — Stream Task
Analyze Printability API
FDM printability analysis. Reports watertightness, volume, holes, non-manifold edges, degenerate faces. Cost: FREE (0 credits).
POST /openapi/v1/print/analyze — Create Task
Provide exactly one of:
input_task_id(string): A SUCCEEDED task you own (image-to-3d, multi-image-to-3d, text-to-3d, remesh, retexture). MUST use Meshy 6 or any Preview model.model_url(string): Public URL of a 3D model. Supported formats:.glb,.gltf,.obj,.fbx,.stl. Max 100 MB. Must usehttp,https, ordata:URL.
Response: {"result": "<task_id>"}
GET /openapi/v1/print/analyze/:id — Retrieve Task
Once SUCCEEDED, the task object contains:
{
"id": "...",
"type": "print-analyze",
"status": "SUCCEEDED",
"progress": 100,
"printability": {
"_version": "v1",
"status": "warning",
"issue_count": 1,
"error_count": 0,
"warning_count": 1,
"metrics": {
"is_watertight": true,
"volume": 1.316,
"non_manifold_edges": 0,
"degenerate_faces": 43242,
"holes": 0
},
"evaluated_at": 1700000001000
},
"consumed_credits": 0
}
printability.status semantics:
healthy: no errors, no warnings.warning: at least one warning, no errors. (Triggered by degenerate faces or holes.)error: at least one error. (Triggered by non-watertight, non-positive volume, or non-manifold edges.) Recommend running repair.unknown: model could not be analyzed.
DELETE /openapi/v1/print/analyze/:id — Delete Task
GET /openapi/v1/print/analyze — List Tasks
GET /openapi/v1/print/analyze/:id/stream — Stream Task (SSE)
Repair Printability API
Repair non-manifold edges, degenerate faces, holes, and ensure watertightness. Cost: 10 credits.
POST /openapi/v1/print/repair — Create Task
Provide exactly one of:
input_task_id(string): A SUCCEEDED task with a GLB asset. Output is GLB.model_url(string): Public URL of.glb/.stl/.obj. Max 100 MB. Output format matches input extension.
Response: {"result": "<task_id>"}
GET /openapi/v1/print/repair/:id — Retrieve Task
{
"id": "...",
"type": "print-repair",
"status": "SUCCEEDED",
"model_urls": {
"glb": "https://...glb?Expires=...",
"fbx": "",
"obj": "",
"stl": "",
"usdz": "",
"3mf": "",
"mtl": ""
},
"thumbnail_url": "https://...preview.png",
"texture_urls": [],
"consumed_credits": 10
}
Only the field matching the input format is populated; other fields are empty strings. Textures are NOT preserved (geometry-only repair).
DELETE /openapi/v1/print/repair/:id — Delete Task
GET /openapi/v1/print/repair — List Tasks
GET /openapi/v1/print/repair/:id/stream — Stream Task (SSE)
Multi-Color Print API
Process a textured 3D model for multi-color 3D printing. Segments the model's texture into discrete color regions and outputs a 3MF file. Cost: 10 credits.
POST /openapi/v1/print/multi-color — Create Task
Provide exactly one of:
input_task_id(string): ID of a completed task with textures (Text to 3D refine, Image to 3D with texture, or Retexture).model_url(string): Public URL of a textured.glbor.fbxmodel.
Optional parameters:
max_colors(integer, 1-16, default 4): Maximum number of colors for segmentation.max_depth(integer, 3-6, default 4): Color segmentation depth. Higher values produce finer color boundaries.
Response: {"result": "<task_id>"}
Example:
task_id = create_task("/openapi/v1/print/multi-color", {
"input_task_id": "textured-task-uuid",
"max_colors": 4,
"max_depth": 4,
})
# OR with a model URL:
# task_id = create_task("/openapi/v1/print/multi-color", {
# "model_url": "https://example.com/textured.glb",
# "max_colors": 6,
# })
GET /openapi/v1/print/multi-color/:id — Retrieve Task
Returns the task object including status, progress, model_urls. Note: response type field is "print-multi-color".
Completed task model_urls:
{
"3mf": "https://assets.meshy.ai/.../model.3mf?Expires=..."
}
GET /openapi/v1/print/multi-color/:id/stream — Stream Task (SSE)
Server-Sent Events stream. Events include: status, progress, model_urls (contains {"3mf": "https://..."}), task_error.
Auto-Rigging API
Create an internal skeleton and bind mesh to it for animation.
Preconditions — check these BEFORE spending credits
Auto-rigging is not suitable for: untextured meshes · non-humanoid assets · humanoid assets with unclear limb and body structure.
- The input model must be textured. The docs state: "We currently support textured humanoid models." A Text to 3D preview task is mesh-only — rigging it will fail. Texture first (
mode: "refine", or Image to 3D withshould_texture: true, or Retexture), then pass that task ID. - Standard humanoid (bipedal) only, with clearly defined limbs and body structure. A non-humanoid input fails pose estimation and returns
422. - ≤ 300,000 faces when using
input_task_id. Over that returns400 Face count exceeded— reduce with the Remesh API first. - With
model_url, the character's face must point toward the +Z axis (the standard glTF forward direction). Models facing another axis fail pose estimation. - Generating with
pose_mode: "t-pose"gives the best rigging results — decide this at generation time, since it cannot be changed afterwards.
POST /openapi/v1/rigging — Create Task
Required (one of):
input_task_id(string): ID of a succeeded task producing a textured humanoid model. Takes priority ifmodel_urlis also given.model_url(string): URL or data URI of a textured humanoid GLB file (.glbonly).
Optional parameters:
height_meters(number): Character height in meters. Default 1.7.texture_image_url(string): UV-unwrapped base color texture (.png).
Cost: 5 credits.
Response: {"result": "<task_id>"}
Succeeded result includes:
rigged_character_glb_url,rigged_character_fbx_urlbasic_animations.walking_glb_url,walking_fbx_url,walking_armature_glb_urlbasic_animations.running_glb_url,running_fbx_url,running_armature_glb_url
GET /openapi/v1/rigging/:id — Retrieve Task
DELETE /openapi/v1/rigging/:id — Delete Task
GET /openapi/v1/rigging/:id/stream — Stream Task
Animation API
Apply animations to rigged characters.
Finding a valid action_id (do this first)
The public Animation Library catalog is served as JSON, no API key required:
GET https://api.meshy.ai/web/public/animations/resources
The docs point here directly: "The full list of available animations (with action_id, name, category, preview URL) is served as JSON at https://api.meshy.ai/web/public/animations/resources — fetch it directly to retrieve the current catalog."
- Response shape:
{"result": {"total": <int>, "list": [ ... ]}}. Each entry carriesid(this is theaction_id),name,key,category,subCategory,previewUrl(an animated GIF),rigType,isDefault,isFree. - Categories currently in the catalog:
WalkAndRun,BodyMovements,DailyActions,Fighting,Dancing. - Narrow the payload with
?category=<Category>(e.g.?category=Fighting) to keep it out of your context window. - Never hardcode or guess an ID. IDs are not a
1..Nrange — the catalog includes-2,-1, and0. Match the user's intent againstname/category/subCategory, and when several candidates fit, show the user thepreviewUrlGIFs and let them pick.
# Pick an action_id for "a waving character"
curl -s "https://api.meshy.ai/web/public/animations/resources?category=DailyActions" \
| python3 -c "import json,sys; [print(a['id'], a['name'], '|', a['subCategory'], '|', a['previewUrl']) for a in json.load(sys.stdin)['result']['list'] if 'wav' in a['name'].lower()]"
POST /openapi/v1/animations — Create Task
Required parameters:
rig_task_id(string): ID of a succeeded rigging task (fromPOST /openapi/v1/rigging).action_id(integer): Animation action ID — look it up in the public catalog above.
Optional parameters:
post_process(object):operation_type(string):"change_fps","fbx2usdz", or"extract_armature".fps(integer): 24, 25, 30, or 60. Only forchange_fps.
Cost: 3 credits.
Response: {"result": "<task_id>"}
Succeeded result includes:
animation_glb_url,animation_fbx_urlprocessed_usdz_url,processed_armature_fbx_url,processed_animation_fps_fbx_url
GET /openapi/v1/animations/:id — Retrieve Task
DELETE /openapi/v1/animations/:id — Delete Task
GET /openapi/v1/animations/:id/stream — Stream Task
Text to Image API
POST /openapi/v1/text-to-image — Create Task
Required parameters:
ai_model(string):"nano-banana","nano-banana-2","nano-banana-pro", or"gpt-image-2".prompt(string): Text description of the image.
Optional parameters:
generate_multi_view(boolean): Multi-angle views. Defaultfalse. Cannot be used withaspect_ratio.multi_view_thumbnails(boolean): Generate 4 cardinal-direction thumbnails. Defaultfalse.alpha_thumbnail(boolean): Generate an RGBA (transparent-background) preview, returned inalpha_thumbnail_url. Defaultfalse.pose_mode(string):"a-pose"or"t-pose".aspect_ratio(string):"1:1"(default),"16:9","9:16","4:3","3:4","3:2","2:3".
Aspect-ratio support is model-specific: nano-banana / nano-banana-2 / nano-banana-pro accept
1:1,16:9,9:16,4:3,3:4. gpt-image-2 accepts ONLY1:1,3:2,2:3— and3:2/2:3are gpt-image-2-only (the nano-banana family rejects them with 400).
Cost: nano-banana 3 credits, nano-banana-2 6 credits, nano-banana-pro 9 credits, gpt-image-2 9 credits.
Response: {"result": "<task_id>"}
GET /openapi/v1/text-to-image/:id — Retrieve Task
DELETE /openapi/v1/text-to-image/:id — Delete Task
GET /openapi/v1/text-to-image — List Tasks
GET /openapi/v1/text-to-image/:id/stream — Stream Task
Image to Image API
POST /openapi/v1/image-to-image — Create Task
Required parameters:
ai_model(string):"nano-banana","nano-banana-2","nano-banana-pro", or"gpt-image-2".prompt(string): Text description of the transformation.reference_image_urls(array): 1–5 reference images as URLs or data URIs.
Optional parameters:
generate_multi_view(boolean): Defaultfalse.multi_view_thumbnails(boolean): Generate 4 cardinal-direction thumbnails. Defaultfalse.alpha_thumbnail(boolean): Generate an RGBA (transparent-background) preview, returned inalpha_thumbnail_url. Defaultfalse.
Aspect-ratio support is model-specific: nano-banana / nano-banana-2 / nano-banana-pro accept
1:1,16:9,9:16,4:3,3:4. gpt-image-2 accepts ONLY1:1,3:2,2:3— and3:2/2:3are gpt-image-2-only (the nano-banana family rejects them with 400).
Cost: nano-banana 3 credits, nano-banana-2 6 credits, nano-banana-pro 9 credits, gpt-image-2 12 credits.
Response: {"result": "<task_id>"}
GET /openapi/v1/image-to-image/:id — Retrieve Task
DELETE /openapi/v1/image-to-image/:id — Delete Task
GET /openapi/v1/image-to-image — List Tasks
GET /openapi/v1/image-to-image/:id/stream — Stream Task
Convert API
Convert a model to other file formats without remeshing. Cost: 1 credit. Print-relevant use: get a printable STL or 3MF from an existing GLB/OBJ result.
POST /openapi/v1/convert — Create Task
Provide exactly one of:
input_task_id(string): A succeeded task you own.model_url(string): Public URL or data URI of a 3D model.
Required parameters:
target_formats(string[]): One or more of"glb","fbx","obj","usdz","blend","stl","3mf".
Response: {"result": "<task_id>"}
GET /openapi/v1/convert/:id — Retrieve Task
DELETE /openapi/v1/convert/:id — Delete Task
GET /openapi/v1/convert — List Tasks
GET /openapi/v1/convert/:id/stream — Stream Task
Resize API
Rescale a model to a real-world size. Cost: 1 credit. Print-relevant use: set the model to a real-world height (mm → m) before slicing so the slicer imports it at the right scale.
POST /openapi/v1/resize — Create Task
Provide exactly one model source:
input_task_id(string), ORmodel_url(string).
Provide exactly one resize mode:
resize_height(number): Target height in meters.resize_longest_side(number): Target longest bounding-box side in meters.auto_size(boolean): AI auto-estimates a real-world height.
Optional parameters:
origin_at(string):"bottom"or"center".
Response: {"result": "<task_id>"}
GET /openapi/v1/resize/:id — Retrieve Task
DELETE /openapi/v1/resize/:id — Delete Task
GET /openapi/v1/resize — List Tasks
GET /openapi/v1/resize/:id/stream — Stream Task
UV Unwrap API
Generate fresh UVs for a GLB model. Cost: 5 credits. Available (GA). Print-relevant use: produce a clean UV white model before externally painting/texturing a model for multicolor printing.
POST /openapi/v1/uv-unwrap — Create Task
Provide exactly one of:
input_task_id(string): A succeeded task whose GLB asset will be unwrapped.model_url(string): Public URL or data URI of a GLB model.
Constraints: GLB only, ≤ 40,000 faces (above this the API returns 400 — remesh down first). Quads/n-gons are triangulated.
Output: a GLB "UV white model" — the mesh with fresh UVs and a placeholder grey material (no textures). Use it as a clean base for external texturing.
Response: {"result": "<task_id>"}
GET /openapi/v1/uv-unwrap/:id — Retrieve Task
DELETE /openapi/v1/uv-unwrap/:id — Delete Task
GET /openapi/v1/uv-unwrap — List Tasks
GET /openapi/v1/uv-unwrap/:id/stream — Stream Task
Creative Lab API
Two-stage pipeline that turns a photo into a styled physical product, ready to print. Four products: figure, lamp, keychain, fridge-magnet. Replace {product} in the path with one of these.
POST /openapi/creative-lab/{product}/v1/prototype — Create Prototype Task
Photo → styled concept image. Cost: 6 credits.
Required parameters:
image_url(string): jpg/jpeg/png/webp URL or data URI.
Optional parameters:
name(string): ≤ 100 characters.
Response: {"result": "<task_id>"}
POST /openapi/creative-lab/{product}/v1/build — Create Build Task
Runs the image-to-3d pipeline on a succeeded prototype → textured GLB / OBJ+MTL. Cost: 30 credits.
Required parameters:
input_task_id(string): A SUCCEEDED prototype task of the same product and key. Web-app prototypes are rejected with 404 — the prototype must have been created via this API.
Optional parameters:
name(string).
Response: {"result": "<task_id>"}
GET /openapi/creative-lab/{product}/v1/{prototype|build}/:id — Retrieve Task
DELETE /openapi/creative-lab/{product}/v1/{prototype|build}/:id — Delete Task
GET /openapi/creative-lab/{product}/v1/{prototype|build} — List Tasks
GET /openapi/creative-lab/{product}/v1/{prototype|build}/:id/stream — Stream Task
Note: GET/DELETE/stream are per-stage — there is no stage-less
/v1/:id. Use theprototypeorbuildsegment matching the task you created.
Balance API
GET /openapi/v1/balance — Get Balance
Returns the current credit balance.
Response: {"balance": 1000}
Enterprise API
GET /openapi/v1/showcases — List Showcases
Search and download community showcase models. Enterprise tier only.
Optional parameters:
page_size(integer): 1–10. Default 3.sort_by(string):"+created_at","-created_at","+updated_at","-updated_at","+downloads","-downloads".search(string): Text search in model names.format(string):"glb"(default),"fbx","obj","usdz".showcase_type(string):"all"(default),"animated","static".
Cost: 1 credit per request.
Webhooks
Configure webhooks at https://www.meshy.ai/settings/api to receive task status updates via HTTP POST. Max 5 active webhooks per account. HTTPS only.
Your server must respond with HTTP status < 400. Consecutive failures may auto-disable the webhook.
Webhook payloads contain the full task object in JSON format matching the corresponding API's task object schema.
Pricing Summary
| API | Cost |
|---|---|
| Text to 3D Preview (Meshy-6/lowpoly) | 20 credits |
| Text to 3D Preview (other models) | 5 credits |
| Text to 3D Refine | 10 credits |
| Image to 3D (Meshy-6, no texture) | 20 credits |
| Image to 3D (Meshy-6, with texture) | 30 credits |
| Image to 3D (other, no texture) | 5 credits |
| Image to 3D (other, with texture) | 15 credits |
| Multi-Image to 3D (Meshy-6, no texture) | 20 credits |
| Multi-Image to 3D (Meshy-6, with texture) | 30 credits |
| Multi-Image to 3D (other, no texture) | 5 credits |
| Multi-Image to 3D (other, with texture) | 15 credits |
| Retexture | 10 credits |
| Remesh | 5 credits |
| Convert | 1 credit |
| Resize | 1 credit |
| UV Unwrap | 5 credits |
| Multi-Color Print | 10 credits |
| Analyze Printability | 0 (free) |
| Repair Printability | 10 credits |
| Auto-Rigging | 5 credits |
| Animation | 3 credits |
| Text to Image (nano-banana) | 3 credits |
| Text to Image (nano-banana-2) | 6 credits |
| Text to Image (nano-banana-pro) | 9 credits |
| Text to Image (gpt-image-2) | 9 credits |
| Image to Image (nano-banana) | 3 credits |
| Image to Image (nano-banana-2) | 6 credits |
| Image to Image (nano-banana-pro) | 9 credits |
| Image to Image (gpt-image-2) | 12 credits |
| Creative Lab (prototype) | 6 credits |
| Creative Lab (build) | 30 credits |
Every task GET response includes a
consumed_creditsfield — read it to report the real credits spent on a task rather than estimating.A
FAILEDtask costs nothing. The docs stateconsumed_credits"Returns0forFAILEDtasks (credits are refunded on failure)." So a failed task is a lost wait, not lost credits — retry a transient failure instead of asking the user to re-approve the spend.
Error Handling
HTTP Status Codes
200 OK: Success.202 Accepted: Task created, processing not yet complete.400 Bad Request: Missing or invalid parameter. For rigging this also coversFace count exceeded(input model over 300,000 faces — remesh it down first).401 Unauthorized: Invalid API key.402 Payment Required: Insufficient credits.403 Forbidden: CORS or permission issue.404 Not Found: Resource not found.422 Unprocessable Entity: Valid request but cannot process. For rigging this is pose estimation failing — the model is not a valid humanoid, or it is untextured, or (withmodel_url) it is not facing +Z.429 Too Many Requests: Rate limit exceeded.5xx: Server error.
Error Response Format
{"message": "Invalid model file extension: .3dm"}
Task Failure
When status is "FAILED", check task_error.message:
"The server is busy. Please try again later."— Timeout or server overload. Retry with exponential backoff."Internal server error."— Processing failure. Verify inputs and retry.
A FAILED task reports consumed_credits: 0 — credits are refunded on failure, so retrying costs the user nothing beyond the wait.