Home / Developers
APIMCPWebhooksBuild 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.
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.
- make_ad (
POST /v1/ads): product, story, shape and length in. The Ad Director writes the script and shot plan (17 credits), the task waits with statuswaiting_for_approvaland an exact quote, then Meo makes the pictures, shots and voiceover and cuts the ad. Approve withPOST /v1/tasks/{id}/approveif the key may approve, or send the person thepreview_url. - dub_video (
POST /v1/dubs, Meo Enterprise): a finished video (up to 10 minutes) in, languages out. The translated lines come back for review (edit them withPOST /v1/tasks/{id}/lines), a person approves on the Meo page because the speaker's voice is cloned, then you get the video with and without subtitles, an SRT file and the transcript for each language. - make_faith_video (
POST /v1/faith-videos) and make_poster (POST /v1/posters): for churches on Faith Studio, faith-based only. A faith video plans its shots and paints its pictures, then waits for approval before the video is made; a poster comes back in every size you ask for. - Chain them: pass
from_task(orfrom_generation) todub_videoto dub an ad you just made. GET /v1/priceslists what each job costs. Statuses:processing,waiting_for_approval,completed,failed,canceled.
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
| Endpoint | What it does |
|---|---|
| GET /v1/account | Plan and credits left |
| GET /v1/models | Every model with modes, settings (params) and credits per quality. Filter with ?kind=video|image |
| GET /v1/models/{id} | One model |
| POST /v1/estimate | Credits a request would cost: {"model","params","video_seconds"} |
| GET /v1/templates | Motion templates (need image_url) and clip templates (need image_urls). ?kind=motion|clip&search= |
| POST /v1/generations | Start a generation. Optional Idempotency-Key header, webhook_url, wait_seconds (max 55) |
| GET /v1/generations | Your generations, newest first. ?limit=&starting_after= |
| GET /v1/generations/{id} | Status and outputs. ?wait=55 long-polls |
| POST /v1/generations/{id}/cancel | Cancel and refund |
| POST /v1/uploads | Upload an image or video (raw body, up to 200 MB). Uploads are deleted after 72 hours |
| POST /v1/ads | Make an ad (a task). See Whole jobs |
| POST /v1/dubs | Dub a finished video (a task, Meo Enterprise) |
| GET /v1/tasks | Your tasks, newest first |
| GET /v1/tasks/{id} | Status, what needs approval, preview link, outputs. ?wait=55 waits for a change |
| POST /v1/tasks/{id}/approve | Approve an ad's quote (if the key may approve) |
| POST /v1/tasks/{id}/lines | Edit a dub's translated lines while it waits for approval: {"language","lines"} |
| POST /v1/tasks/{id}/cancel | Stop a task |
| GET /v1/tasks/{id}/subtitles/{lang}.srt | Subtitles of a dubbed language |
| GET /v1/prices | Credits per finished job |
| GET /v1/languages | Languages for dubbing and voiceovers |
| GET /v1/connection | This 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
- 120 requests a minute per key, 20 new generations a minute, and your plan's number of generations running at once (Starter 2, Creator 4, Studio 8).
- Credits are taken when a generation starts and refunded automatically if it fails, is blocked or is cancelled while queued.
- Errors return a status code and
{"error":"message"}: 400 bad input, 401 bad key, 402 no plan or not enough credits, 404 not found, 409 can't cancel, 429 slow down, 503 paused. - Up to 10 keys per account, plus apps connected with sign-in. Revoke a key or disconnect an app any time from the account menu.
- 402 errors from a key's limits carry a
code:over_budget,over_job_limitorover_max_credits. Only a person can buy credits.
Questions or need higher limits? Contact us.