Browse the docs
Developers / Docs
Clippie API and MCP docs
The Clippie API makes AI Story videos, Clippie Recast videos, AI images and video, voiceovers and transcripts from your own code, at https://api.clippie.ai/v1. The MCP server at https://api.clippie.ai/mcp gives Claude, ChatGPT, Cursor and other AI clients the same tools. Both spend the same credits and minutes as the app, at the same prices.
Paste it into any AI assistant. The shortcuts open with the prompt typed in; nothing is sent until you press enter.
Quickstart
- Create an API key in Settings, API (paid plans). It starts with
clp_pat_and is shown once. Put it in an environment variable,CLIPPIE_API_KEY, never in code. - Make your first call:
GET /v1/meshows your plan, credits and minutes. - Check the cost: send the create call with
"dryRun": true. Nothing is spent. - Create: the same call without
dryRun, with anIdempotency-Keyheader. It answers202 { job }straight away. - Wait:
GET /v1/jobs/:id?wait=30until the job iscompleted, or pass awebhookUrland getjob.completedposted to you.
curl https://api.clippie.ai/v1/me \
-H "Authorization: Bearer $CLIPPIE_API_KEY"
# => { "user": { ... }, "plan": { ... }, "credits": { "balance": ... }, "minutesLeft": { ... }, "watermark": { ... } }# 1. Create the video. Send the same Idempotency-Key if you retry,
# so a retry returns the first job instead of charging twice.
# Add "dryRun": true to see the exact cost without spending anything.
curl https://api.clippie.ai/v1/videos/ai-story \
-H "Authorization: Bearer $CLIPPIE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5d1c0e2a-7c4f-4a51-9b1e-2f0c6f1d9a77" \
-d '{
"prompt": "The lost library of Alexandria",
"aspectRatio": "9:16",
"durationSeconds": 60,
"webhookUrl": "https://example.com/clippie-webhook"
}'
# => 202 { "job": { "id": "job_...", "status": "queued", ... } }
# 2. Wait for it. ?wait= holds the request open for up to 50 seconds.
curl "https://api.clippie.ai/v1/jobs/job_...?wait=30" \
-H "Authorization: Bearer $CLIPPIE_API_KEY"
# => { "job": { "status": "completed", "output": { "videoUrl": "...", "editorUrl": "..." } } }import { randomUUID } from 'node:crypto';
const API = 'https://api.clippie.ai/v1';
const auth = { Authorization: `Bearer ${process.env.CLIPPIE_API_KEY}` };
const video = { prompt: 'The lost library of Alexandria', aspectRatio: '9:16', durationSeconds: 60 };
// Check the cost first. A dry run spends nothing.
const dry = await fetch(`${API}/videos/ai-story`, {
method: 'POST',
headers: { ...auth, 'Content-Type': 'application/json' },
body: JSON.stringify({ ...video, dryRun: true }),
}).then((res) => res.json());
console.log(dry.estimate); // { credits, minutes, wouldSucceed, reason }
// Make one key per video and reuse it on retries, so you are never charged twice.
const idempotencyKey = randomUUID();
let { job } = await fetch(`${API}/videos/ai-story`, {
method: 'POST',
headers: { ...auth, 'Content-Type': 'application/json', 'Idempotency-Key': idempotencyKey },
body: JSON.stringify(video),
}).then((res) => res.json());
// Long-poll: each call waits up to 30 seconds for the job to change.
while (!['completed', 'failed', 'canceled'].includes(job.status)) {
({ job } = await fetch(`${API}/jobs/${job.id}?wait=30`, { headers: auth }).then((res) => res.json()));
}
console.log(job.status, job.output?.videoUrl, job.output?.editorUrl);import os, uuid, requests
API = "https://api.clippie.ai/v1"
auth = {"Authorization": f"Bearer {os.environ['CLIPPIE_API_KEY']}"}
video = {"prompt": "The lost library of Alexandria", "aspectRatio": "9:16", "durationSeconds": 60}
# Check the cost first. A dry run spends nothing.
dry = requests.post(f"{API}/videos/ai-story", json={**video, "dryRun": True}, headers=auth).json()
print(dry["estimate"])
# Make one key per video and reuse it on retries, so you are never charged twice.
idempotency_key = str(uuid.uuid4())
job = requests.post(
f"{API}/videos/ai-story",
json=video,
headers={**auth, "Idempotency-Key": idempotency_key},
).json()["job"]
# Long-poll: each call waits up to 30 seconds for the job to change.
while job["status"] not in ("completed", "failed", "canceled"):
job = requests.get(f"{API}/jobs/{job['id']}", params={"wait": 30}, headers=auth, timeout=60).json()["job"]
print(job["status"], (job.get("output") or {}).get("videoUrl"))Authentication
Send a bearer token on every call: Authorization: Bearer <token>.
- API keys (
clp_pat_…) are for your code and for MCP clients that send a fixed header. Create them in Settings, API. A key has thereadscope, thecreatescope or both, and expires after 30 days, 90 days (the default) or never. You can have 20. Only a hash is stored, so a lost key cannot be shown again; revoke it and make a new one. - OAuth access tokens (
clp_at_…) are what Claude, ChatGPT, Cursor and VS Code get when you connect them to the MCP server and sign in. They use authorization code with PKCE and dynamic client registration; a401carries aWWW-Authenticateheader that points clients at the sign-in. - The API is part of every paid plan. Without an active subscription, calls answer
403 subscription_required. A suspended account gets403 suspended.
Keep keys in an environment variable or a secret manager. Anyone with a key can spend your credits within its scopes.
Requests and responses
- Base URL:
https://api.clippie.ai/v1. Breaking changes go to/v2; new fields can appear at any time. - POST bodies are JSON (
Content-Type: application/json). GET calls take query parameters. - Lists take
?limit=(up to 100, default 25) and?cursor=, and answer{ data, nextCursor }. PassnextCursorback to get the next page; it isnullon the last one. - Every response has an
X-Request-Id. Include it when you contact support. - The OpenAPI 3.1 spec is at https://api.clippie.ai/v1/openapi.json.
Errors
Errors are { "error": { "type", "message", "param"?, "requestId" } }. Branch on type; the message is written for people.
- 400
- type:
invalid_request- Meaning:
- A field is missing or wrong.
paramnames it.
- 401
- type:
unauthorized- Meaning:
- No key, or the key is wrong, expired or revoked. MCP clients get a
WWW-Authenticatehint to sign in.
- 402
- type:
insufficient_credits- Meaning:
- Not enough credits for this call. Top up or wait for the next period.
- 402
- type:
insufficient_minutes- Meaning:
- Not enough voiceover, caption or export minutes.
- 403
- type:
subscription_required- Meaning:
- The API needs an active paid plan.
- 403
- type:
forbidden_scope- Meaning:
- The key is read-only and the call creates something.
- 403
- type:
suspended- Meaning:
- The account is suspended.
- 403
- type:
forbidden- Meaning:
- This account cannot use the API (yet).
- 404
- type:
not_found- Meaning:
- No such job, file or project on your account.
- 409
- type:
idempotency_conflict- Meaning:
- The Idempotency-Key was used with a different body.
- 409
- type:
too_many_active_jobs- Meaning:
- Too many jobs are running. Wait for one to finish.
- 422
- type:
generation_rejected- Meaning:
- The model refused the input, for example a safety filter.
- 429
- type:
rate_limited- Meaning:
- Too many calls. Wait for
Retry-Afterseconds.
- 500
- type:
internal- Meaning:
- Something broke on our side. Retry with the same Idempotency-Key.
A generation that fails after the 202 is a job failure, not an HTTP error: the job ends failed with an error, and whatever was charged is refunded.
Rate limits
- 120 calls a minute per account, REST and MCP together. Responses carry
X-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Reset. - Over the limit, calls answer
429 rate_limitedwith aRetry-Afterheader. Wait that many seconds. - Create calls also count against the same per-feature limits as the app (below).
- At most 5 jobs run at once per account (3 for AI Story videos). Another one answers
409 too_many_active_jobs.
- Limit bucket:
ai_image
- Limit bucket:
ai_video
- Limit bucket:
ai_video
- Limit bucket:
upscale
- Limit bucket:
voiceover
- Limit bucket:
transcribe
- Limit bucket:
llm
- Limit bucket:
render
- Limit bucket:
render
Idempotency, dry runs and estimates
- Send
Idempotency-Key: <up to 255 chars>on every create call (MCP: theidempotencyKeyargument). A retry with the same key and body within 24 hours returns the first job and charges nothing; the same key with a different body is409 idempotency_conflict. Make one key per thing you want made, for example a UUID, and keep it until the job exists. - Add
"dryRun": trueto any create call to run every check and get{ dryRun: true, estimate }back: the credits and minutes it would take,wouldSucceed, and thereasonwhen it would be refused. Nothing is spent and no job is made. - estimate_cost (
POST /v1/estimate) does the same for any create tool by name, which is handy for agents and for adding up a batch.
Jobs and waiting
Every create call answers 202 { job }. A job goes queued → processing → rendering → completed, failed or canceled.
job.outputholds what was made:videoUrl,imageUrls,audioUrl,captions,editorUrl(open it in the Clippie editor) andlibraryId.job.costis what was actually charged, after any refund:{ credits, minutes: { voiceover, captions, export } }.- Get job with
?wait=30holds the request (up to 50 seconds) until the job changes, so a simple loop never hammers the API. MCP:get_jobwithwaitSeconds. - Cancel job stops a job that has not started. List jobs shows your recent ones.
{
"job": {
"id": "job_abc123",
"object": "job",
"kind": "ai_story_video",
"status": "queued",
"step": null,
"progress": null,
"input": {
"prompt": "The lost library of Alexandria"
},
"output": null,
"cost": {
"credits": 0,
"minutes": {
"voiceover": 1,
"captions": 1,
"export": 1
}
},
"error": null,
"source": "api",
"webhookUrl": null,
"createdAt": "2026-10-24T12:00:00.000Z",
"startedAt": null,
"completedAt": null
}
}Webhooks
Pass webhookUrl (https, public address) on a create call and Clippie POSTs { event, job } to it when the job finishes.
- Events:
job.completedandjob.failed. - Headers:
Clippie-Event,Clippie-Job-IdandClippie-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 of"<t>.<raw body>"with yourwhsec_…signing secret from Settings, API. - Answer with any 2xx within 10 seconds. Failed deliveries are retried after 30 s, 2 m, 10 m, 30 m, 1 h, 2 h, 4 h and 8 h. Redirects are not followed.
- Delivery is at least once: dedupe on
Clippie-Job-Idplus the event. Check the signature against the raw body and reject timestamps older than five minutes.
import { createHmac, timingSafeEqual } from 'node:crypto';
// header: the Clippie-Signature header, "t=<unix>,v1=<hex>"
// rawBody: the request body exactly as received, before JSON parsing
// secret: your whsec_... signing secret from Settings > API
export function verifyClippieWebhook(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(',').map((part) => part.trim().split('=')));
const timestamp = Number(parts.t);
if (!Number.isInteger(timestamp) || !parts.v1) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest();
const given = Buffer.from(parts.v1, 'hex');
return given.length === expected.length && timingSafeEqual(given, expected);
}import hashlib, hmac, time
# header: the Clippie-Signature header, "t=<unix>,v1=<hex>"
# raw_body: the request body bytes exactly as received
# secret: your whsec_... signing secret from Settings > API
def verify_clippie_webhook(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(part.strip().split("=", 1) for part in header.split(",") if "=" in part)
try:
timestamp = int(parts.get("t", ""))
except ValueError:
return False
if abs(time.time() - timestamp) > tolerance:
return False
signed = f"{timestamp}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))MCP server
The MCP server is at https://api.clippie.ai/mcp (Streamable HTTP). It offers every endpoint below as a tool, with the same scopes, prices and errors. Claude, ChatGPT, Cursor and VS Code sign in with OAuth when you add the URL; clients that send a fixed header can use an API key instead.
Install
https://api.clippie.ai/mcpclaude mcp add --transport http clippie https://api.clippie.ai/mcp{
"mcpServers": {
"clippie": {
"url": "https://api.clippie.ai/mcp"
}
}
}{
"servers": {
"clippie": {
"type": "http",
"url": "https://api.clippie.ai/mcp"
}
}
}https://api.clippie.ai/mcpclaude mcp add --transport http clippie https://api.clippie.ai/mcp \
--header "Authorization: Bearer clp_pat_..."
# Cursor mcp.json
{
"mcpServers": {
"clippie": {
"url": "https://api.clippie.ai/mcp",
"headers": { "Authorization": "Bearer clp_pat_..." }
}
}
}
# VS Code .vscode/mcp.json
{
"inputs": [
{ "type": "promptString", "id": "clippie-key", "description": "Clippie API key", "password": true }
],
"servers": {
"clippie": {
"type": "http",
"url": "https://api.clippie.ai/mcp",
"headers": { "Authorization": "Bearer ${input:clippie-key}" }
}
}
}
# Any client
curl https://api.clippie.ai/mcp \
-H "Authorization: Bearer $CLIPPIE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Server instructions
What the server tells your AI when it connects:
Clippie makes short-form videos, images and audio for the signed-in user, from their own credits and minutes. Call get_account first. Before any tool that spends (its description says so), run estimate_cost or the tool with dryRun: true and confirm the cost with the user. Give every create call an idempotencyKey and reuse it if you retry, so a retry never charges twice. Create tools return a job: use get_job with waitSeconds (up to 50) instead of polling in a tight loop, and never report a job as finished before its status is completed. Finished jobs include links to the media and to the Clippie editor. Lists take limit and cursor; catalogs are also resources under clippie://catalog/.
Resources
Catalogs your client can attach to the conversation without a tool call.
clippie://account- Title:
- Account
- What it is:
- Your plan, credit balance and minutes left.
clippie://catalog/voices- Title:
- Voices
- What it is:
- Narration voices (first 100; list_voices searches them all).
clippie://catalog/caption-styles- Title:
- Caption styles
- What it is:
- Burned-in caption styles.
clippie://catalog/art-styles- Title:
- Art styles
- What it is:
- Looks for AI Story videos.
clippie://catalog/story-templates- Title:
- Story templates
- What it is:
- Ready-made AI Story formats.
clippie://catalog/motion-templates- Title:
- Motion templates
- What it is:
- Trending motion clips for Clippie Recast (first 100).
clippie://catalog/models- Title:
- AI models
- What it is:
- AI Studio video and image models with credit prices.
clippie://catalog/music- Title:
- Music
- What it is:
- Background music tracks (first 100).
clippie://jobs/{id}- Title:
- Job
- What it is:
- One API job: status, output and cost.
Prompts
Guided workflows you can pick in your client. Each one checks the cost and asks you before it spends.
make_short_video- What it does:
- Topic → script → voice and style → cost check → create → wait for the finished video.
- Arguments:
topic,style(optional),length(optional)
recast_me- What it does:
- Put a photo of you into a trending motion clip with Clippie Recast.
- Arguments:
photoUrl(optional),template(optional)
batch_shorts- What it does:
- Several short videos from a list of topics, with one cost check for the whole batch.
- Arguments:
topics,style(optional)
Endpoints
Templates
Finished, narrated videos built and rendered on the server.
- REST:
POST /v1/videos/ai-story- MCP tool:
create_ai_story_video- Cost:
- Spends
- REST:
POST /v1/videos/:projectId/render- MCP tool:
render_video- Cost:
- Spends
AI tools
Single generations: images, AI video, Recast, upscales, speech, transcripts and scripts.
- REST:
POST /v1/ai/images- MCP tool:
generate_image- Cost:
- Spends
- REST:
POST /v1/ai/videos- MCP tool:
generate_video- Cost:
- Spends
- REST:
POST /v1/ai/recast- MCP tool:
recast_video- Cost:
- Spends
- REST:
POST /v1/ai/upscale- MCP tool:
upscale- Cost:
- Spends
- REST:
POST /v1/ai/speech- MCP tool:
text_to_speech- Cost:
- Spends
- REST:
POST /v1/ai/transcribe- MCP tool:
transcribe- Cost:
- Spends
- REST:
POST /v1/ai/scripts- MCP tool:
write_script- Cost:
- Free
Resources
Catalogs to pick from: voices, styles, templates, models, music and footage.
- REST:
GET /v1/voices- MCP tool:
list_voices- Cost:
- Free
- REST:
GET /v1/caption-styles- MCP tool:
list_caption_styles- Cost:
- Free
- REST:
GET /v1/art-styles- MCP tool:
list_art_styles- Cost:
- Free
- REST:
GET /v1/story-templates- MCP tool:
list_story_templates- Cost:
- Free
- REST:
GET /v1/motion-templates- MCP tool:
list_motion_templates- Cost:
- Free
- REST:
GET /v1/models- MCP tool:
list_models- Cost:
- Free
- REST:
GET /v1/background-videos- MCP tool:
list_background_videos- Cost:
- Free
- REST:
GET /v1/music- MCP tool:
list_music- Cost:
- Free
- REST:
GET /v1/sound-effects- MCP tool:
list_sound_effects- Cost:
- Free
- REST:
GET /v1/languages- MCP tool:
list_languages- Cost:
- Free
Jobs & files
Wait for and cancel jobs, check costs, and upload the files other calls use.
- REST:
GET /v1/jobs- MCP tool:
list_jobs- Cost:
- Free
- REST:
GET /v1/jobs/:id- MCP tool:
get_job- Cost:
- Free
- REST:
POST /v1/jobs/:id/cancel- MCP tool:
cancel_job- Cost:
- Free
- REST:
POST /v1/estimate- MCP tool:
estimate_cost- Cost:
- Free
- REST:
POST /v1/files- MCP tool:
upload_file- Cost:
- Free
- REST:
POST /v1/files/uploads- MCP tool:
- REST only
- Cost:
- Free
- REST:
POST /v1/files/uploads/complete- MCP tool:
- REST only
- Cost:
- Free
- REST:
GET /v1/files/:id- MCP tool:
get_file- Cost:
- Free
Account
Your plan, balances and where they went.
- REST:
GET /v1/me- MCP tool:
get_account- Cost:
- Free
- REST:
GET /v1/credits/history- MCP tool:
list_credit_history- Cost:
- Free
