One REST call → a narrated, explorable explainer
The same pipeline behind the Studio — reference-grounded briefing, generation, validation and auto-repair — exposed as a REST API. Send a topic; get back a complete .unstill file your product can play with the open player. No video files, no render farm.
Authentication
Every request carries a Bearer key. Keys are scoped to your account and its credit balance.
Authorization: Bearer usk_YOUR_KEYKeep keys server-side. Never ship them in client bundles — anyone with the key spends your credits.
API keys
Loading your keys…
Generate an explainer
POST /api/v1/generate
| Field | Type | Description |
|---|---|---|
| topic | string | What to explain, in plain English. Required unless brief is sent. |
| dimension | "3d" | "2d" | Cinematic 3D scene (default) or flat animated diagram. |
| visualBrief | string | Optional factual ground truth (shapes, ratios, conventions) the generator must follow. |
| brief | object | A full concept brief (as produced by the Studio) — skips the automatic briefing step. |
| critique | boolean | Run the closed-loop visual accuracy pass (slower, higher fidelity). |
curl https://your-unstill-host/api/v1/generate \
-X POST \
-H "Authorization: Bearer usk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"topic": "how DNS resolves a domain name",
"dimension": "3d"
}' -o dns.unstill.jsonSync generation takes 1–5 minutes — set your client timeout to 300s, or use async mode.
Async jobs & webhooks
Add "async": true and the call returns 202 immediately with a job id. Poll GET /api/v1/jobs/:id — status movesqueued → running → succeeded | failed; on success the job response carries the same fields as a sync response. Optionally pass an https webhookUrl and the full result is POSTed to it on completion.
# 1) submit — returns immediately with a job id
curl https://your-unstill-host/api/v1/generate \
-X POST \
-H "Authorization: Bearer usk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"topic": "how DNS resolves a domain name",
"async": true,
"webhookUrl": "https://your-app.com/hooks/unstill"
}'
# → 202 { "id": "9b2f…", "status": "queued", "poll": "/api/v1/jobs/9b2f…" }
# 2) poll until status is "succeeded" (or wait for the webhook)
curl https://your-unstill-host/api/v1/jobs/9b2f… \
-H "Authorization: Bearer usk_YOUR_KEY"Credits are charged at submission. Job records (and results) are queryable for later retrieval — treat the webhook as a convenience, not the only delivery.
The response
{
"unstill": { "unstill": "1.0", "format": "2.0", "meta": { … }, "three": { … }, "narrative": { … } },
"brief": { "concept": "…", "storyboard": [ … ] },
"validation": { "valid": true, "warnings": [], "qualityWarnings": [] },
"credits": 295
}unstill is the complete playable file. brief is the editorial plan the generator followed (useful for showing chapter lists). validation confirms the file passed the player's structural checks and quality lint.
Credits & limits
API calls draw from the same credit balance as the Studio — a generation costs 5 credits. Check your balance any time:
curl https://your-unstill-host/api/v1/credits \
-H "Authorization: Bearer usk_YOUR_KEY"Up to 5 active keys per account. Need volume pricing or higher throughput? Talk to us.
Errors
| Status | Meaning |
|---|---|
| 400 | Malformed body — send JSON with topic or brief. |
| 401 | Missing/invalid/revoked API key. |
| 402 | Out of credits — top up at /pricing. |
| 422 | The supplied brief is missing required fields. |
| 429 | Rate limit exceeded (default 12 generations/hour per account) — honor the Retry-After header. |
| 502 | The generator couldn't produce a valid file for this topic — retry or simplify the topic. |
Error bodies are { "error": { "code": 402, "message": "…" } }.
Playing .unstill files
A .unstill file is self-contained JSON played by the open player (/player/unstill-player.js, three.js-based). Embed it in your product, or create a hosted share link from the Studio and iframe /v/<slug> — playback is free and unmetered either way.