AnkonAI Developer API
Generate narrated whiteboard explainer videos from a topic, script, PDF, or reference image — programmatically. 8+ languages, vertical or landscape, burned-in captions + SRT. Billed in credits — 1 credit = 1 minute, 2-credit minimum (top-ups from $2/credit).
Use from AI clients (MCP)
OAuth 2.1 is the open standard MCP uses to authenticate to remote servers — it isn't a Claude feature. Any client that implements it can sign in once and call AnkonAI for you. Today that includes the Claude apps (Claude Code, Claude.ai, Claude Desktop), Cursor, ChatGPT, GitHub Copilot, and the Gemini CLI. Connect once, then ask your assistant "make a 60-second video about X" — it calls the AnkonAI API for you and returns the MP4 plus a shareable watch page.
Two ways to authenticate:
- OAuth 2.1 (no key) — sign in with your AnkonAI account in the client; no key to create or paste.
- API key (any client) — send an
X-Ankon-Api-Keyheader; works everywhere, including scripts.
ankonai-claude-code) and does not yet implement Dynamic Client Registration (RFC 7591). So the no-key OAuth flow currently completes only from Claude Code, Claude.ai, and Claude Desktop. Other OAuth-capable clients each expect DCR or their own pre-registered client + redirect URI, so they can't finish AnkonAI's OAuth flow yet — use the API-key snippets below for those. (OAuth 2.1 itself is standard; the limit is purely which client registrations AnkonAI has wired up.)Claude Code, Claude.ai & Claude Desktop — OAuth (no API key)
In Claude Code: Settings → Connectors → Add custom connector, name it AnkonAI, and use this URL. The same connector URL works from the Claude.ai web/desktop app and Claude Desktop — Settings → Connectors there:
https://api.ankonai.com/mcpYou must enter an OAuth Client ID — ankonai-claude-code (leave the secret blank). This is a first-party client registered specifically for the Claude apps, not a general OAuth client. AnkonAI doesn't support automatic (Dynamic) client registration, so leaving the Client ID blank fails with "Automatic client registration isn't supported." Connect → your browser opens the AnkonAI sign-in → approve → done. The client caches the token and refreshes it automatically. No key to create or paste.
Other MCP clients — Cursor, Copilot, Gemini CLI
These clients all speak OAuth 2.1 for remote MCP servers, but each typically expects Dynamic Client Registration or its own pre-registered client/redirect URI, which AnkonAI doesn't expose yet. The API-key path below works in every one of them (and in Claude Code too, if you prefer a CLI-only setup). ChatGPT's Apps & Connectors is the exception — it mandates OAuth 2.1 + DCR and rejects bearer tokens outright, so it can't connect to AnkonAI until DCR is enabled.
Claude Code — API key (CLI)
Create a key at /dashboard/api-keys, then:
claude mcp add --transport http ankon https://api.ankonai.com/mcp \
--header "X-Ankon-Api-Key: anko_…"Cursor
Add to ~/.cursor/mcp.json
{
"mcpServers": {
"ankon": {
"type": "http",
"url": "https://api.ankonai.com/mcp",
"headers": { "X-Ankon-Api-Key": "anko_…" }
}
}
}Codex
Add to ~/.codex/config.toml
[mcp_servers.ankon]
type = "http"
url = "https://api.ankonai.com/mcp"
headers = { "X-Ankon-Api-Key" = "anko_…" }Authentication
Create an API key in your dashboard. Send it in the X-Ankon-Api-Key header on every request. Keys start with anko_ and are shown in full only once at creation — store them securely. (The in-app frontend uses short-lived JWT sessions; API keys are for servers and scripts.)
curl https://api.ankonai.com/api/jobs/ \
-H "X-Ankon-Api-Key: anko_your_key_here" \
-H "Content-Type: application/json"Quick start
Create a job → poll its status until completed → download the MP4 from output_url. A 2-minute video typically renders in a few minutes.
1 · Create a video job
curl -X POST https://api.ankonai.com/api/jobs/ \
-H "X-Ankon-Api-Key: $ANKON_KEY" \
-H "Content-Type: application/json" \
-d '{
"input_type": "topic",
"payload": {
"topic": "how photosynthesis works",
"duration_min": 2,
"language": "en-us",
"orientation": "landscape",
"fact_check": false,
"captions": true
}
}'Response: 201 with a job object — note the id and that status starts as pending. Credits are held immediately and refunded automatically if the render fails.
2 · Poll until complete
# Poll every ~5s until status == "completed"
curl https://api.ankonai.com/api/jobs/<id>/ \
-H "X-Ankon-Api-Key: $ANKON_KEY"3 · Use the result
When status is completed, the job object contains the finished artifacts:
{
"id": "2f8566a0-8e9a-4c66-b73b-498c8b1f5fae",
"status": "completed",
"progress": "Completed",
"output_url": "https://.../final_video_captioned.mp4",
"metadata_url": "https://.../metadata.txt",
"captions_url": "https://.../captions.srt",
"thumbnail_url": "https://.../thumbnail.jpg",
"meta_title": "How Photosynthesis Works",
"meta_description": "..."
}Every finished job returns the MP4, an SRT sidecar (always produced), a metadata.txt with the AI-written title, description, tags, and chapter timestamps, and the AI-lettered thumbnail. Thumbnails and real chapter timestamps are generated for long-form videos; shorts reuse the captioned first frame as a thumbnail. All artifacts are downloadable from their signed URLs.
Python
import requests, time
KEY = "anko_..."
H = {"X-Ankon-Api-Key": KEY, "Content-Type": "application/json"}
job = requests.post("https://api.ankonai.com/api/jobs/",
headers=H, json={
"input_type": "topic",
"payload": {"topic": "how photosynthesis works", "duration_min": 2},
}).json()
while True:
j = requests.get(f"https://api.ankonai.com/api/jobs/{job['id']}/", headers=H).json()
if j["status"] in ("completed", "failed"):
break
time.sleep(5)
print(j["output_url"] if j["status"] == "completed" else j["error"])Node.js
const KEY = "anko_...";
const H = { "X-Ankon-Api-Key": KEY, "Content-Type": "application/json" };
const BASE = "https://api.ankonai.com";
const job = await fetch(BASE + "/api/jobs/", { method: "POST", headers: H,
body: JSON.stringify({ input_type: "topic",
payload: { topic: "how photosynthesis works", duration_min: 2 } }) })
.then(r => r.json());
let j;
do { await new Promise(r => setTimeout(r, 5000));
j = await fetch(BASE + "/api/jobs/" + job.id + "/", { headers: H }).then(r => r.json());
} while (j.status === "pending" || j.status === "running");
console.log(j.status === "completed" ? j.output_url : j.error);Payload reference
POST /api/jobs/ body is { input_type, payload }. input_type is "topic" (AI writes the script) or "script" (you provide the words). All other keys live inside payload:
| Key | Type | Req. | Values / default |
|---|---|---|---|
| topic | string | topic mode | The video idea, e.g. "how photosynthesis works". AI writes the narration. |
| script | string | script mode | The exact narration words. duration_min is derived from word count (~150 wpm). |
| file_keys | string[] | optional | Storage keys from POST /api/media/upload/. Max 3 files, 3 MB combined. The video explains the attached material. |
| duration_min | number | optional | 0.5–10. Default 2. 1 credit = 1 min, 2-credit minimum. |
| language | string | optional | One of the 9 languages below. Default en-us. |
| voice_id | string | optional | From GET /api/voices/?lang=. Omit for the language default. |
| orientation | string | optional | "horizontal" (16:9, default) | "vertical" (9:16, for Shorts/Reels). |
| render_mode | string | optional | "serial" (default — reliable) | "fast" (~3× faster via a parallel backend, +50% credit surcharge; auto-falls back to serial if the fast backend is unavailable). |
| captions | boolean | optional | Burn-in (hardcode) captions into the MP4. Default false (the player shows a soft subtitle track instead). The SRT sidecar is always produced either way. When unset, the pipeline applies a format-aware default: on for shorts, off for long-form. |
| enhance | boolean | optional | Denser pacing — roughly 2× the scenes for 2× the credits (hard caps apply). Default false. |
| fact_check | boolean | optional | Independent fact-check pass (+1 flat credit per video; not refunded on under-delivery). Default false. |
| is_long | boolean|null | optional | Force long/short. Default null → derived from duration (>90 s = long). |
| num_scenes | number|null | optional | Override scene count. Default null → auto from duration. |
| publish_to_gallery | boolean | optional | List in the public gallery once completed. Default false. |
| logo_key | string | optional | Storage key of an uploaded logo (png/jpg/webp, max 1 MB). Adds a corner watermark. |
Unrecognized keys are ignored. Prohibited content (sexual / CSAM, terrorism, instructions-for-harm, self-harm, hate/harassment, illegal) is rejected with 422 before any credits are held.
Sample request & response
Topic mode — full request
POST /api/jobs/
{
"input_type": "topic",
"payload": {
"topic": "how photosynthesis works",
"duration_min": 2,
"language": "en-us",
"voice_id": "af_heart",
"orientation": "landscape",
"captions": true,
"fact_check": false,
"enhance": false,
"render_mode": "serial",
"publish_to_gallery": false
}
}201 Created — response
{
"id": "2f8566a0-8e9a-4c66-b73b-498c8b1f5fae",
"input_type": "topic",
"payload": { "topic": "how photosynthesis works", "duration_min": 2, "language": "en-us", "captions": true },
"status": "pending",
"progress": "Queued",
"credits_held": 2,
"credits_cost": 2,
"real_cost_usd": 0.416,
"output_url": "",
"metadata_url": "",
"captions_url": "",
"thumbnail_url": "",
"error": "",
"attempts": 0,
"is_public": false,
"created_at": "2026-07-12T17:30:00Z",
"started_at": null,
"finished_at": null,
"meta_title": "",
"meta_description": ""
}output_url, metadata_url, captions_url, thumbnail_url, and meta_title/meta_description are populated once the render completes — poll GET /api/jobs/{id}/ until status is completed.
Script mode — minimal request
POST /api/jobs/
{
"input_type": "script",
"payload": {
"script": "The Sun is a star at the center of our solar system...",
"language": "en-us"
}
}In script mode duration_min is derived from the word count (~150 wpm, capped at 10 min); you can still set it explicitly to override.
Attachments (docs & images)
To ground the video in your own material, upload files first and pass their storage keys in payload.file_keys. The video then explains the attached content (PDFs are extracted; images are described by a vision model). Limits: up to 3 files, 3 MB combined, types .pdf .txt .md .png .jpg .jpeg .webp.
# 1. upload (multipart) — returns a storage_key
curl -X POST https://api.ankonai.com/api/media/upload/ \
-H "X-Ankon-Api-Key: $ANKON_KEY" \
-F "file=@diagram.png"
# -> { "storage_key": "uploads/abc123.png", "filename": "diagram.png", "size": 12345 }
# 2. create a video that explains it
curl -X POST https://api.ankonai.com/api/jobs/ \
-H "X-Ankon-Api-Key: $ANKON_KEY" -H "Content-Type: application/json" \
-d '{ "input_type": "topic",
"payload": { "topic": "explain this diagram",
"file_keys": ["uploads/abc123.png"], "duration_min": 2 } }'Languages & voices
9 narration languages. Query GET /api/voices/ (public) for the full list, or GET /api/voices/?lang=hi for one language. Pass the chosen voice_id in the payload.
en-usEnglish (US) — 7 voicesen-gbEnglish (UK) — 4 voicesesSpanish — 3 voicesfrFrench — 1 voicept-brPortuguese (BR) — 3 voicesitItalian — 2 voiceshiHindi — 4 voicesjaJapanese — 4 voiceszhChinese — 4 voicesEndpoints
Base URL: https://api.ankonai.com/api — every path below is relative to it.
/api/jobs/Create + enqueue a video job. Body: { input_type: 'topic'|'script', payload: { topic|script, duration_min, language, orientation, fact_check, captions, ... } }.
/api/jobs/{id}/Status + output URLs for one job.
/api/jobs/List your jobs (filters: q, lang, status, input_type, date_from, date_to; paginated).
/api/jobs/estimate/Pre-flight cost estimate (no auth required). Query: duration_min, fact_check, enhance, fast_render.
/api/voices/Available languages + voices (no auth required).
/api/jobs/{id}/retry/Re-run a failed job (credits re-held; refunded again if it re-fails).
/api/jobs/{id}/visibility/Toggle public/private gallery visibility (completed jobs only).
/api/keys/List your API keys (masked).
/api/keys/Create a key — returns the full key exactly once.
/api/keys/{id}/Revoke a key.
/api/keys/{id}/usage/Aggregated call counts (total / 24h / 7d), top endpoints, and recent activity for a key.
/api/media/upload/Upload an attachment (multipart) — returns a storage_key for payload.file_keys.
The async model
Video generation is asynchronous — a POST /api/jobs/ returns immediately with status: "pending" and the job is rendered on a worker queue. Poll GET /api/jobs/{id}/ every 3–5 seconds; status moves through pending → running → completed (or failed). Use the progress field for a human-readable stage label.
Pricing & credits
1 credit = 1 minute of finished video, with a 2-credit minimum. Credits come from your balance (free credits on signup, or paid packs / plans). Failed renders are refunded automatically — you only pay for a finished video. Top-ups are $2/credit ($10 minimum) on every paid plan — top-up credits never expire but are spendable only while you have an active subscription (they unfreeze when you resubscribe). Subscriptions lower the effective rate — see the pricing page for current packs and plans.
Credit surcharges & multipliers
fact_check— +1 flat credit per video (not refunded on under-delivery).enhance— 2× credits for ~2× the scene density; under-delivery refunds scale at 2× too.render_mode: "fast"— +50% credit surcharge (~3× faster; auto-falls back to serial if the fast backend is unavailable).- Watermark removed automatically for any account with an active subscription — even when the video is charged to rolled-over free credits.
What if my video comes out shorter than I requested?
You are billed on the requested length, not the final runtime — finished videos may run slightly shorter (narration pacing, voice rate). Small variance is accepted: if a finished video lands at ≥ 95% of the requested length, there's no refund. If it falls below that, the un-delivered minutes are automatically credited backto your balance at the same 1-credit-per-minute rate — you never pay for video time you didn't receive. The fact-check surcharge isn't refunded (that work ran regardless of length), and failed renders are refunded in full.
| Request | No refund if delivered ≥ | Example refund |
|---|---|---|
| 2 min | 1:54 | none — the 2-credit minimum leaves nothing to refund (failed renders still refund in full) |
| 3 min | 2:51 | 2:00 → 1 cr (max 1) |
| 5 min | 4:45 | 4:00 → 1 · 3:00 → 2 · 2:00 → 3 |
| 10 min | 9:30 | 9:00 → 1 · 7:00 → 3 · 5:00 → 5 |
Enhance (2× credits) refunds at 2× the rate. Values are rounded; the exact credit-back is recomputed per job from the delivered minutes.
Errors & rate limits
401— missing or invalid API key / JWT.402— insufficient credits (response includesbalanceandcredits_needed).422— request denied by the content safety gate (sexual / CSAM, terrorism, instructions-for-harm, self-harm, hate/harassment, illegal). Rejected before any credits are held.429— rate limited (90/min for mutations; reads poll at a 600/min ceiling). Back off and retry.404/409— unknown job, or action not allowed in the current state.
Questions? Email support@ankonai.com or start with 10 free credits — apply for the beta.