Get started
Four steps from nothing to a finished MP4. Steps 1 and 2 are one-time setup on the website; after that, every video is a single API call.
Open Settings → API keys, create a key and copy it. It starts with chalk_live_sk_v1_ and is shown only once. Put it in your environment:
MANIMOTION_API_KEY=chalk_live_sk_v1_... # used by the SDKs
MANIMOTION_KEY=chalk_live_sk_v1_... # used by the HTTP examples below
MANIMOTION_API=https://api.manimotion.devVideos are uploaded to your own bucket, so you own the files and serve them from your CDN. In Settings → Storage, add a Cloudflare R2, AWS S3 or MinIO bucket. Once it’s saved, every request uses it automatically and you never send bucket credentials again.
Prefer not to save credentials? You can pass them per request instead. Storage shows how.
Skip this and your videos use a good default model. To choose one, pass model with any id your plan includes. To use any model on any plan, connect your own provider key in Settings → AI providers. Details are in Models and Bring your own key.
The quickest way. generate() starts the job, waits while it runs, and returns the finished video. Most videos take 1–5 minutes.
// npm install @manimotion/sdk
import { Manimotion } from "@manimotion/sdk";
const manimotion = new Manimotion(); // reads MANIMOTION_API_KEY
const video = await manimotion.videos.generate({
prompt: "Explain how binary search works",
onProgress: (v) => console.log(v.message),
});
console.log(video.videoUrl);Planning the lesson…
Generating narration…
Designing visuals…
https://cdn.example.com/videos/a1b2c3d4.mp4Using another language, or want to see what the SDK does under the hood? It’s two requests.
The response comes back immediately with a job_id. It is not the video yet.
curl -sS -X POST "$MANIMOTION_API/video/request" \
-H "Content-Type: application/json" \
-H "x-api-key: $MANIMOTION_KEY" \
-d '{
"prompt": "Explain Bayes theorem with a medical testing example",
"style": "auto"
}'{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "queued",
"cached": false,
"video_url": null,
"eta_seconds": 150,
"eta_display": "~2–3 min",
"message": "Queued…",
"tier": "tier2",
"model": "anthropic/claude-haiku-5.5",
"key_source": "platform"
}Call this every 2–3 seconds until status is completed (use video_url) or failed (read error and error_code).
curl -sS "$MANIMOTION_API/video/status/$JOB_ID" \
-H "x-api-key: $MANIMOTION_KEY"{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"video_url": "https://cdn.example.com/videos/a1b2c3d4.mp4",
"error": null,
"error_code": null,
"model": "anthropic/claude-haiku-5.5",
"key_source": "platform",
"cached": false,
"style": "math",
"duration": 54.2,
"phase": "completed",
"message": "Done!",
"eta_seconds": 0,
"tier": "tier2"
}A complete script that creates a video and waits for it:
JOB=$(curl -sS -X POST "$MANIMOTION_API/video/request" \
-H "Content-Type: application/json" \
-H "x-api-key: $MANIMOTION_KEY" \
-d '{"prompt": "Teach the product rule with a visual derivation"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
echo "job=$JOB"
while true; do
BODY=$(curl -sS "$MANIMOTION_API/video/status/$JOB" -H "x-api-key: $MANIMOTION_KEY")
echo "$BODY"
echo "$BODY" | grep -q '"status":"completed"' && break
echo "$BODY" | grep -q '"status":"failed"' && exit 1
sleep 3
donehttps://cdn.example.com/videos/a1b2c3d4.mp4