Developers
Keyvello API & Agents
Generate faceless short-form videos from your own code, scripts, or AI agents. One REST API, an MCP server for Claude and Cursor, and a CLI — all authenticated with a single key.
Overview
The Keyvello API turns a prompt into a finished vertical video: script, AI images or footage, narration, captions, and a rendered MP4. Everything the web app can do is being exposed here, endpoint by endpoint.
It is built for two kinds of caller. First, AI agents — Claude Code, Claude Desktop, Cursor, or your own chatbot — talking to Keyvello through the MCP server so "make me a 60-second video about the Roman Empire" just works. Second, scripts and backends that want to batch-produce content on a schedule.
Base URL:
https://www.keyvello.com/api/v1Requests and responses are JSON. Every field name is camelCase. Credits are spent from the same balance as the web app, so an API render and a dashboard render are billed identically.
Authentication
Create a key at /dashboard/api-keys. Keys look like kv_live_.... Pass one on every request — the canonical form is a bearer token:
Authorization: Bearer kv_live_xxxxxxxxxxxxxxxxxxxxxxxxThe x-api-key header is also accepted as a shorthand for clients that cannot set Authorization:
x-api-key: kv_live_xxxxxxxxxxxxxxxxxxxxxxxx- Keys are hashed at rest (SHA-256). We store only the hash and a short display prefix, so a database leak never leaks a usable credential.
- The plaintext key is shown exactly once, at creation. Copy it then — it cannot be recovered afterwards, only revoked and replaced.
- A key carries your full account permissions and spends your credits. Keep it server-side: never ship it in a browser bundle, mobile app, or public repo.
- Revoke a key from the dashboard the moment it leaks. Revocation takes effect on the next request.
A missing or invalid key returns 401 with code UNAUTHORIZED.
Quickstart
Three calls: check your balance, queue a video, poll until it is ready.
1. Check your credits
curl https://www.keyvello.com/api/v1/credits \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"credits": 132,
"plan": "starter"
}2. Create a video Shipping shortly
Video creation over the API is shipping shortly — the read endpoints below are live today, and POST /api/v1/videos lands next. The request shape is frozen and documented here so you can build against it now.
curl -X POST https://www.keyvello.com/api/v1/videos \
-H "Authorization: Bearer $KEYVELLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "faceless_shorts",
"input": { "type": "topic", "content": "3 facts about deep sea creatures" },
"durationSeconds": 60,
"voiceId": "openai-cedar",
"imageStyle": "cinematic",
"captions": true
}'The call returns as soon as the job is queued — it does not wait for the render.
{
"id": "8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa",
"status": "queued",
"creditsUsed": 20
}3. Poll until it is ready
curl https://www.keyvello.com/api/v1/videos/8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"id": "8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa",
"status": "ready",
"progress": 100,
"statusMessage": null,
"videoUrl": "https://cdn.keyvello.com/videos/8f14e45f.mp4",
"thumbnailUrl": "https://cdn.keyvello.com/thumbs/8f14e45f.jpg",
"duration": 58.4,
"template": "faceless_shorts",
"title": "3 Facts About Deep Sea Creatures",
"creditsUsed": 20,
"createdAt": "2026-08-04T11:02:44.812Z"
}Poll every 3-5 seconds. A typical render takes 2-5 minutes. When status is ready, call the download endpoint for a file URL (paid plans only).
Endpoints reference
All paths are relative to https://www.keyvello.com/api/v1. Every response — success or error — carries the rate-limit headers described in Rate limits.
Current credit balance and plan for the key's owner.
No parameters.
curl https://www.keyvello.com/api/v1/credits \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"credits": 132,
"plan": "starter"
}plan is one of free, starter, plus, pro.
The narration voice catalog. Served from a static roster — it never calls ElevenLabs, so it is cheap to poll.
No parameters.
curl https://www.keyvello.com/api/v1/voices \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"voices": [
{
"id": "openai-cedar",
"name": "Cedar",
"gender": "male",
"accent": "American",
"provider": "openai",
"requiresPaidPlan": false
},
{
"id": "pNInz6obpgDQGcFmaJgB",
"name": "Adam",
"gender": "male",
"accent": "American",
"provider": "elevenlabs",
"requiresPaidPlan": true
}
]
}Free plans can only generate with provider: "openai". ElevenLabs voices (requiresPaidPlan: true) are rejected downstream with 403 FORBIDDEN — filter on the flag before you offer a voice picker.
The template catalog — every template the API exposes, plus the roadmap of ones that are coming.
No parameters.
curl https://www.keyvello.com/api/v1/templates \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"templates": [
{
"id": "faceless_shorts",
"name": "AI Stories",
"description": "Vertical faceless short (30-90s) built from AI images or gameplay footage, with AI narration and word-level captions. The default Keyvello template.",
"status": "available",
"params": {
"input": "{ type: 'topic' | 'script', content: string } — required. ...",
"durationSeconds": "30 | 60 | 90 (default 30). Ignored when a full script is supplied.",
"voiceId": "Voice id from GET /api/v1/voices (e.g. 'openai-cedar'). ..."
},
"creditsHint": "10-27 credits for 30-90s at base quality"
}
]
}The full catalog — with every parameter — is rendered from the same source of truth in Templates below.
Lists your videos, newest first. Cursor-paginated on createdAt.
| Query param | Type | Description |
|---|---|---|
| limit | integer | Default 20, clamped to 1-100. A non-numeric value falls back to the default rather than erroring. |
| status | string | One of draft, queued, generating, processing, ready, failed. Anything else returns 400. |
| starting_after | ISO 8601 | Cursor. Pass the createdAt of the last item you received to fetch the next page. |
curl "https://www.keyvello.com/api/v1/videos?limit=2&status=ready" \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"videos": [
{
"id": "8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa",
"status": "ready",
"progress": 100,
"statusMessage": null,
"videoUrl": "https://cdn.keyvello.com/videos/8f14e45f.mp4",
"thumbnailUrl": "https://cdn.keyvello.com/thumbs/8f14e45f.jpg",
"duration": 58.4,
"template": "faceless_shorts",
"title": "3 Facts About Deep Sea Creatures",
"creditsUsed": 20,
"createdAt": "2026-08-04T11:02:44.812Z"
}
],
"hasMore": true
}hasMore tells you whether another page exists without a second request.
One video's status. This is the endpoint you poll while a render is running.
| Path param | Type | Description |
|---|---|---|
| id | UUID | The video id returned when it was created. A malformed id is a clean 400. |
curl https://www.keyvello.com/api/v1/videos/8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"id": "8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa",
"status": "generating",
"progress": 45,
"statusMessage": "Rendering scenes",
"videoUrl": null,
"thumbnailUrl": null,
"duration": null,
"template": "faceless_shorts",
"title": "3 Facts About Deep Sea Creatures",
"creditsUsed": 20,
"createdAt": "2026-08-04T11:02:44.812Z"
}A video belonging to another account is indistinguishable from a missing one — both return 404 NOT_FOUND.
Returns a public file URL for a finished video. It does not stream the bytes — you get a link your own downloader can fetch.
| Param | Type | Description |
|---|---|---|
| id | UUID (path) | The video id. |
| aspect | string (query) | Optional: portrait, landscape, or square. Omit for the project's own format. Asking for an aspect that has not been exported yet returns 404 rather than the wrong file. |
curl "https://www.keyvello.com/api/v1/videos/8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa/download?aspect=portrait" \
-H "Authorization: Bearer $KEYVELLO_API_KEY"{
"url": "https://cdn.keyvello.com/videos/8f14e45f-portrait.mp4",
"aspect": "portrait"
}Downloads require a paid plan. On the free plan you get:
HTTP/1.1 402 Payment Required
{
"error": "Upgrade to a paid plan to download videos.",
"code": "PAYMENT_REQUIRED",
"details": { "pricingUrl": "https://www.keyvello.com/pricing" }
}Queues a new video and returns immediately with its id. Credits are deducted when the job starts and refunded automatically if it fails.
This endpoint is shipping shortly. The contract below is frozen — build against it now and it will work the day it lands.
| Body field | Type | Description |
|---|---|---|
| template | string | Template id. Defaults to faceless_shorts. See Templates. |
| ...template params | object | The chosen template's own fields, at the top level of the body. Every template's parameters are listed below and returned by GET /api/v1/templates. |
curl -X POST https://www.keyvello.com/api/v1/videos \
-H "Authorization: Bearer $KEYVELLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "faceless_shorts",
"input": { "type": "topic", "content": "3 facts about deep sea creatures" },
"durationSeconds": 60,
"voiceId": "openai-cedar",
"imageStyle": "cinematic",
"aspectRatio": "portrait",
"qualityTier": "base",
"captions": true
}'{
"id": "8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa",
"status": "queued",
"creditsUsed": 20
}Creation is additionally limited to 5 requests per minute per user — see Rate limits.
Templates
Rendered from the same catalog GET /api/v1/templates serves, so this page can never drift from the API. Credit hints are estimates — the real cost comes back on the create call.
Available
AI Stories
faceless_shortsLiveVertical faceless short (30-90s) built from AI images or gameplay footage, with AI narration and word-level captions. The default Keyvello template.
Credits: 10-27 credits for 30-90s at base quality
| Parameter | Description |
|---|---|
| input | { type: 'topic' | 'script', content: string } — required. 'topic' writes the script for you; 'script' uses your text verbatim. |
| durationSeconds | 30 | 60 | 90 (default 60). Ignored when a full script is supplied. |
| voiceId | Voice id from GET /api/v1/voices (e.g. 'openai-cedar'). Defaults to a free OpenAI voice. |
| imageStyle | natural | anime | cinematic | comic | isometric | watercolor | pixel | neon | oil_painting | line_drawing | graffiti | cubism (default 'natural') |
| backgroundType | ai_images | ai_video | gameplay | stock (default 'ai_images'). ai_video is far more expensive — see creditsHint. |
| aspectRatio | portrait | landscape | square (default 'portrait') |
| qualityTier | base | pro | ultra (default 'base'). pro = 1.5x credits, ultra = 2.5x. |
| captions | boolean — burn word-level captions into the video (default true, +2 credits). |
| music | boolean — mix a background music bed (default false). |
| musicId | Optional music track id; only used when music is true. |
| title | Optional project title. Auto-generated from the input when omitted. |
Fake Texts
fake_textsLiveSimulated text-message conversation (iMessage or Reddit style) that plays over gameplay footage with typing sounds and narration.
Credits: 25 credits (fixed-length ~20s render)
| Parameter | Description |
|---|---|
| conversationType | imessage | reddit (default 'imessage') |
| conversation | The conversation itself — either a plain-text transcript or a structured message object. Required. |
| gameplayType | Background gameplay clip: minecraft | subway_surfers | gta | gta-racing | fortnite | rocket_league. Required. |
AI Podcaster
podcasterLivePodcast-desk talking avatar: a character sits behind a studio mic and lip-syncs your script, with captions burned in.
Credits: 35 credits per 15s block at base quality, 45 at pro — minimum one block (35/45). Duration is estimated from the script at ~2.5 words/second.
| Parameter | Description |
|---|---|
| characterImageUrl | Required. A Keyvello-hosted character image or a pre-made path such as '/characters/podcaster/hiro.png'. External URLs are rejected by the moderation layer. |
| topic | Up to 200 chars. Either topic or customScript is required. |
| customScript | Up to 1000 chars. Drives both the video and the price (cost scales with word count). |
| voiceId | Required. Voice id (e.g. 'openai-cedar'). |
| qualityTier | base | pro (default 'base'). base = Kling Avatar v2, pro = VEED Fabric. |
| addCaptions | boolean (default true) |
| captionSettings | Optional caption style object. |
| addMusic | boolean (default false) |
| musicId | Optional music track id (uuid). |
| musicVolume | Number 0-1 (default 0.15) |
| styleId | Optional visual style preset id (pixar, realistic, anime, movie, comic, ...). |
| hasMask | boolean (default false). True for masked/helmeted characters — uses body motion instead of lip-sync. |
Living Objects
living_objectsLiveAn everyday object comes alive and rants about its existence in first person, lip-synced with an emotion-driven performance.
Credits: 65 / 95 / 140 credits for 15s / 30s / 60s at base quality; 98 / 143 / 210 at pro
| Parameter | Description |
|---|---|
| object | Required, up to 100 chars — the object that talks (e.g. 'a hotel pillow'). |
| emotion | Required: angry | sad | happy | scared | depressed | excited | confused | sarcastic |
| script | Required, 10-1200 chars. Must fit the chosen duration (max ~45 words for 15s, ~85 for 30s, ~160 for 60s). |
| voiceId | Voice id (default 'openai-cedar') |
| duration | 15s | 30s | 60s (default '30s') |
| qualityTier | base | pro (default 'base') |
| addCaptions | boolean (default true) |
| captionSettings | Optional caption style object. |
| addMusic | boolean (default false) |
| musicId | Optional music track id. |
Coming soon
These templates exist in the Keyvello app today but are not exposed through the public API yet. They are listed so you can plan around the roadmap; their parameters are published when each one opens up.
| Template | ID | Credit hint |
|---|---|---|
| Split Screen Side-by-side comparison video pairing your clip with stock or gameplay footage. | split_screen | TBD — not available through the API yet |
| Stick Animation Animated stick-figure story with narration and captions. | stick-animation | TBD — not available through the API yet |
| Kids Stories Illustrated children-friendly story video with a gentle narrator. | kids_story | TBD — not available through the API yet |
| Skeleton Shorts AI-video short built from Grok-generated skeleton clips stitched to a narrated script. | skeleton_shorts | In-app cost: 45 credits for 30s, 85 for 60s (base); 80 / 115 at pro |
| Tier List Animated S-to-F tier ranking video with AI-generated item art and narration. | tier_list | In-app cost: 70 credits |
| This or That Rapid-fire "would you rather" choice video with per-round image pairs. | this_or_that | In-app cost: 25 credits base + 2 per round (30 + 3 per round at pro) |
| Talking Avatar Lip-synced talking head from a still portrait plus your script. | talking_avatar | TBD — cost scales with duration, quality tier and resolution |
| Street Interview Man-on-the-street interview clip with an AI interviewee answering on camera. | street_interview | In-app cost: 170 credits |
| POV Time Travel First-person time-travel sequence moving through eras in one continuous shot. | pov_time_travel | In-app cost: 260 credits |
| Cryptid Vlog Found-footage style vlog hosted by a cryptid (Bigfoot, Mothman, ...). | cryptid_vlog | In-app cost: 170 credits |
| Glass Fruit ASMR Satisfying ASMR clip of glass fruit being sliced in slow motion. | glass_fruit_asmr | In-app cost: 130 credits |
| Food Eating Itself Surreal clip of food with a face eating itself. | food_eating_itself | In-app cost: 35-175 credits by duration and tier, +20 with audio |
| Miniature Cooking ASMR Tiny-kitchen cooking ASMR clip with close-up macro shots. | miniature_cooking_asmr | In-app cost: 130 credits |
| Abandoned Animatronic Creepy found-footage animatronic in an abandoned location. | abandoned_animatronic | In-app cost: 25 credits (base), 45 (pro), 70 (ultra) |
Video lifecycle
draft → queued → generating → processing → ready
↘ failed| Status | Meaning |
|---|---|
| draft | Created but not submitted for rendering. API-created videos normally skip this. |
| queued | Accepted and waiting for a worker. |
| generating | Script, images/footage, and narration are being produced. progress climbs from 0 to 100. |
| processing | Final composition and upload. Not every template passes through this state. |
| ready | Done. videoUrl and thumbnailUrl are populated; call the download endpoint for a file URL. |
| failed | Render failed. statusMessage explains why, and the credits are refunded automatically. |
- Poll
GET /api/v1/videos/{id}every 3-5 seconds. Anything faster just burns your 60 req/min budget. - A typical render finishes in 2-5 minutes. Longer formats and higher quality tiers take longer.
- Failed generations refund their credits automatically — do not build your own refund logic on top.
Errors
Every error uses the same JSON shape. details is optional and its contents depend on the code.
{
"error": "Insufficient credits",
"code": "INSUFFICIENT_CREDITS",
"details": { "required": 20, "available": 6 }
}| Code | HTTP | When it happens |
|---|---|---|
| UNAUTHORIZED | 401 | Missing, malformed, revoked, or unknown API key. |
| VALIDATION_ERROR | 400 | Bad parameter. details echoes the offending value. |
| PAYMENT_REQUIRED | 402 | Downloads require a paid plan. details.pricingUrl points at /pricing. |
| INSUFFICIENT_CREDITS | 402 | Not enough credits for this render. details.required and details.available tell you the gap. |
| FORBIDDEN | 403 | Action not allowed for this account — e.g. a premium (ElevenLabs) voice on a free plan, or downloads paused during a payment dispute. |
| CONTENT_BLOCKED | 403 | Blocked by the moderation layer — the request appears to depict a real, identifiable person. |
| NOT_FOUND | 404 | Video does not exist, or belongs to another account (indistinguishable on purpose). |
| CONFLICT | 409 | A generation is already running for this project. |
| RATE_LIMITED | 429 | Too many requests. Honor the Retry-After header; details carries retryAfter (seconds) and reset (ISO 8601). |
| INTERNAL_ERROR | 500 | Something broke on our side. Safe to retry with backoff. |
Branch on code, not on the human-readable error string — messages get reworded, codes do not.
Rate limits
- 60 requests per minute per API key across all of
/api/v1and/api/mcp. The budget is keyed to the key, not the account — a runaway integration only throttles itself. - Video creation is additionally limited to 5 per minute per user, and that budget is shared with the web app: renders you start in the dashboard count against the same 5.
Every response carries the current state of your budget:
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | Requests allowed in the window (60). |
| X-RateLimit-Remaining | Requests left in the current window. |
| X-RateLimit-Reset | Unix timestamp in seconds when the window resets. |
| Retry-After | Seconds to wait. Sent only on a throttled (429) response — honor it before retrying. |
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785926400
Retry-After: 17
{
"error": "Too many requests. Please try again later.",
"code": "RATE_LIMITED",
"details": { "retryAfter": 17, "reset": "2026-08-04T11:20:00.000Z" }
}MCP (Claude & other agents)
The Model Context Protocol server gives an AI assistant direct access to Keyvello. Ask for "a 60-second faceless video about the Roman Empire" and it picks a voice, queues the render, polls until it is done, and hands back the download link.
Shipping shortly. The MCP server launches alongside API video creation. The setup below is final — save it now and it will work on day one.
Remote server (no install) Shipping shortly
If your client supports remote MCP servers, skip the local process entirely and connect to https://www.keyvello.com/api/mcp with an Authorization header.
claude mcp add --transport http keyvello https://www.keyvello.com/api/mcp \
--header "Authorization: Bearer kv_live_xxx"Local server (npx)
Runs the keyvello-mcp package locally. Requires Node.js 20 or newer. No install step — npx fetches it on demand.
Claude Code
claude mcp add keyvello -e KEYVELLO_API_KEY=kv_live_xxx -- npx -y keyvello-mcpClaude Desktop
Edit claude_desktop_config.json — on macOS ~/Library/Application Support/Claude/claude_desktop_config.json, on Windows %APPDATA%\Claude\claude_desktop_config.json — then restart Claude Desktop.
{
"mcpServers": {
"keyvello": {
"command": "npx",
"args": ["-y", "keyvello-mcp"],
"env": {
"KEYVELLO_API_KEY": "kv_live_xxx"
}
}
}
}Cursor
Edit ~/.cursor/mcp.json (global) or .cursor/mcp.json inside a project.
{
"mcpServers": {
"keyvello": {
"command": "npx",
"args": ["-y", "keyvello-mcp"],
"env": {
"KEYVELLO_API_KEY": "kv_live_xxx"
}
}
}
}Environment variables
| Variable | Required | Default | Purpose |
|---|---|---|---|
| KEYVELLO_API_KEY | yes | — | Your kv_live_... key. Without it the server prints setup instructions and exits 1. |
| KEYVELLO_API_URL | no | https://www.keyvello.com | Point the server at a different Keyvello instance. |
Tools
| Tool | What it does |
|---|---|
| create_video | Queue a new video from a topic or a full script |
| get_video_status | Check render progress (queued → generating → processing → ready) |
| list_videos | List your videos, optionally filtered by status |
| get_download_url | Get a downloadable file URL for a finished video |
| list_voices | Browse available narration voices |
| list_templates | Browse video templates and their parameters |
| get_credits | Check your credit balance and plan |
create_videoreturns as soon as the job is queued — have the agent pollget_video_statusroughly every 15 seconds.- For
faceless_shortspass exactly one oftopicorscript. - Other templates take different fields: call
list_templates, then pass them throughcreate_video'sextraParamsobject — it is shallow-merged over the request body.
CLI
The keyvello CLI wraps the same API for terminals, cron jobs, and shell scripts. Requires Node.js 20 or newer.
Shipping shortly, alongside API video creation.
npm i -g keyvello# 1. Save your API key (create one at /dashboard/api-keys)
keyvello login
# 2. See what a video will cost before spending credits
keyvello create --template faceless_shorts --topic "3 facts about deep sea creatures" --estimate
# 3. Create it and wait for the render
keyvello create --template faceless_shorts --topic "3 facts about deep sea creatures" --duration 60 --wait
# 4. Download it
keyvello download 8f14e45f-ceea-467a-9b3c-2b1f0d6e77aa -o deep-sea.mp4Commands
| Command | What it does |
|---|---|
| keyvello login | Prompts for an API key, validates it, and saves it to ~/.config/keyvello/config.json with owner-only permissions. |
| keyvello create | Creates a video. Options include --template, --topic, --script, --duration, --voice, --style, --aspect, --quality, --no-captions, --music, --params (raw JSON escape hatch), --estimate, and --wait (polls every 5s, 15 min timeout). |
| keyvello status <id> | Status, progress, credits used, and the video/thumbnail URLs for one video. |
| keyvello list | Your recent videos. Options: --limit, --status. |
| keyvello download <id> | Downloads a finished video. Options: --aspect, -o, --output. Requires a paid plan. |
| keyvello voices | Lists voices with provider and whether each needs a paid plan. |
| keyvello templates | Lists template ids, names, availability, and credit hints. |
| keyvello credits | Prints your remaining credits and plan. |
Environment variables
KEYVELLO_API_KEY takes priority over the saved config file; KEYVELLO_API_URL overrides the base URL. With neither a key nor a config file, the CLI tells you to run keyvello login and exits 1.
Ready to build?
Create a key, check your credits, and ship your first integration in a few minutes.