Files
boliu b9db44b566 [Fix] Correct API facts that make agents do the wrong thing (ENG-1576) (#7)
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>
2026-08-08 19:16:45 +08:00

41 KiB
Raw Permalink Blame History

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" with ai_model: "meshy-t2" (default and recommended for that model type). model_type: "lowpoly" is deprecated — the docs recommend smart-topology instead. smart-topology exists 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: true if you need metallic/roughness/normal maps.
  • Don't set both texture_prompt and texture_image_url — if both are provided, texture_prompt takes precedence.
  • Don't assume model format availability. Check that the URL key exists in model_urls before downloading.
  • Don't guess an action_id. Animation action IDs are not a 1..N range (the catalog includes -2, -1, and 0). 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 with should_texture: true, or Retexture), then rig.
  • Don't pass hd_texture. It is deprecated in favour of texture_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_remesh are 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 with smart-topology.
  • ai_model (string): "meshy-5", "meshy-6", or "latest" (default, resolves to Meshy 6).
  • topology (string): "quad" or "triangle" (default).
  • target_polycount (integer): 100300,000. Default 30,000.
  • should_remesh (boolean): Default false for Meshy 6, true for others.
  • pose_mode (string): "a-pose", "t-pose", or "" (default). (Replaces the deprecated is_a_t_pose flag.)
  • decimation_mode (integer): Adaptive polycount mode, 14. Trades detail vs. polygon budget.
  • moderation (boolean): Screen input for harmful content. Default false.
  • 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. Default false.
  • 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-4 is retired; requests that pass ai_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. Default false.
  • 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 with meshy-5, meshy-6, or latest — 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" require meshy-6/latest; at "8k" no emission map is produced. Refine mode only.
  • hd_texture (boolean): Deprecated — use texture_resolution instead (hd_texture: truetexture_resolution: "4k"). When both are set, texture_resolution wins.
  • remove_lighting (boolean): Removes highlights and shadows from the base color texture. Default true. Meshy-6/latest only.
  • moderation (boolean): Default false.
  • 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. Default false.
  • 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. Ignores topology, should_remesh, save_pre_remeshed_model.
    • "lowpoly" (deprecated) — ignores ai_model, topology, target_polycount, should_remesh, save_pre_remeshed_model.
  • ai_model (string): allowed values depend on model_type.
    • model_type: "standard""meshy-5", "meshy-6", or "latest" (default, Meshy 6).
    • model_type: "smart-topology""meshy-t2" (default, recommended — supports target_polycount) or "meshy-t1" (the old low-poly model; does not support setting a face count via target_polycount).
  • topology (string): "quad" or "triangle" (default).
  • target_polycount (integer): 100300,000. Default 30,000.
  • should_remesh (boolean): Default false for Meshy 6, true for others.
  • save_pre_remeshed_model (boolean): Store GLB before remeshing. Default false.
  • input_task_id (string): Chain directly off a succeeded text-to-image / image-to-image task — use its generated image as the input without re-uploading. Provide this OR image_url.
  • should_texture (boolean): Generate textures. Default true. Without texture: 20 credits (Meshy-6) / 5 credits (others). With texture: +10 credits.
  • enable_pbr (boolean): PBR maps. Default false.
  • texture_resolution (string): "2k" (default), "4k", or "8k". "4k"/"8k" are not available with meshy-5; at "8k" no emission map is produced.
  • hd_texture (boolean): Deprecated — use texture_resolution instead (hd_texture: truetexture_resolution: "4k"). When both are set, texture_resolution wins.
  • multi_view_thumbnails (boolean): Render four cardinal-view thumbnails (front / right / back / left) and return them under thumbnail_urls. Default false. 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 as alpha_thumbnail_url. Default false.
  • pose_mode (string): "a-pose", "t-pose", or "" (default). (Replaces the deprecated is_a_t_pose flag.)
  • decimation_mode (integer): Adaptive polycount mode, 14.
  • texture_prompt (string): Text to guide texturing. Max 600 characters.
  • texture_image_url (string): Image to guide texturing.
  • image_enhancement (boolean): Optimize input image. Default true. Meshy-6/latest only.
  • remove_lighting (boolean): Removes highlights and shadows from the base color texture for cleaner results under custom lighting. Default true. Meshy-6/latest only.
  • moderation (boolean): Default false.
  • 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. Default false.
  • 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 14 images of the same object from different angles.

Required parameters:

  • image_urls (array): 14 images as URLs or data URIs.

Optional parameters: Same as Image to 3D (except image_urlimage_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_type parameter at all — no "lowpoly", no "smart-topology". ai_model is "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): 100300,000. Default 30,000.
  • resize_height (number): Height in meters. Default 0 (no resize). Mutually exclusive with auto_size.
  • auto_size (boolean): Use AI to auto-estimate real-world height. Mutually exclusive with resize_height. Default false.
  • origin_at (string): "bottom" or "center". Default "bottom" when auto_size is true.
  • convert_format_only (boolean): Only convert format, skip remeshing. Default false.

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-texture redirects to /api/retexture, and there is no separate /openapi/v1/text-to-texture endpoint — use /openapi/v1/retexture for both text-style-prompt and image-style-url retexturing.

POST /openapi/v1/retexture — Create Task

Required (one of each):

  • input_task_id or model_url: The model to retexture.
  • text_style_prompt or image_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. Default true.
  • enable_pbr (boolean): PBR maps. Default false.
  • texture_resolution (string): "2k" (default), "4k", or "8k". At "8k" no emission map is produced.
  • hd_texture (boolean): Deprecated — use texture_resolution instead (hd_texture: truetexture_resolution: "4k"). When both are set, texture_resolution wins.
  • remove_lighting (boolean): Removes highlights and shadows from the base color texture. Default true. 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 use http, https, or data: 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 .glb or .fbx model.

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.

  1. 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 with should_texture: true, or Retexture), then pass that task ID.
  2. Standard humanoid (bipedal) only, with clearly defined limbs and body structure. A non-humanoid input fails pose estimation and returns 422.
  3. ≤ 300,000 faces when using input_task_id. Over that returns 400 Face count exceeded — reduce with the Remesh API first.
  4. 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.
  5. 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 if model_url is also given.
  • model_url (string): URL or data URI of a textured humanoid GLB file (.glb only).

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_url
  • basic_animations.walking_glb_url, walking_fbx_url, walking_armature_glb_url
  • basic_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 carries id (this is the action_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..N range — the catalog includes -2, -1, and 0. Match the user's intent against name / category / subCategory, and when several candidates fit, show the user the previewUrl GIFs 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 (from POST /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 for change_fps.

Cost: 3 credits.

Response: {"result": "<task_id>"}

Succeeded result includes:

  • animation_glb_url, animation_fbx_url
  • processed_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. Default false. Cannot be used with aspect_ratio.
  • multi_view_thumbnails (boolean): Generate 4 cardinal-direction thumbnails. Default false.
  • alpha_thumbnail (boolean): Generate an RGBA (transparent-background) preview, returned in alpha_thumbnail_url. Default false.
  • 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 ONLY 1:1, 3:2, 2:3 — and 3:2 / 2:3 are 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): 15 reference images as URLs or data URIs.

Optional parameters:

  • generate_multi_view (boolean): Default false.
  • multi_view_thumbnails (boolean): Generate 4 cardinal-direction thumbnails. Default false.
  • alpha_thumbnail (boolean): Generate an RGBA (transparent-background) preview, returned in alpha_thumbnail_url. Default false.

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 ONLY 1:1, 3:2, 2:3 — and 3:2 / 2:3 are 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), OR model_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 the prototype or build segment 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): 110. 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_credits field — read it to report the real credits spent on a task rather than estimating.

A FAILED task costs nothing. The docs state consumed_credits "Returns 0 for FAILED tasks (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 covers Face 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 (with model_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.