The Mocai API turns a video into a 3D take (mesh, skeleton, and exports) with a few HTTP calls. Uploads count toward your plan's monthly footage allowance and appear in your library next to portal uploads. Grab the Postman collection to start in minutes, or skip the HTTP entirely with the Blender add-on, the Unity plugin, the Maya module, or the Unreal plugin.
Send your API key as a bearer token on every request. Keys start with mk_live_ and are shown once at creation. Create and revoke them under App → Developers. API access is included on every plan, Free included. Requests are rate-limited per account (Free 10/min, Basic 20/min, Pro 60/min; all your keys share the budget); every response carries RateLimit-Limit / -Remaining / -Reset headers, and going over returns 429 rate_limited with Retry-After. Sign-in tokens from the CLI (mocai login) are accepted the same way.
curl {BASE}/api/v1/takes \
-H "Authorization: Bearer mk_live_…"Uploads are two steps so video bytes go straight to storage: POST /api/v1/uploads returns a list of parts (byte ranges with their own upload URLs; send them concurrently for roughly double the throughput), you PUT each range, then create the take from the uploadId; the parts are stitched together server-side. Options: hands toggles finger tracking, maxPersons is capped by your plan, an optional selection of normalized first-frame boxes pins exactly which people to reconstruct, and smoothing (off | standard | strong, default standard) applies zero-lag temporal smoothing to the skeleton and mesh exports and portal playback; keypoint JSON/CSV always stay raw for analytics. An optional exports array (fbx | glb | usd | bvh, default ["fbx"]) picks which native formats are baked up front; every other kind can be generated on a ready take with one request, so select what you will actually open. mesh (default true) attaches the performer's skinned body mesh to the FBX/GLB/USD skeleton exports; set it false for armature-only files. An optional rigs array (mixamo | ue5 | metahuman) adds FBX exports retargeted onto those skeletons, and rootMotion picks how every skeleton export travels: drop-in (default) scales motion to each skeleton's canonical proportions so it applies straight to a stock character, while performer keeps the subject's true scale and trajectory for measurement or in-engine retargeting. referencePose (none | t-pose | a-pose, default none) adds a reference-pose frame before the motion in every skeleton export (native FBX/GLB/USD/BVH and the rig retargets), standing at the origin facing forward, so a manual retarget in Maya or MotionBuilder has a frame where both skeletons match. Each rig gets its own version of the pose (the UE5 and MetaHuman A-poses match Epic's skeletons). The motion then starts at frame 1, one frame later than the video; keypoint JSON/CSV and usd-mesh are unchanged. For clips under ~30 MB you can still POST multipart/form-data with a file field directly to /api/v1/takes in one call. If you are building an integration, send an X-Mocai-Client header naming it (name/version, like mocai-blender/1.4.0); the value is echoed back as client on the take resource. Our DCC plugins do this automatically.
# 1) Create an upload session
curl -X POST {BASE}/api/v1/uploads \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-d '{ "contentType": "video/mp4", "sizeBytes": 48211234 }'
# → 201 { "uploadId": "9dK1…", "maxBytes": 2147483648, "parts": [
# { "url": "https://storage.googleapis.com/…", "offset": 0, "size": 16070412 },
# { "url": "https://storage.googleapis.com/…", "offset": 16070412, "size": 16070411 },
# { "url": "https://storage.googleapis.com/…", "offset": 32140823, "size": 16070411 } ] }
# 2) PUT each part's byte range to its URL, in parallel for ~2× throughput
# (no auth header; each URL is its own credential; ≤8 MB files get one part)
curl -X PUT --data-binary @part0.bin "https://storage.googleapis.com/…" &
curl -X PUT --data-binary @part1.bin "https://storage.googleapis.com/…" &
curl -X PUT --data-binary @part2.bin "https://storage.googleapis.com/…" &
wait
# 3) Create the take from the finished upload
curl -X POST {BASE}/api/v1/takes \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-d '{ "uploadId": "9dK1…", "title": "Golf swing", "hands": true, "maxPersons": 1, "smoothing": "standard", "exports": ["fbx", "glb"] }'
# → 201
{
"id": "8Fk2…",
"status": "queued",
"via": "api",
"client": null,
"options": { "hands": true, "maxPersons": 1, "smoothing": "standard",
"exports": ["fbx", "glb"], "mesh": true },
"exports": []
}Status moves through queued → processing → baking → ready. When ready, exports[] lists the assets you selected at upload (default: FBX) plus any requested rig retargets. Ready no longer means every kind exists; it means your selection does. The full menu: FBX, GLB, BVH, USD on the native skeleton, usd-mesh (per-frame mesh animation; generated from the rigged character on newer takes), raw keypoints as JSON/CSV, and the rig retargets (fbx-mixamo, fbx-ue5, fbx-metahuman). Native skeleton exports carry the solver's full 127-joint body rig — 4-joint spine, arm/leg twist chains, articulated feet, and full fingers — with a true standing bind pose (zero rotation = rest), so auto-retargeting tools calibrate correctly, and by default FBX/GLB/USD also include the performer's skinned body mesh bound to that rig (a richer file that imports exactly the same; opt out with mesh: false); the fbx-<rig> kinds are the same motion on simpler standard skeletons, armature-only. Any kind can be added to a take that is already ready: POST /api/v1/takes/{id}/exports with { "kinds": ["glb", "fbx-mixamo"] } queues a background job (202) and the new kinds appear on the resource when done (a take.exported webhook fires too). The pre-selection spelling { "rigs": ["mixamo"] } still works. The export endpoint answers with a 302 to a short-lived download URL — follow the redirect (standard clients like curl -L handle this, and correctly drop the Authorization header on the way). Takes older than your plan's retention window become expired (files removed; expiresAt on the resource warns 7 days ahead). A clip is refused up front if your monthly allowance is already spent; the take that crosses the cap still completes.
curl {BASE}/api/v1/takes/8Fk2… \
-H "Authorization: Bearer mk_live_…"
{
"id": "8Fk2…",
"status": "ready",
"frames": 240,
"persons": 1,
"exports": [
{ "kind": "fbx", "url": "/api/v1/takes/8Fk2…/exports/fbx" },
{ "kind": "glb", "url": "/api/v1/takes/8Fk2…/exports/glb" }
]
}
# any other kind is one request away
curl -X POST {BASE}/api/v1/takes/8Fk2…/exports \
-H "Authorization: Bearer mk_live_…" \
-d '{ "kinds": ["bvh", "keypoints-json"] }' # → 202, poll the takeRegister an endpoint and we POST a signed event when a take finishes. No polling. Events: take.ready, take.failed, and take.exported (rig exports added to a ready take). Verify X-Mocai-Signature by computing HMAC-SHA256 of the raw request body with your signing secret.
# register once
curl -X PUT {BASE}/api/v1/webhook \
-H "Authorization: Bearer mk_live_…" \
-d '{ "url": "https://your-app.com/hooks/mocai" }'
# then we POST you signed events, no polling
POST https://your-app.com/hooks/mocai
X-Mocai-Event: take.ready
X-Mocai-Signature: sha256=5f2b… # HMAC-SHA256(secret, raw body)
{
"event": "take.ready",
"data": { "take": { "id": "8Fk2…", "status": "ready", "exports": [ … ] } }
}GET /api/v1/account returns your plan, this month's footage usage, and the limits that apply to your account, so a pipeline can check what's left before it uploads. Minutes are 30fps-equivalent (a minute of 60fps video counts as two), and minutesReserved covers takes still processing. Usage resets at resetsAt, 00:00 UTC on the 1st. On a custom plan, minutes past the included amount are billed rather than refused: overage shows how many so far and the projected cost, and uploads only pause at hardCapMinutes if your plan has one. It's also a handy way to check an API key works.
curl {BASE}/api/v1/account \
-H "Authorization: Bearer mk_live_…"
{
"account": { "email": "you@studio.com", "plan": "pro", "planName": "Pro" },
"usage": {
"period": "2026-10",
"minutesUsed": 41.5, // completed takes, 30fps-equivalent minutes
"minutesReserved": 2.1, // takes still processing
"minutesIncluded": 60,
"minutesRemaining": 16.4,
"takes": 23,
"resetsAt": "2026-11-01T00:00:00.000Z"
},
"allowances": { "maxClipSeconds": 60, "maxPersons": 10, "retentionDays": 90, … },
"boundary": { "includedMinutes": 60, "hardCapMinutes": 60, "billsOverage": false },
"overage": null, // custom plans: { "minutes", "centsPerMinute", "projectedCents" }
"rateLimit": { "limit": 60, "remaining": 59, "resetSec": 60 }
}AI agents can drive the whole flow through the Model Context Protocol at https://api.mocai.ai/v1/mcp (streamable HTTP). Apps with connector support, like Claude and ChatGPT, sign you in with OAuth the first time a tool needs your account, and you can disconnect them any time on the Developers page; clients configured by hand can send an API key as a bearer header instead. Step-by-step setup for each app is on the Agents page. The tools mirror the REST API: get_started (works without a key), get_account (the same resource as GET /api/v1/account), request_upload, create_take, get_take, list_takes, get_export_url, generate_exports (the same as POST /api/v1/takes/{id}/exports), and delete_take. Tool calls share your account's rate limit with REST; connecting and listing tools are free. Point an agent at the server before signing in and get_started explains plans and the workflow, so agents can onboard themselves.
# Claude, ChatGPT, and other apps with connectors:
# add https://api.mocai.ai/v1/mcp as a custom connector, then sign in.
# Claude Code (signs in through your browser on first use)
claude mcp add --transport http mocai https://api.mocai.ai/v1/mcp
# Or with an API key instead of signing in
claude mcp add --transport http mocai https://api.mocai.ai/v1/mcp \
--header "Authorization: Bearer mk_live_…"
# Any MCP client (JSON config)
{
"mcpServers": {
"mocai": {
"type": "http",
"url": "https://api.mocai.ai/v1/mcp",
"headers": { "Authorization": "Bearer mk_live_…" }
}
}
}The mocai command does the whole flow for files on your machine: upload, wait for processing, and download the exports. It ships on npm (npx @mocai/cli) and PyPI (uvx mocai) with identical commands. Give it files or folders; options mirror POST /api/v1/takes (--exports, --rigs, --kinds, --no-hands, and more), and --json prints a machine-readable result. mocai mcp runs a local MCP server, so Claude Desktop, Claude Code, or Cursor can process a folder of clips with one tool call; Claude Desktop users can install it as a one-click extension. Sign in with mocai login, or set MOCAI_API_KEY for scripts and CI.
# Sign in once (opens your browser), then process files or folders npx @mocai/cli login npx @mocai/cli process clip.mp4 takes/ --exports fbx,glb --rigs ue5 --out mocap # Same tool on PyPI uvx mocai process clip.mp4 # Local MCP server: agents process files on your machine in one call claude mcp add mocai -- npx -y @mocai/cli mcp
Errors use standard HTTP status codes with a typed body: { "error": { "code", "message" } }. Two-step uploads take files up to 2 GB (inline multipart stays under ~30 MB), and a 403 usage_limit_reached means the monthly footage allowance is spent (check what's left any time with GET /api/v1/account), while 403 billing_past_due means a failed payment is pausing new uploads until it clears. List endpoints paginate with limit and the nextBefore cursor.