Back to API Keys

API Reference

Generate multi-scene videos and infographics programmatically — or connect AI agents like Claude and Cursor over MCP. The MCP connector, REST endpoints, and API keys all require Ultimate, Creator, or AppSumo Tier 5+.

Base URL: https://us-central1-motionvid-ai.cloudfunctions.net/api
Version: v1

Authentication

All API requests must include your API key in the Authorization header.

Authorization: Bearer mvk_your_api_key_here

Create and manage API keys from your dashboard. Keys start with mvk_ and should be kept secret. The only unauthenticated endpoint is GET /v1/health — it returns {"status":"ok"} for uptime checks.

POST/v1/videos

Create an async video generation job. miltos-5 (default) produces a full multi-scene video with optional narration, music and SFX; miltos-3/4 produce single-scene animations. Returns 202 with a job id immediately — poll GET /v1/videos/{id} or receive a webhook.

Request Body

ParameterTypeDescription
prompt*stringWhat the video should be about
modelstring'miltos-5' (multi-scene, default), 'miltos-4', or 'miltos-3'. See GET /v1/models for capabilities.
enhance_promptbooleanRewrite the prompt with the enhancement model before generating (small extra credit cost). The enhanced prompt is returned on the job.
aspect_ratiostring'16:9', '9:16', '1:1', or '4:5'. Default: 9:16 (multi-scene) / 16:9 (single-scene). Ignored when dimensions is set — dimensions takes precedence.
dimensions{width, height}Exact canvas size in pixels. Takes precedence over aspect_ratio when both are set.
durationnumberTarget duration in seconds. Default 30 (multi-scene) / 15 (single-scene). Max: model-dependent (miltos-5: 120s)
animation_stylestringVisual style pack id — see GET /v1/animation-styles for the live catalog. Omit for the house default; unknown ids are rejected with 400.
background_colorstringHex color for the canvas background. Default: chosen by the model
narrationbooleanAI voiceover narration. miltos-5 only (default true). Passing true on miltos-3/miltos-4 returns 400 — single-scene models have no audio.
narration_voicestringElevenLabs voice id — see GET /v1/voices. Omit to auto-pick a voice fitting the content; unknown ids are rejected with 400.
narration_languagestringNarration language code ('en', 'es', 'fr', 'de', 'pt', 'it', 'nl', 'sv', 'pl', 'ja', 'ko', 'zh', 'hi', 'ar'). Omit to auto-detect from the prompt.
musicbooleanBackground music (miltos-5 only, default true)
sfxbooleanSound effects (miltos-5 only, default false)
ai_videostringAI-generated video clips inside scenes: 'auto' (default — only where the story calls for film), 'on' (mix clips in), 'off' (motion graphics only; cheapest), 'full' (every scene is a generated clip — an all-footage build, the most expensive; builds in one pass unless storyboard review is on). miltos-5 only; non-auto on single-scene models returns 400.
storyboardbooleanai_video 'full' only. Pause the build for human review before clips are generated — first at the cast sheets, then at the storyboard stills. The job goes to status 'awaiting_approval' at each gate until POST /v1/videos/{id}/approve (or the approval card in the MotionVid app) answers it. Default off: the build runs straight through.
planbooleanPlan the story arc before generating scenes (multi-scene, default true)
brand_kit_idstringSaved brand kit id — see GET /v1/brand-kits. Injects the full brand context (logo, colors, fonts, assets). miltos-5 only.
brand_settingsobjectInline brand colors, fonts, name — {primary_color, secondary_color, accent_color, heading_font, body_font, visual_style, brand_name, brand_description}
imagesarrayImages to include: [{url, context?}]. Max 20.
documentsarraySource documents to ground the content: [{url, mime_type, file_name?}]. PDF/CSV/plain text. Max 5.
webhook_urlstringReceives video.completed / video.failed / video.canceled events
renderbooleanAuto-render an MP4 when generation finishes (uses a render from your plan; 4K uses two)

Example

curl -X POST https://us-central1-motionvid-ai.cloudfunctions.net/api/v1/videos \
  -H "Authorization: Bearer mvk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A 60-second explainer on how solar panels work",
    "model": "miltos-5",
    "enhance_prompt": true,
    "duration": 60,
    "aspect_ratio": "16:9",
    "narration": true,
    "music": true,
    "render": true,
    "webhook_url": "https://example.com/webhooks/motionvid"
  }'

Response (202 Accepted)

{
  "id": "vj_abc123",
  "status": "queued",
  "model": "miltos-5",
  "estimated_credits": 21
}

Errors: 402 insufficient_credits (multi-scene needs at least 7 credits to start), 409 too_many_in_flight (max 5 concurrent jobs), 400 invalid_request (e.g. narration on a single-scene model).

GET/v1/videos/{id}

Poll a video job. Multi-scene jobs report live progress as scenes are committed. Statuses: queued → processing → rendering (when render: true) → completed | failed | canceled, with phase giving the finer step inside processing. Multi-scene jobs typically take around 5 minutes, occasionally up to 15.

Response

{
  "id": "vj_abc123",
  "status": "processing",
  "phase": "generating",
  "phase_detail": "Building scenes — 3 of 6 committed. Scenes are built one at a time, roughly 30-90 seconds each, so a steady count between polls is normal, not a stall.",
  "poll_after_seconds": 30,
  "model": "miltos-5",
  "prompt": "A 60-second explainer on how solar panels work",
  "enhanced_prompt": "Create a cinematic 60-second explainer...",
  "project_id": "proj_789",
  "progress": { "scenes_committed": 3, "scenes_planned": 6 },
  "scenes": [
    { "id": "s1", "title": "Hook — the sun's energy", "duration_seconds": 8 },
    { "id": "s2", "title": "Photovoltaic effect", "duration_seconds": 11 }
  ],
  "app_url": "https://motionvid.ai/motion-graphics/proj_789",
  "created_at": "2026-07-07T10:00:00.000Z",
  "updated_at": "2026-07-07T10:03:12.000Z"
}

A job is not stuck just because it looks idle: the AI writes the script and plan before any scene exists, so phase: "planning" with zero committed scenes is normal for the first several minutes. updated_at tracks job-record writes only and stays frozen through generation — poll on phase, not on it.

When completed (and rendered), the response additionally carriescredits_cost and a download_url — a ~15-minute signed link. Re-fetch this endpoint any time for a fresh link. app_url never expires and opens the video in MotionVid, so it is the better link to show a person.

Related: GET /v1/videos lists your jobs (paginated via limit + cursor). GET /v1/videos/{id}/frames returns one base64 JPEG per scene, sampled from the rendered video — a filmstrip you can show without downloading the MP4 (max_frames, default 6, max 12); on a job paused for review it returns the storyboard stills or cast sheets instead. POST /v1/videos/{id}/approve answers a paused review gate on a storyboard: true build (empty body approves and the build continues on the same job id; {"changes": "…"} redoes the board and pauses again — jobs pause at status awaiting_approval and never expire). POST /v1/videos/{id}/render re-renders a completed job, optionally at different dimensions. POST /v1/videos/{id}/cancel cancels a job — queued and paused jobs cancel instantly, in-flight jobs cooperatively (poll until canceled; credits for scenes already generated still apply).

POST/v1/videos/{id}/edit

Change a finished video by describing the change — the same AI agent that powers in-app editing runs your instruction against the job's project, with the original build as conversation context. Edits are surgical: only what the instruction names is regenerated, so a wording or color change costs a fraction of a rebuild. Multi-scene (miltos-5) jobs only; the source job must be in a terminal state (completed, failed, or canceled — asking a failed build to finish is a valid edit).

Request body

ParameterTypeDescription
prompt*stringThe change, in plain words — scope it like a message to an editor ("make scene 2's map show only the EU", "change the palette to warm tones", "shorten it to 20 seconds")
imagesarrayImages for the edit (max 20): { url, context?, role? } — role "import" (default, goes in the video), "style" (look only), or "reference" (model decides)
renderbooleanRe-render the MP4 when the edit lands (uses a render from your plan; 4K uses two). Off by default — or call /render afterwards
webhook_urlstringHTTPS endpoint notified when the edit job finishes

Example

curl -X POST https://api.motionvid.ai/v1/videos/vj_abc123/edit \
  -H "Authorization: Bearer mvk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "The map in scene 2 is wrong — show only the EU member states, colored in our brand blue",
    "render": true
  }'

Returns 202 with a new job id — poll it via GET /v1/videos/{id} exactly like a created job. Credits are charged for what the edit actually regenerates: text and layout changes are cheap, regenerating AI clips bills like generation.

GET/v1/models

Capability matrix for the selectable generation models — use it to pick the model for POST /v1/videos.

Response

{
  "models": [
    {
      "id": "miltos-5",
      "label": "Miltos 5.0",
      "description": "Our most intelligent model yet",
      "multi_scene": true,
      "supports": { "narration": true, "music": true, "sfx": true, "ai_video": true },
      "duration_options": [15, 30, 60, 120],
      "max_duration_seconds": 120,
      "min_credits_to_start": 7,
      "default": true
    },
    {
      "id": "miltos-4",
      "label": "Miltos 4.0",
      "description": "High-quality single-scene infographics",
      "multi_scene": false,
      "supports": { "narration": false, "music": false, "sfx": false, "ai_video": false },
      "duration_options": [15, 30, 60],
      "max_duration_seconds": 60,
      "flat_credit_estimate": 5,
      "default": false
    }
  ]
}
GET/v1/brand-kits

Your saved brand kits for the brand_kit_id parameter. Read-only — create and edit kits in the MotionVid app (Brand Kits page).

Response

{
  "brand_kits": [
    {
      "id": "aBc123",
      "name": "Acme",
      "description": "Bold, developer-first tone",
      "website_url": "https://acme.dev",
      "colors": { "primary": "#6366F1", "secondary": "#8B5CF6", "accent": "#EC4899" },
      "fonts": { "heading": "Inter", "body": "Inter" },
      "has_logo": true
    }
  ]
}
GET/v1/voices

Narration voice catalog for the narration_voice parameter (ElevenLabs premade voices). Omit narration_voice to auto-pick a voice fitting the content.

Response

{
  "voices": [
    {
      "id": "JBFqnCBsd6RMkjVDRZzb",
      "name": "George",
      "description": "Warm resonance that instantly captivates listeners.",
      "gender": "male",
      "accent": "British",
      "age": "middle_aged",
      "use_case": "narration",
      "languages": ["en", "es", "fr", "de"]
    }
  ]
}
GET/v1/animation-styles

Live catalog of visual style packs for the animation_style parameter. Styles are curated in-app, so fetch this rather than hardcoding ids. default_style is what you get when animation_style is omitted; no_style disables style guidance entirely.

Response

{
  "default_style": "taste-default",
  "no_style": "no-style",
  "styles": [
    {
      "id": "editorial-print",
      "name": "Editorial Print",
      "vibe": "Refined magazine layouts, serif headlines",
      "palette": [
        { "label": "Background", "hex": "#F7F4EE" },
        { "label": "Accent", "hex": "#C8442A" }
      ]
    }
  ]
}

MCP — Connect AI Agents

MotionVid ships a hosted Model Context Protocol server, so Claude, Claude Code, Cursor, and any MCP-compatible agent can generate videos for you. It exposes the same capabilities as this REST API: create_video, get_video_status, edit_video, preview_video, approve_video, render_video, cancel_video, list_videos, list_models, list_animation_styles, list_voices, list_brand_kits, enhance_prompt, get_credit_balance, and whoami.

Endpoint: https://motionvid.ai/mcp  (Streamable HTTP)
1. Desktop: Claude → Settings → Connectors
   Web: claude.ai/customize/connectors
2. Add custom connector
3. Name: MotionVid — URL: https://motionvid.ai/mcp
4. Click Connect and sign in with your MotionVid account

Auth: OAuth 2.1 with PKCE + dynamic client registration (the “Connect” flow), or a mvk_ API key as a bearer header. MCP and REST + API keys both need Ultimate, Creator, or AppSumo Tier 5+. Both auth methods spend the same credits — and users with saved BYOK keys pay no credits for the parts a key covers.

POST/v1/infographics/generate

Generate a new infographic animation from a text prompt. Returns synchronously with the generated project. Pass render: true to also kick off a video render in the same call.

Request Body

ParameterTypeDescription
prompt*stringDescription of the infographic to generate
dimensions{width, height}Canvas size in pixels. Default: 1280x720
durationnumberAnimation duration in seconds. Default: 15
animationStylestringAnimation style preset
backgroundColorstringHex color for background. Default: auto-detected
brandSettingsobjectBrand colors, fonts, and visual style
imagesarrayReference images to include in the generation. Each item: {url: string, context?: string}. Max 20.
documentsarraySource documents whose content grounds the generation. Supported mimeType values: application/pdf, text/csv, text/plain. Each item: {url, mimeType, fileName?}. Max 5.
webhookUrlstringURL to receive render completion webhooks
renderbooleanIf true, immediately starts a render after generation. Result is delivered via webhook.

Brand Settings Object

ParameterTypeDescription
primaryColorstringPrimary brand color, e.g. "#6366F1"
secondaryColorstringSecondary brand color
accentColorstringAccent color
headingFontstringGoogle Font name for headings
bodyFontstringGoogle Font name for body text
visualStyle"2d" | "3d"Visual style of the infographic
brandNamestringBrand name to display

Document Object

ParameterTypeDescription
url*stringPublicly accessible HTTPS URL to the document
mimeType*stringMIME type of the document. Must be one of the supported types below.
fileNamestringDisplay name for the file (e.g. "Q3-report.pdf")

Supported mimeType values: application/pdf, text/csv, text/plain

curl -X POST https://us-central1-motionvid-ai.cloudfunctions.net/api/v1/infographics/generate \
  -H "Authorization: Bearer mvk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Monthly revenue growth showing 45% increase",
    "dimensions": {"width": 1920, "height": 1080},
    "duration": 12,
    "brandSettings": {
      "primaryColor": "#6366F1",
      "headingFont": "Poppins",
      "brandName": "Acme Corp"
    }
  }'
POST/v1/infographics/:id/render

Render a generated infographic to video. Returns immediately with a 202 status — the result is delivered via webhook.

ParameterTypeDescription
versionId*stringVersion ID from the generate response
dimensions{width, height}Render resolution. Default: matches generation
fpsnumberFrames per second. Default: 30
webhookUrlstringOverride the default webhook URL for this render
settingsValuesobjectOverride individual settings for this render (colors, text, fonts, etc). Keys must match those in the version's settingsSchema. Merged with stored defaults.
curl -X POST https://us-central1-motionvid-ai.cloudfunctions.net/api/v1/infographics/abc123/render \
  -H "Authorization: Bearer mvk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "versionId": "def456",
    "dimensions": {"width": 1920, "height": 1080},
    "webhookUrl": "https://your-server.com/webhook"
  }'
GET/v1/infographics

List your projects, newest first. Supports cursor-based pagination.

Query Parameters

ParameterTypeDescription
limitnumberResults per page (max 100, default 20)
cursorstringProject ID to paginate from (use nextCursor from previous response)
curl "https://us-central1-motionvid-ai.cloudfunctions.net/api/v1/infographics?limit=20" \
  -H "Authorization: Bearer mvk_your_key"

# Next page:
curl "https://us-central1-motionvid-ai.cloudfunctions.net/api/v1/infographics?limit=20&cursor=abc123" \
  -H "Authorization: Bearer mvk_your_key"
GET/v1/infographics/:id

Get project details and all versions.

curl https://us-central1-motionvid-ai.cloudfunctions.net/api/v1/infographics/abc123 \
  -H "Authorization: Bearer mvk_your_key"
GET/v1/infographics/:id/versions/:versionId

Get version details including status, settings, and the full settings schema for customization.

curl https://us-central1-motionvid-ai.cloudfunctions.net/api/v1/infographics/abc123/versions/def456 \
  -H "Authorization: Bearer mvk_your_key"

Webhooks

When a render completes or fails, we send a POST request to your webhook URL. Configure a default webhook URL when creating your API key, or pass one per-request.

Payload

{
  "event": "render.completed",
  "data": {
    "projectId": "abc123",
    "versionId": "def456",
    "renderId": "render_789",
    "url": "https://storage.googleapis.com/...",
    "dimensions": { "width": 1920, "height": 1080 },
    "fps": 30,
    "duration": 12
  },
  "timestamp": "2024-12-01T10:35:00.000Z"
}

Events

EventDescription
render.completedRender finished. Includes url, dimensions, fps, and duration.
render.failedRender failed. Includes error plus the same projectId/versionId/renderId/dimensions/fps/duration context as completed events. Credits are automatically refunded.
video.completedA /v1/videos job finished. Includes videoId, projectId, creditsCost, and (when rendered) a short-lived downloadUrl — download immediately or re-fetch GET /v1/videos/{id}.
video.failedA /v1/videos job failed. Includes error. Only completed work is charged.
video.canceledA /v1/videos job was canceled via POST /v1/videos/{id}/cancel. Credits for scenes already generated still apply.

Signature Verification

Deliveries are signed two ways with your webhook secret — use whichever is easier. Recommended: the Standard Webhooks headers (webhook-id, webhook-timestamp, webhook-signature) verified with any off-the-shelf library:

import { Webhook } from "standardwebhooks"; // npm i standardwebhooks

const wh = new Webhook(process.env.WEBHOOK_SECRET);
// Throws on bad signature or stale timestamp (replay protection):
const payload = wh.verify(rawBody, {
  "webhook-id": req.headers["webhook-id"],
  "webhook-timestamp": req.headers["webhook-timestamp"],
  "webhook-signature": req.headers["webhook-signature"],
});

Alternatively, the legacy X-MotionVid-Signature header carries a hex HMAC-SHA256 of the raw request body:

import crypto from 'crypto';

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-motionvid-signature'];
  const isValid = verifyWebhook(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET
  );

  if (!isValid) return res.status(401).send('Invalid signature');

  const { event, data } = req.body;
  if (event === 'render.completed') {
    console.log('Video URL:', data.url);
  }

  res.status(200).send('OK');
});

Retry policy: Failed deliveries are retried 3 times with exponential backoff (1s, 5s, 25s). Return a 2xx status to acknowledge receipt.

Error Codes

All errors return a consistent JSON structure:

{
  "error": {
    "code": "insufficient_credits",
    "message": "Monthly generation limit reached.",
    "status": 402
  }
}
StatusCodeDescription
400
invalid_requestMissing or invalid request parameters
401
unauthorizedMissing, invalid, or revoked API key
402
insufficient_creditsMonthly usage limit reached
403
forbiddenPlan does not include API access
404
not_foundResource does not exist or is not yours
409
too_many_in_flightMax 5 video jobs in progress — wait for one to finish
409
not_cancelableThe job already completed, failed, or was canceled
429
rate_limitedToo many requests (see Rate Limits)
500
internal_errorServer error — retry or contact support
503
voices_unavailableVoice catalog temporarily unreachable — retry shortly

Rate Limits

Rate limits are applied per API key. Exceeding them returns a 429 status.

EndpointLimit
All endpoints60 requests / minute
POST /v1/infographics/generate10 requests / minute
POST /v1/videos5 requests / minute + max 5 jobs in flight

Rate limit headers (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset) are included in every response.