STUDIO

Home / Developers

APIMCPWebhooks

Build with Meo Studio

Generate AI videos and images, whole ads and dubbed videos from your own app, scripts or AI agent. A REST API and a remote MCP server for Claude, ChatGPT, Cursor and other agents, on the same credits as the studio, with a budget on every key.

Get an API keyOpenAPI spec
REST APIhttps://www.mymeo.app/v1
MCP serverhttps://www.mymeo.app/mcp
AuthOAuth sign-in or a Bearer API key
BillingYour plan's credits, refunds on failure

1. Get an API key

Sign in, open the account menu (your initial, top right) and choose API & MCP. Create a key and copy it: it starts with meo_live_ and is shown once, with a webhook signing secret. The API works on any active plan and spends that plan's credits, exactly like the studio.

Every key has limits you can change there: a monthly credit budget (5,000 by default), the most one job may cost, which studios it may use, and whether it may approve ads itself. Add "max_credits" to any request to cap that one call. Anything over a limit is refused before it starts, with nothing charged (status 402).

2. Quickstart

Make a 5-second Kling 3.0 video and wait up to 55 seconds for it:

curl https://www.mymeo.app/v1/generations \
  -H "Authorization: Bearer $MEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-3",
    "prompt": "A red vintage sports car drifts around a wet mountain curve at dusk, low tracking shot",
    "params": { "resolution": "720p", "duration": 5, "aspect_ratio": "16:9" },
    "wait_seconds": 55
  }'

If it isn't finished yet you get status queued or processing. Poll GET /v1/generations/{id}?wait=55 or pass a webhook_url. When it completes, outputs[0].url is your file.

// Node 18+ / Deno / Bun
const res = await fetch("https://www.mymeo.app/v1/generations", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.MEO_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
  body: JSON.stringify({ model: "soul-2", prompt: "Editorial portrait in soft window light", params: { aspect_ratio: "4:5" }, wait_seconds: 30 }),
});
const gen = await res.json();
console.log(gen.status, gen.outputs?.[0]?.url);
import os, requests
H = {"Authorization": f"Bearer {os.environ['MEO_API_KEY']}"}
gen = requests.post("https://www.mymeo.app/v1/generations", headers=H, json={
    "model": "seedance-2", "prompt": "A lone surfer at golden hour, slow tracking shot",
    "params": {"resolution": "720p", "duration": 5}}).json()
while gen["status"] in ("queued", "processing"):
    gen = requests.get(f"https://www.mymeo.app/v1/generations/{gen['id']}?wait=55", headers=H).json()
print(gen["status"], gen["outputs"][0]["url"] if gen["outputs"] else gen["error"])

Animate your own photo: upload it first, then pass the returned URL as image_url. Every upload must carry X-Upload-Consent: yes, your confirmation that you own the file or hold every necessary right and consent, including from each person in it (Terms). If you pass your own https links instead of uploads, add "rights_confirmed": true to the generation request. Uploads are screened for resemblance to well-known people, and flagged uploads can only be used after extra consent in the web studio.

curl https://www.mymeo.app/v1/uploads -H "Authorization: Bearer $MEO_API_KEY" -H "X-Upload-Consent: yes" -H "Content-Type: image/jpeg" --data-binary @photo.jpg
# → {"url":"https://www.mymeo.app/uploads/…/abc.jpg"}

3. MCP server for AI assistants

Let Claude, ChatGPT, Cursor or any MCP client make images, videos, ads and dubbed videos for you. The server speaks Streamable HTTP at https://www.mymeo.app/mcp. People connect with OAuth sign-in: the app opens Meo Studio, the person signs in, sets a monthly budget and what it may make, and clicks Allow. We support dynamic client registration, client ID metadata documents and PKCE (S256); discovery is at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server. An API key in the Authorization header works too. Tools: list_models, get_model, estimate_cost, create_generation, get_generation, list_generations, cancel_generation, get_account, make_ad, dub_video, edit_dub_lines, make_faith_video, make_poster, get_task, list_tasks, approve_task, cancel_task, get_prices, list_languages.

Claude Code (then run /mcp to sign in)

claude mcp add --transport http meo https://www.mymeo.app/mcp

Claude Desktop (Settings → Developer → Edit config)

{
  "mcpServers": {
    "meo": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://www.mymeo.app/mcp", "--header", "Authorization: Bearer meo_live_…"]
    }
  }
}

Cursor, Windsurf, VS Code (mcp.json)

{
  "mcpServers": {
    "meo": { "url": "https://www.mymeo.app/mcp", "headers": { "Authorization": "Bearer meo_live_…" } }
  }
}

Old links with the key inside (/mcp/k/…) keep working until 31 January 2027. Switch to sign-in or the header before then.

Then just ask: "Make a 5-second cinematic drone shot of Tokyo at night with Kling 3.0 and give me the link."

4. Whole jobs: ads and dubbing

Besides single pictures and clips, an agent can ask for a finished piece of work. Each one is a task that pauses for approval before it spends real money, then returns every file with stable IDs.

curl https://www.mymeo.app/v1/ads -H "Authorization: Bearer $MEO_API_KEY" -H "Content-Type: application/json"   -d '{"product":"Cold brew protein coffee","story":"A tired office worker gets their energy back","seconds":15,"ratio":"9:16","max_credits":400}'
# → {"id":"task_…","status":"processing",…}
curl https://www.mymeo.app/v1/tasks/task_…?wait=55 -H "Authorization: Bearer $MEO_API_KEY"
# → {"status":"waiting_for_approval","approval":{"what":"Approve the script (3 shots, 15 s)…","script":{…},"estimate":{"credits":222}},…}
curl -X POST https://www.mymeo.app/v1/tasks/task_…/approve -H "Authorization: Bearer $MEO_API_KEY"

5. Endpoints

EndpointWhat it does
GET /v1/accountPlan and credits left
GET /v1/modelsEvery model with modes, settings (params) and credits per quality. Filter with ?kind=video|image
GET /v1/models/{id}One model
POST /v1/estimateCredits a request would cost: {"model","params","video_seconds"}
GET /v1/templatesMotion templates (need image_url) and clip templates (need image_urls). ?kind=motion|clip&search=
POST /v1/generationsStart a generation. Optional Idempotency-Key header, webhook_url, wait_seconds (max 55)
GET /v1/generationsYour generations, newest first. ?limit=&starting_after=
GET /v1/generations/{id}Status and outputs. ?wait=55 long-polls
POST /v1/generations/{id}/cancelCancel and refund
POST /v1/uploadsUpload an image or video (raw body, up to 200 MB). Uploads are deleted after 72 hours
POST /v1/adsMake an ad (a task). See Whole jobs
POST /v1/dubsDub a finished video (a task, Meo Enterprise)
GET /v1/tasksYour tasks, newest first
GET /v1/tasks/{id}Status, what needs approval, preview link, outputs. ?wait=55 waits for a change
POST /v1/tasks/{id}/approveApprove an ad's quote (if the key may approve)
POST /v1/tasks/{id}/linesEdit a dub's translated lines while it waits for approval: {"language","lines"}
POST /v1/tasks/{id}/cancelStop a task
GET /v1/tasks/{id}/subtitles/{lang}.srtSubtitles of a dubbed language
GET /v1/pricesCredits per finished job
GET /v1/languagesLanguages for dubbing and voiceovers
GET /v1/connectionThis key's limits, what it spent this month and its recent activity

Request body for POST /v1/generations: model (video: …; image: z-image-turbo…), prompt, params (keys from the model's params list, such as resolution, duration, aspect_ratio), and optionally image_url, image_urls, video_url + video_seconds, template_id. The mode (text, image or video to video) is picked from the inputs; set mode to override. Statuses: queued, processing, completed, failed, canceled. Download outputs soon: provider links can expire.

6. Webhooks

Pass webhook_url (public https) and we POST the finished generation there as {"type":"generation.completed"|"generation.failed"|"generation.canceled","data":{…}}. Tasks send task.waiting_for_approval, task.processing, task.completed, task.failed and task.canceled with the task as data. Failed deliveries are retried 6 times with backoff. Verify the Meo-Signature: t=…,v1=… header with your key's webhook secret:

import crypto from "node:crypto";
function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

7. Limits and errors

Questions or need higher limits? Contact us.