AnkonAI Developer API
Generate narrated whiteboard explainer videos from a topic, script, or PDF — programmatically. 8+ languages, vertical or landscape, burned-in captions + SRT, from $0.85/min.
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://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://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://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": "..."
}Python
import requests, time
KEY = "anko_..."
H = {"X-Ankon-Api-Key": KEY, "Content-Type": "application/json"}
job = requests.post("https://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://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://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). |
| resolution | string | optional | "1K" only — 2K/4K are ignored (1K is the supported resolution). |
| captions | boolean | optional | Burned-in captions + SRT sidecar. Default false — the player auto-shows the soft subtitle track instead. |
| fact_check | boolean | optional | Independent fact-check pass (+1 flat credit per video). 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, terrorism, instructions-for-harm, hate) 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",
"resolution": "1K",
"captions": true,
"fact_check": false,
"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://ankonai.com/api/media/upload/ \
-H "X-Ankon-Api-Key: $ANKON_KEY" \
-F "[email protected]"
# -> { "storage_key": "uploads/abc123.png", "filename": "diagram.png", "size": 12345 }
# 2. create a video that explains it
curl -X POST https://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
/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, resolution, fact_check.
/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.
A full machine-readable OpenAPI spec (Swagger / Redoc) is available at /api/docs/ and /api/schema/.
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. Effective bulk rate from $0.85/min; see the pricing page for current packs.
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 (harmful / sexual / terrorism / instructions-for-harm content).429— rate limited (90/min for mutations; reads poll at a higher ceiling). Back off and retry.404/409— unknown job, or action not allowed in the current state.
Questions? Email [email protected] or start with 10 free credits — apply for the beta.