Welcome to MMAI
MMAI (Magic Motion AI) is a text-to-motion platform that converts natural language descriptions into professional 3D character animations in seconds. This guide covers everything from your first motion to full API integration.
Overview
MMAI uses a state-of-the-art motion diffusion model to generate full-body 3D animations from a single text prompt. Describe any human movement — from a subtle wave to a complex martial arts combo — and the AI produces a ready-to-use animation file within seconds.
Your First Motion
- 1
Open the Dashboard
Navigate to magicmotion.ai/dashboard. The 3D stage will load with a default character ready for animation.
- 2
Enter a prompt
Type a clear, descriptive sentence in the prompt field. Be specific — "person waving hello with both hands enthusiastically" produces better results than "wave".
- 3
Set animation length
Choose between 2 and 10 seconds. Each second costs 1 credit, so a 5-second clip costs 5 credits.
- 4
Click Generate
The model processes your request. The animation previews live in the 3D stage the moment it completes.
- 5
Download your file
Use the Download button to export FBX. From My Motions you can re-download any previous generation at any time.
Understanding Credits
Credits are consumed at 1 credit per second of animation generated. A 5-second clip costs 5 credits; a 10-second clip costs 10. Credits reset monthly and do not roll over.
| Plan | Monthly Credits | API Access |
|---|---|---|
| Starter Magic | 300 | — |
| Creator MagicPopular | 1,000 | ✓ |
| Pro Magic | 10,000 | ✓ |
Timeline Editor
Available on Creator Magic and Pro Magic plans, the Timeline Editor lets you sequence and blend multiple motion clips into a single animation.
Exporting Animations
Generated animations are delivered as FBX files. The animation_url in every API response is a signed URL to download the FBX directly.
FBXUnreal Engine, Unity, Blender, Maya, 3ds Max
Primary output format. Signed URL expires after 1 hour.
GLBWeb (Three.js, Babylon.js), AR/VR, Sketchfab
Available as an additional export from the dashboard.
BVHBlender, MotionBuilder, iClone, Cascadeur
Raw motion capture format. Available as an additional export from the dashboard.
Custom Characters
Bring Your Own Character (BYOC) is available on Creator Magic and above. Import any humanoid character and MMAI's smart retargeting applies generated animations to your skeleton automatically.
Learn by Watching
Step-by-step video guides for every skill level.
Getting Started
MMAI in 5 Minutes
A rapid tour of the dashboard — prompts, parameters, generation, and your first download.
Understanding the Dashboard
Deep-dive into every control: the 3D stage, control panel, credit counter, and more.
Timeline Walkthrough
Timeline Editor Basics
Add clips, set transitions, and preview a blended multi-motion sequence in real time.
Complex Motion Sequences
Build a 30-second showcase by chaining idle, walk, jump, and land animations.
Character Import
Importing from Meshy & Tripo
Export a character from Meshy AI or Tripo, import into MMAI, and apply a motion in minutes.
Blender to MMAI Workflow
Prepare a Blender character for BYOC — correct T-pose, clean armature, and export settings.
API Integration
API Quick Start in JavaScript
Call the generate-motion endpoint, handle the response, and save the animation URL in Node.js.
Async Polling Pattern
Use async_mode to kick off long generations and poll job-status until completion.
Integrate in Minutes
The MMAI REST API lets you generate animations programmatically. API access is available on Creator Magic and Pro Magic plans only.
Get Your API Key
- 1Sign up at magicmotion.ai
- 2Subscribe to Creator Magic or Pro Magic
- 3Go to your Account page → API tab
- 4Reveal and copy your API key
Authentication
All API requests require a Bearer token in the Authorization header.
Authorization: Bearer your_api_key_hereVerify your key at any time without using credits:
curl -X GET https://magicmotion.ai/api/test-api-key \
-H "Authorization: Bearer your_api_key_here"First Request
curl -X POST https://magicmotion.ai/api/generate-motion \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12
}'const generateAnimation = async (prompt, options = {}) => {
const response = await fetch('https://magicmotion.ai/api/generate-motion', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.MAGIC_MOTION_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt,
length: options.length ?? 5,
guidance_scale: options.guidance_scale ?? 4,
...options,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`MMAI API Error: ${error.message}`);
}
return response.json();
};
const result = await generateAnimation('jumping with joy', {
length: 6,
guidance_scale: 10,
});
console.log('Animation URL:', result.animation_url);
console.log('Credits remaining:', result.metadata.credits_remaining);import requests
import os
def generate_animation(prompt, length=5, guidance_scale=4, seed=None):
headers = {
'Authorization': f'Bearer {os.getenv("MAGIC_MOTION_API_KEY")}',
'Content-Type': 'application/json',
}
data = {'prompt': prompt, 'length': length, 'guidance_scale': guidance_scale}
if seed:
data['seed'] = seed
response = requests.post(
'https://magicmotion.ai/api/generate-motion',
headers=headers,
json=data,
)
response.raise_for_status()
return response.json()
result = generate_animation('running in place', length=7, guidance_scale=8)
print(f"Animation URL: {result['animation_url']}")
print(f"Credits remaining: {result['metadata']['credits_remaining']}")Handling Responses
A successful synchronous response (200 OK):
{
"success": true,
"job_id": "run_abc123def456",
"animation_url": "https://storage.supabase.co/...",
"metadata": {
"prompt": "A Person dancing with excitement",
"original_prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12,
"seed": "1234567",
"credits_used": 8,
"credits_remaining": 492
}
}animation_url is a signed URL that expires after 1 hour. Download the file promptly and store it on your own infrastructure.Advanced Usage
Scale your integration with async generation and intelligent polling.
Async Mode
By default the API waits for generation to complete before responding. For longer clips or batch pipelines, pass async_mode: true to receive a job_id immediately (202 Accepted) without holding the connection open.
# Start generation — returns immediately
curl -X POST https://magicmotion.ai/api/generate-motion \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12,
"async_mode": true
}'
# Response (202 Accepted)
# {
# "success": true,
# "job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
# "status": "IN_PROGRESS",
# "message": "Generation started. Poll /api/job-status/{job_id} for updates.",
# "metadata": { ..., "estimated_time_seconds": 48 }
# }Polling for Status
Poll GET /api/job-status/{job_id} until the status is COMPLETED or FAILED.
async function waitForAnimation(jobId, apiKey) {
const POLL_MS = 3000;
const MAX_MS = 10 * 60 * 1000; // 10 minutes
const start = Date.now();
while (Date.now() - start < MAX_MS) {
await new Promise((r) => setTimeout(r, POLL_MS));
const res = await fetch(
`https://magicmotion.ai/api/job-status/${jobId}`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
);
const data = await res.json();
if (data.status === 'COMPLETED') return data.animation_url;
if (data.status === 'FAILED')
throw new Error(`Generation failed: ${data.message}`);
// IN_QUEUE or IN_PROGRESS — keep polling
}
throw new Error('Timed out waiting for animation');
}Rate Limits & Best Practices
| Limit | Value |
|---|---|
| Credit cost | 1 credit per second of animation |
| Max animation length | 10 seconds |
| Max generation time | 10 minutes |
| Creator Magic credits | 1,000 / month |
| Pro Magic credits | 10,000 / month |
- Use async_mode for all production jobs to avoid holding HTTP connections.
- Download and cache animation URLs immediately — signed URLs expire after 1 hour.
- Pin the seed parameter when you need reproducible outputs across runs.
- Check credits_remaining in every response to avoid failed generations.
- On a 408 timeout, retry with async_mode: true and poll the returned job_id.
API Reference
Full parameter reference for every endpoint. Base URL: https://magicmotion.ai
/api/generate-motionGenerate a 3D character animation from a text prompt. Returns the completed animation URL (sync) or a job ID to poll (async).
Request body
| Parameter | Type | Description | Default |
|---|---|---|---|
promptrequired | string | Text description of the animation, e.g. "dancing", "waving hello". | |
length | integer | Duration in seconds. Range: 2–10. Costs 1 credit per second. | 5 |
guidance_scale | integer | How closely the model follows the prompt. Range: 3–20. | 4 |
seed | string | Optional seed for reproducible results. Omit for a random result. | |
async_mode | boolean | If true, returns job_id immediately (202 Accepted) without waiting for generation. | false |
Response — sync (200 OK)
{
"success": true,
"job_id": "run_abc123def456",
"animation_url": "https://storage.supabase.co/...",
"metadata": {
"prompt": "A Person dancing with excitement",
"original_prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12,
"seed": "1234567",
"credits_used": 8,
"credits_remaining": 492
}
}Response — async (202 Accepted)
{
"success": true,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"status": "IN_PROGRESS",
"message": "Generation started. Use the job_id to check status via GET /api/job-status/{job_id}",
"request_id": "abc123",
"metadata": {
"prompt": "A Person dancing with excitement",
"original_prompt": "dancing with excitement",
"length": 8,
"guidance_scale": 12,
"seed": "1234567",
"estimated_time_seconds": 48
}
}/api/job-status/{job_id}Check the status of an async generation job. Poll after calling /generate-motion with async_mode: true.
| Parameter | Type | Description | Default |
|---|---|---|---|
job_idrequired | string | The job_id returned by a previous call to /generate-motion with async_mode: true. |
{
"success": true,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"status": "IN_PROGRESS",
"message": "Job is currently being processed",
"request_id": "def456",
"checked_at": "2024-12-30T18:57:14.005Z"
}{
"success": true,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"status": "COMPLETED",
"animation_url": "https://storage.supabase.co/...",
"request_id": "def456",
"completed_at": "2024-12-30T18:57:14.005Z"
}{
"success": false,
"job_id": "e8f184eb-c082-43de-8963-2ae33eb6006c-u2",
"status": "FAILED",
"message": "Motion generation failed. Please try again with different parameters.",
"request_id": "def456",
"failed_at": "2024-12-30T18:57:14.005Z"
}/api/test-api-keyValidate an API key and retrieve account information without consuming credits.
{
"success": true,
"message": "API key is valid",
"account": {
"user_id": "uuid-here",
"plan": "Creator Magic",
"credits_available": 450,
"features": {
"api_access": true,
"priority_support": true,
"advanced_models": true,
"batch_processing": false
},
"subscription_active": true
},
"endpoints": {
"motion_generation": "/api/generate-motion",
"job_status": "/api/job-status/{job_id}",
"list_generations": "/api/generations",
"get_generation_by_id": "/api/generations/{id}"
}
}/api/generationsRetrieve a paginated, searchable list of all your previous generations.
Query parameters
| Parameter | Type | Description | Default |
|---|---|---|---|
page | integer | 1-based page number. | 1 |
pageSize | integer | Items per page. Range: 1–100. | 24 |
sort | string | newest · oldest · name_asc · name_desc · length_asc · length_desc | newest |
query | string | Searches generation folder name and prompt text. | |
include_download_urls | boolean | If true, each item includes animation_url when available. | false |
curl -X GET "https://magicmotion.ai/api/generations?page=1&pageSize=20&sort=newest&query=dance&include_download_urls=false" \
-H "Authorization: Bearer your_api_key_here"{
"success": true,
"page": 1,
"pageSize": 20,
"sort": "newest",
"total": 128,
"totalPages": 7,
"include_download_urls": false,
"items": [
{
"id": 1234,
"prompts_id": 4567,
"created_at": "2026-02-19T14:32:55.901Z",
"files_parent_folder": "dance_loop_energy",
"seed": 1234567,
"guidance_scale": 8,
"anim_length": 6,
"prompt_texts": ["A Person dancing with high energy"]
}
]
}/api/generations/{id}Retrieve a single generation by its numeric ID, including a signed download URL.
| Parameter | Type | Description | Default |
|---|---|---|---|
idrequired | integer | The numeric id of the generation returned by /api/generations. |
curl -X GET https://magicmotion.ai/api/generations/1234 \
-H "Authorization: Bearer your_api_key_here"{
"success": true,
"generation": {
"id": 1234,
"prompts_id": 4567,
"created_at": "2026-02-19T14:32:55.901Z",
"files_parent_folder": "dance_loop_energy",
"seed": 1234567,
"guidance_scale": 8,
"anim_length": 6,
"prompt_texts": ["A Person dancing with high energy"],
"animation_url": "https://storage.supabase.co/...",
"download_available": true
}
}Error Codes
| Status | Meaning | Resolution |
|---|---|---|
| 400 | Invalid parameters | Check parameter ranges (length 2–10, guidance_scale 3–20). |
| 401 | Invalid or missing API key | Verify your Authorization header. |
| 402 | Insufficient credits | Top up credits or upgrade your plan. |
| 403 | API access not included in plan | Upgrade to Creator Magic or Pro Magic. |
| 408 | Generation timed out | Retry with async_mode: true and poll the returned job_id. |
| 500 | Internal server error | Retry the request. Contact support if persistent. |
Ready to build?
Jump into the dashboard to generate your first motion, or grab your API key and start integrating today.
MMAI Platform Docs · v1.0 · Updated February 2026