AIMoCap

Documentation

Quick start, Studio workflows, API guidance, output formats, pricing, and FAQ.

AIMoCap Docs

API Reference

Request and response fields for AIMoCap async mocap jobs, plus a Python example.

Async workflow

1. Create a job

POST /api/v1/mocap/jobs

Request body:

{
  "title": "tennis-02",
  "sourceFilename": "tennis_02.mp4",
  "sourceDurationSec": 18.4,
  "trimStartSec": 1.2,
  "trimEndSec": 13.8,
  "exportFps": 30,
  "mocapType": "full_body",
  "targetIds": ["default", "unitree_g1"]
}

Field reference:

Field Type Required Notes
title string No Optional job title.
sourceFilename string Yes Original video filename.
sourceDurationSec number No Video duration in seconds; optional.
trimStartSec number No Trim start time in seconds. Defaults to 0.
trimEndSec number No Trim end time in seconds. Defaults to the end of the uploaded video.
exportFps number No 24, 30, 60, or 120. Defaults to 30 when omitted.
mocapType string No full_body or upper_body. Defaults to full_body when omitted.
targetIds string[] Yes One job can request one or more outputs. Currently supports default and unitree_g1.

Common create-time errors:

  • export_fps_invalid
  • mocap_type_invalid
  • api_target_ids_required
  • api_target_unsupported
  • api_source_duration_invalid
  • api_duration_limit_exceeded
  • api_file_size_limit_exceeded
  • api_trim_range_invalid
  • api_concurrency_limit_exceeded
  • api_plan_inactive
  • insufficient_api_v_credits

Response:

{
  "jobId": "mocap_xxx",
  "status": "draft",
  "mocapType": "full_body",
  "uploadUrl": "https://...",
  "sourceKey": "..."
}

2. Upload the source file

Upload the source video to uploadUrl with PUT.

3. Complete upload

POST /api/v1/mocap/jobs/{jobId}/complete-upload

After upload, call this endpoint to submit the job for processing. AIMoCap checks the uploaded file, requested trim range, account limits, and available API v-credit before the mocap algorithm starts.

If the uploaded file or requested clip is outside your API plan limits, the job fails with a clear error code and no v-credit is consumed.

4. Billing

API jobs use API v-credit, separate from normal web credits.

V-credit is consumed after the source video is accepted for processing. The amount is based on the final processed clip duration in seconds, rounded up. If a system-side processing failure happens after v-credit is consumed, AIMoCap refunds the job v-credit once.

5. Poll job status

GET /api/v1/mocap/jobs/{jobId}

Example response fields:

{
  "job": {
    "id": "mocap_xxx",
    "status": "processing",
    "targetIds": ["default", "robot:unitree_g1"],
    "exportFps": 30,
    "mocapType": "full_body"
  },
  "events": [],
  "result": null
}

6. Read the final result

GET /api/v1/mocap/jobs/{jobId}/result

Common result fields:

Field Notes
jobId Job identifier.
status Final job status.
targetIds Requested public output targets, such as default and unitree_g1.
mocapType Capture type used by the job: full_body or upper_body.
previewVideoUrl Preview video URL.
outputs Per-target output array.
createdAt Creation timestamp.
updatedAt Last update timestamp.
completedAt Completion timestamp.

Each item in outputs uses this shape:

Field Notes
targetId default or unitree_g1.
resultType fbx or robot_motion_json.
status Output status.
fbxUrl Present for default.
motionJsonUrl Present for unitree_g1.

Notes:

  • One API job can produce both FBX and Unitree G1 robot motion data.
  • Public integrations should read outputs[] to find each target’s result URL.
  • Custom avatar targets are planned for a later public API update.

Python example

import time
import requests

BASE_URL = "https://aimocap.net"
API_KEY = "YOUR_AIMOCAP_API_KEY"
VIDEO_PATH = "tennis_02.mp4"

headers = {
    "Authorization": f"Bearer {API_KEY}",
}

with open(VIDEO_PATH, "rb") as f:
    video_bytes = f.read()

create_payload = {
    "title": "tennis-02",
    "sourceFilename": "tennis_02.mp4",
    "trimStartSec": 1.2,
    "trimEndSec": 13.8,
    "exportFps": 30,
    "mocapType": "full_body",
    "targetIds": ["default", "unitree_g1"],
}

create_resp = requests.post(
    f"{BASE_URL}/api/v1/mocap/jobs",
    json=create_payload,
    headers=headers,
)
create_resp.raise_for_status()
create_data = create_resp.json()["data"]

upload_resp = requests.put(
    create_data["uploadUrl"],
    data=video_bytes,
    headers={"Content-Type": "video/mp4"},
)
upload_resp.raise_for_status()

complete_resp = requests.post(
    f"{BASE_URL}/api/v1/mocap/jobs/{create_data['jobId']}/complete-upload",
    headers=headers,
)
complete_resp.raise_for_status()

while True:
    job_resp = requests.get(
        f"{BASE_URL}/api/v1/mocap/jobs/{create_data['jobId']}",
        headers=headers,
    )
    job_resp.raise_for_status()
    job_data = job_resp.json()["data"]
    status = job_data["job"]["status"]
    if status in {"completed", "failed", "canceled"}:
        break
    time.sleep(5)

result_resp = requests.get(
    f"{BASE_URL}/api/v1/mocap/jobs/{create_data['jobId']}/result",
    headers=headers,
)
result_resp.raise_for_status()
result = result_resp.json()["data"]

print("preview:", result.get("previewVideoUrl"))
for output in result.get("outputs", []):
    print(output["targetId"], output["resultType"], output.get("fbxUrl") or output.get("motionJsonUrl"))