Technical documentation
EditCaption API documentation
Complete reference for the REST API and the MCP server: authentication, endpoints, job statuses, errors, code examples and agency workspace control. For pricing and creating a key, see the API page.
Overview
The EditCaption API always follows the same 3 steps: upload a media file, start a job (one endpoint per tool), then poll the job until it's done and download the result. Costly tools (captions, transcription, audio, voices) use your workspace's credits, at the same rate as in the app, reserved at launch and fully refunded if the job fails; the others are free. The REST API is included in the Pro and Scale plans; the MCP server is included in every plan, and it also runs your agency workspace (brands, posts, scheduling, analytics).
# 1. Upload a video (file or public URL)
curl -X POST https://editcaption.com/api/v1/uploads \
-H "Authorization: Bearer ecap_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/my-video.mp4"}'
# → { "uploadId": "…", "durationSeconds": 53, "expiresIn": 10800 }
# 2. Start a job (here: automatic captions)
curl -X POST https://editcaption.com/api/v1/subtitles \
-H "Authorization: Bearer ecap_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"uploadId": "…"}'
# → { "jobId": "…", "status": "queued", "creditsUsed": 18,
# "creditsRemaining": 9982, "estimatedSeconds": 80 }
# 3. Poll the job until "completed", then download
curl https://editcaption.com/api/v1/jobs/JOB_ID \
-H "Authorization: Bearer ecap_live_YOUR_KEY"
# → { "status": "completed", "result": { "url": "/api/download/…", "filename": "…" } }Authentication
All /api/v1/* routes and the MCP server use the same API key, sent as a standard HTTP header:
Authorization: Bearer ecap_live_...| Key format | ecap_live_… |
| Creation | From My account → Developer (the full key is only shown once, when it's created), or automatically when you authorize an MCP connector (Claude, ChatGPT, Perplexity, see MCP server), in which case it's never shown in clear and can only be used by that connector. |
| Server-side storage | Only a fingerprint (sha256) is stored: the key can never be retrieved in clear after it's created. |
| Revocation | From the dashboard, anytime. A revoked key immediately gets 401 on every endpoint (and the MCP server). |
| Plans | Access depends on the door, not the key: /api/v1 is reserved for the Pro and Scale plans (otherwise 403 plan_required); the MCP server is open to every plan, the free Solo plan included. The same key works for both and uses the workspace's credits. |
Credits & plans
One rule for the app, the MCP and the API: each tool has a fixed credit cost (grid below and in each endpoint), taken from the workspace's wallet (owner), shared by the team. The plan's monthly credits go first (reset every month, no rollover), then topped-up credits (no expiry). A failed job is refunded in full, each pot getting its share back.
| Solo | 300 credits a month (after email verification) · REST API not included · MCP included · 20 requests/min |
| Starter | 4,000 credits a month · REST API not included · MCP included · 60 requests/min |
| Pro | 9,000 credits a month · REST API included · MCP included · 120 requests/min |
| Scale | 20,000 credits a month · REST API included · MCP included · 240 requests/min |
Top-ups (paid plans) from Settings → Workspace: 3,000 credits for €9, 7,000 credits for €19, 15,000 credits for €39. Balance anytime: GET /api/v1/account or the MCP whoami tool.
Limits & file formats
| Max file size | 5 GB, for direct upload as well as URL ingestion. |
| Max length | No specific length limit: only the 5 GB size limit applies. |
| Input video formats | Any common video format (mp4, mov, avi, mkv, webm…). Formats aren't checked at upload: an error only shows up during processing if the file is unreadable. |
| Output video formats | mp4, webm, mov, avi, mkv (convert); mp4 (captions, watermark, trim, voice change, merge). |
| Output audio formats | mp3, wav, aac (extraction); wav (text to speech). |
| File retention | 3 hours after upload or job completion. A result can be downloaded several times during that window (unlike the public website, where the file is deleted after the first download). |
| Rate limit | Per account and per minute, API and MCP combined: Solo 20, Starter 60, Pro 120, Scale 240. Above that: 429 rate_limited with a Retry-After header. Polling every 3 seconds stays under the Pro limit. |
Job statuses & polling
Each job goes through 4 possible statuses, returned by GET /api/v1/jobs/:id:
queued | The job is queued, not yet picked up by a worker. |
processing | The job is running. |
completed | Finished successfully. The response contains result.url and result.filename. |
failed | Failed. The response contains an error field (message). Reserved credits are refunded in full automatically. |
No webhooks: poll
The API doesn't send an HTTP callback when a job completes: poll GET /api/v1/jobs/:id periodically. Every launch response (status: "queued") includes an indicative estimatedSeconds: a good basis for your first wait. In practice, poll every 2 to 5 seconds, no aggressive backoff needed (no rate limit for now), until completed or failed.
Error handling
Every error returns a JSON body shaped like { error: { code, message } } with the matching HTTP status. Possible codes, across all endpoints:
| Status | code | When |
|---|---|---|
| 400 | invalid_request | Missing or invalid parameter (detailed per endpoint below). |
| 401 | unauthorized | Authorization header missing, invalid or revoked key. |
| 402 | insufficient_credits | Insufficient balance. The response adds balance, required, upgradeUrl and, on a paid plan, topupUrl. |
| 403 | plan_required | Plan without REST API access (Pro and Scale only), or account without a workspace. The response adds upgradeUrl. |
| 403 | email_not_verified | Solo plan with an unverified email address (its free credits are locked). The response adds verifyUrl. |
| 429 | rate_limited | Too many requests per minute for your plan. See the Retry-After header. |
| 404 | not_found | Job not found, expired, or owned by another API key. |
| 500 | internal_error | Unexpected server error. Try again; contact us if it persists. |
{
"error": {
"code": "insufficient_credits",
"message": "You have 12 credits left and this action needs 60. Top up here: https://editcaption.com/espace/reglages#credits, or switch to a bigger plan: https://editcaption.com/prix",
"balance": 12,
"required": 60,
"topupUrl": "https://editcaption.com/espace/reglages#credits",
"upgradeUrl": "https://editcaption.com/prix"
}
}Reference
Endpoints
All routes below accept a JSON body (Content-Type: application/json), except /api/v1/uploads which also accepts multipart.
/api/v1/uploadsUpload media
Ingests a video or audio file, either by direct multipart upload ("file" field) or as JSON with a public URL to download. Returns an uploadId that all other endpoints can use for 3 hours. Always the first step before calling a tool.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
file | string | — | Binary file (multipart/form-data). Mutually exclusive with url. |
url | string | — | Public http(s) URL of the media to download (JSON). Mutually exclusive with file. |
Example request
curl -X POST https://editcaption.com/api/v1/uploads \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"url":"https://exemple.com/ma-video.mp4"}'200 response
{
"uploadId": "3f2a9c7e-...",
"durationSeconds": 53,
"expiresIn": 10800
}Possible errors
| 400 invalid_request | Neither file nor url given, invalid/unreachable URL, or file over 5 GB. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
- ·Max size: 5 GB, both for direct upload and URL (the limit is enforced during the download, not just on the Content-Length header).
- ·The media length is measured right away (ffprobe) and returned in the response: it's the basis for the cost of the tools that follow.
/api/v1/subtitles20 credits / minBurned-in automatic captions
Transcribes the audio and burns styled captions right into the video (rendered, not a separate .srt file; see /api/v1/transcribe for that).
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Returned by POST /api/v1/uploads. |
style | string | classique | classique = drop shadow, impact = rectangular background, contour = thick colored outline. Values: classique | impact | contour |
color | string | #FFFFFF | Text color, hex #RRGGBB. |
effectColor | string | #000000 | Color of the shadow (classique), background (impact) or outline (contour). |
position | number | 82 | 0 = top of the screen, 100 = bottom. Note: inverted compared to the slider on editcaption.com. |
size | string | normale | Text size. Values: petite | normale | grande |
captionWords | integer | 4 | Words per caption group shown (ignored if srtContent is given with a fixed split). Values: 1 | 2 | 4 |
srtContent | string | — | Your own .srt content (skips automatic transcription), handy to reuse a transcription you already corrected. |
fontId | string | inter | Display font. Values: inter | montserrat | poppins | fredoka | archivo-black | anton |
karaokeMode | string | none | Karaoke-style word-by-word highlight. Ignored if captionWords = 1. Values: none | couleur | taille | halo |
highlightColor | string | #FFD166 | Color of the active word in karaoke mode. |
shadowIntensity | number | 60 | 0-100. classique style only. |
contourThickness | number | 50 | 0-100. contour style only. |
boxOpacity | number | 75 | 0-100. impact style only. |
Example request
curl -X POST https://editcaption.com/api/v1/subtitles \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-...","style":"impact","color":"#FFFFFF","position":85,"captionWords":2}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 18,
"creditsRemaining": 9982,
"estimatedSeconds": 80
}Possible errors
| 400 invalid_request | uploadId missing, captionWords not in {1,2,4}, or srtContent doesn't contain "-->". |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/transcribe15 credits / minTranscription (text or SRT)
Transcribes a video's audio into a downloadable plain-text file or .srt captions, without burning them into the video.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Returned by POST /api/v1/uploads. |
language | string | auto | ISO 639-1 code (fr, en, es…) or "auto" for automatic detection. See the languages appendix. |
format | string | txt | txt = continuous text with line breaks at sentence ends. srt = timestamped captions. Values: txt | srt |
srtSplitMode | string | phrase | Only with format=srt. "phrase" groups words into full sentences, one per entry. "mot" gives one entry per word, each with its own start and end timecode. Both keep the text as is (punctuation and case): for styled, burned-in captions, see /api/v1/subtitles. Values: "phrase" | "mot" |
Example request
curl -X POST https://editcaption.com/api/v1/transcribe \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-...","format":"srt","srtSplitMode":"mot"}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 14,
"creditsRemaining": 9968,
"estimatedSeconds": 16
}Possible errors
| 400 invalid_request | uploadId missing, invalid format, or invalid srtSplitMode. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/clean-audio10 credits / minAudio clean-up
Removes background noise and clarifies the voice in a video. No settings: fully automatic.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Returned by POST /api/v1/uploads. |
Example request
curl -X POST https://editcaption.com/api/v1/clean-audio \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-..."}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 9,
"creditsRemaining": 9959,
"estimatedSeconds": 80
}Possible errors
| 400 invalid_request | uploadId missing. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/extract-audioFreeAudio extraction
Extracts a video's audio track in the requested format.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Returned by POST /api/v1/uploads. |
format | string | mp3 | Output audio file format. Values: mp3 | wav | aac |
Example request
curl -X POST https://editcaption.com/api/v1/extract-audio \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-...","format":"wav"}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 3,
"creditsRemaining": 9956,
"estimatedSeconds": 16
}Possible errors
| 400 invalid_request | uploadId missing or unsupported format. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/convertFreeFormat conversion
Converts a video to another container format.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Returned by POST /api/v1/uploads. |
format | string | mp4 | Output video format. Values: mp4 | webm | mov | avi | mkv |
Example request
curl -X POST https://editcaption.com/api/v1/convert \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-...","format":"webm"}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 5,
"creditsRemaining": 9951,
"estimatedSeconds": 80
}Possible errors
| 400 invalid_request | uploadId missing or unsupported format. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/mergeFreeMerge videos
Merges at least 2 uploaded videos into one, joined in the order of the given array.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdsrequired | array | — | At least 2 uploadIds, in the order you want them merged. |
Example request
curl -X POST https://editcaption.com/api/v1/merge \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadIds":["3f2a9c7e-...","a11b22cc-..."]}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 10,
"creditsRemaining": 9941,
"estimatedSeconds": 160
}Possible errors
| 400 invalid_request | uploadIds missing, fewer than 2 items, or an item isn't a valid uploadId. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
- ·The result is a new, independent file: the source uploadIds stay valid (and downloadable separately) until they expire after 3h.
/api/v1/watermarkFreeImage or text watermark
Adds a watermark as a repeated tiled pattern ("fixe") or a floating position that moves every 3/5/8 seconds ("flottant"). The watermark can be an uploaded image or text (rendered as a logo automatically).
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Video to watermark, returned by POST /api/v1/uploads. |
watermarkUploadId | string | — | uploadId of an image (uploaded first via /api/v1/uploads). Required if watermarkText is missing. |
watermarkText | string | — | Text to turn into a logo. Required if watermarkUploadId is missing. |
type | string | fixe | fixe = tilted pattern repeated across the frame. flottant = a single instance that moves around. Values: fixe | flottant |
opacity | integer | 80 | Watermark opacity. Values: 10-100 |
interval | integer | 5 | Seconds between two positions. Floating mode only. Values: 3 | 5 | 8 |
Example request
curl -X POST https://editcaption.com/api/v1/watermark \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-...","watermarkText":"@moncompte","type":"flottant","opacity":70}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 5,
"creditsRemaining": 9936,
"estimatedSeconds": 80
}Possible errors
| 400 invalid_request | uploadId missing, neither watermarkUploadId nor watermarkText given, or invalid type. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/trimFreeTrim a segment
Keeps the [start, end] segment of a video, in seconds.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Returned by POST /api/v1/uploads. |
startrequired | number | — | Segment start in seconds, ≥ 0. |
endrequired | number | — | Segment end in seconds, must be > start. |
Example request
curl -X POST https://editcaption.com/api/v1/trim \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-...","start":12.5,"end":45}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 2,
"creditsRemaining": 9934,
"estimatedSeconds": 48
}Possible errors
| 400 invalid_request | uploadId missing, start < 0, or end ≤ start. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
- ·The source video isn't used up by this call: it stays available for other segments until it expires after 3h.
/api/v1/text-to-speech15 credits / 1,000 chars (40 with a cloned voice)Text to speech
Generates an audio file (.wav) from text, with a catalog voice or the account's cloned voice.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
textrequired | string | — | Text to synthesize. |
voice | string | lea | "clone" uses the cloned voice saved on the account that owns the key; error 400 if no voice was saved on editcaption.com. Values: id from GET /api/v1/voices | clone |
Example request
curl -X POST https://editcaption.com/api/v1/text-to-speech \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"text":"Bienvenue dans cette vidéo générée automatiquement.","voice":"thomas"}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 1,
"creditsRemaining": 9933,
"estimatedSeconds": 10
}Possible errors
| 400 invalid_request | text missing, invalid voice, or voice="clone" with no cloned voice saved on the account. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/voice-change25 credits / min (60 with a cloned voice)Voice change
Transcribes a video's speech, then fully replaces its audio track with a new synthetic voice.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uploadIdrequired | string | — | Returned by POST /api/v1/uploads. |
voice | string | lea | Replacement voice. "clone" requires a cloned voice on the account. Values: id from GET /api/v1/voices | clone |
language | string | fr | Language spoken in the source video (ISO 639-1 code), used for the intermediate transcription. |
Example request
curl -X POST https://editcaption.com/api/v1/voice-change \
-H "Authorization: Bearer ecap_live_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{"uploadId":"3f2a9c7e-...","voice":"camille"}'200 response
{
"jobId": "b91c...",
"status": "queued",
"creditsUsed": 12,
"creditsRemaining": 9921,
"estimatedSeconds": 80
}Possible errors
| 400 invalid_request | uploadId missing, invalid voice, voice="clone" with no cloned voice, or no speech detected in the video. |
| 402 insufficient_credits | Credit balance lower than the job cost. The response includes balance and required. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
| 500 internal_error | Unexpected server error. |
/api/v1/jobs/{id}Job status
Returns the state of a job started by one of the 10 tool endpoints. A job is only visible to the API key that created it (another key, even on the same account, gets 404).
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
idrequired | string | — | jobId returned by the tool endpoint (in the URL, not the body). |
Example request
curl -X GET https://editcaption.com/api/v1/jobs/JOB_ID \
-H "Authorization: Bearer ecap_live_VOTRE_CLE"200 response
{
"jobId": "b91c...",
"status": "completed",
"result": {
"url": "/api/download/3f2a9c7e-...",
"filename": "video-sous-titres.mp4"
}
}Possible errors
| 404 not_found | Unknown jobId, expired (> 3h after completion), or owned by another key. |
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
- ·result.url is a relative path: prefix it with https://editcaption.com to get a downloadable link.
- ·The file can be downloaded several times for 3 hours after completion; it isn't a one-time link.
/api/v1/accountCredit balance
Returns the current credit balance (included monthly credits + purchased credits) and the email of the account that owns the API key.
Example request
curl -X GET https://editcaption.com/api/v1/account \
-H "Authorization: Bearer ecap_live_VOTRE_CLE"200 response
{
"balance": 12940,
"purchased": 9940,
"included": 3000,
"includedMonthly": 3000,
"includedRenewsAt": "2026-11-07T09:00:00.000Z",
"email": "toi@exemple.com"
}Possible errors
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
/api/v1/voicesVoice catalog
Lists the voices you can use with text-to-speech and voice-change (100+ voices in 8 languages). Pass a voice id as the voice parameter. Free.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
language | string | — | Filter by language (query string). Values: fr | en | es | pt | de | it | ar | nl |
gender | string | — | Filter by gender (query string). Values: f | m |
lang | string | fr | Language of the returned descriptions. Values: fr | en |
Example request
curl -X GET https://editcaption.com/api/v1/voices \
-H "Authorization: Bearer ecap_live_VOTRE_CLE"200 response
{
"voices": [
{
"id": "lea",
"name": "Léa",
"language": "fr",
"gender": "f",
"description": "Jeune, claire, pétillante",
"usages": [
"Réseaux",
"Dynamique"
]
}
],
"clone": "clone"
}Possible errors
| 401 unauthorized | Authorization header missing, invalid or revoked key. |
- ·"clone" isn't in the list: it's the cloned voice saved on your editcaption.com account.
Complete code examples
A complete upload → job → wait → result flow, in JavaScript and Python. Replace the example URL and API key with yours.
// Node.js 18+ (native fetch) or browser. No npm install needed.
const API_KEY = "ecap_live_YOUR_KEY";
const BASE = "https://editcaption.com";
async function call(path, body) {
const res = await fetch(BASE + path, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? res.statusText);
return data;
}
async function waitForJob(jobId) {
while (true) {
const res = await fetch(`${BASE}/api/v1/jobs/${jobId}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const data = await res.json();
if (data.status === "completed") return data.result;
if (data.status === "failed") throw new Error(data.error);
await new Promise((r) => setTimeout(r, 3000)); // no webhook: poll every 3s
}
}
async function main() {
const upload = await call("/api/v1/uploads", { url: "https://example.com/my-video.mp4" });
const job = await call("/api/v1/subtitles", { uploadId: upload.uploadId, style: "impact" });
const result = await waitForJob(job.jobId);
console.log("Video ready:", BASE + result.url);
}
main();AI agents
MCP server
EditCaption runs a remote MCP server (Streamable HTTP) at https://editcaption.com/api/mcp. Once connected, the video tools below and the agency workspace tools show up right inside your agent, nothing to install.
One-click connection (recommended)
EditCaption implements a full OAuth 2.1 authorization server (RFC 8414 + RFC 9728, Dynamic Client Registration RFC 7591) on top of the MCP server. Claude, ChatGPT and Perplexity's native custom connectors detect it automatically: paste the URL below into their "Add custom connector" screen, sign in to your EditCaption account, click "Authorize". No key to copy: an API key is created and managed for you behind the scenes (visible and revocable in My account → Developer, labeled "MCP connector"). The exact clicks per platform are on the API page.
https://editcaption.com/api/mcpManual alternative (without OAuth)
For an MCP client that doesn't do OAuth discovery (script, headless environment, older config…), create a key from My account → Developer (Pro and Scale plans) and pass it as a static header:
{
"mcpServers": {
"editcaption": {
"url": "https://editcaption.com/api/mcp",
"headers": { "Authorization": "Bearer ecap_live_YOUR_KEY" }
}
}
}Video tools
| upload_media | Fetches a file from a public URL. Returns uploadId + durationSeconds. |
| generate_subtitles | Burned-in automatic captions.20 credits / min. |
| transcribe_video | Transcription to text or SRT.15 credits / min. |
| clean_audio | Audio clean-up.10 credits / min. |
| extract_audio | Audio extraction (mp3/wav/aac).Free. |
| convert_video | Format conversion (mp4/webm/mov/avi/mkv).Free. |
| merge_videos | Merge several videos.Free. |
| add_watermark | Image or text watermark, tiled or floating.Free. |
| trim_video | Trim a [start, end] segment.Free. |
| list_voices | Voice catalog (100+ voices, 8 languages) for text_to_speech and change_voice. Free. |
| text_to_speech | Text to speech.15 credits / 1,000 chars (40 with a cloned voice). |
| change_voice | Replaces a video's voice.25 credits / min (60 with a cloned voice). |
| get_job_status | Job status (queued/processing/completed/failed). |
Example prompts
- "Upload this video https://example.com/interview.mp4 and add impact-style captions with white text."
- "Take this video, trim the first 30 seconds, then convert the result to webm."
- "Extract this video's audio as mp3, then transcribe it in English."
- "Merge these three videos in this order, then add a floating "@myaccount" text watermark."
Every plan
Run your agency workspace with Claude
The same MCP server gives access to your agency workspace: your assistant reads each brand's strategy, creates posts in your calendar, adds media, schedules them and reports on analytics, with exactly the same rules as in the app (per-brand access, client approval, 30-minute lead time, TikTok options).
| Access | Every plan, the free Solo plan included, within the plan's limits (brands, members, storage). A team member only sees their own brands. |
| Cost | Free: managing brands, posts, scheduling and analytics never uses credits. Only costly video tools use them, just like in the app (see Credits & plans). |
| Dates | ISO 8601. Without an offset (2026-10-12T18:00), the time is read in the workspace time zone (whoami tool). |
| Auto-publishing | Video posts, connected accounts, at least 30 minutes ahead. If your client requires approval, the post must be approved by publish time. For TikTok, the assistant must ask you who can see the video (privacyLevel): it never picks it on its own. |
| Media | attach_media accepts a public URL, an uploadId (upload_media) or the jobId of a finished video tool: caption a video, then add the result straight to a post. |
Agency workspace tools
| whoami | ()Signed-in account, workspace, plan, time zone, credit balances (included + purchased), auto-publish platforms, current time. |
| list_brands | ()Brands you can access: id, whether client approval is required, client language, post count, social accounts (id, network, handle). |
| get_brand | (brandId)Everything to write for a brand: strategy, colors, current topics, ideas, to-dos, watchlist, accounts (id, connection, auto-publish available), labels, formats allowed per network. |
| list_posts | (brandId, from?, to?, status?, includeUnscheduled?)Calendar posts over a date range (inclusive) and/or a status; includeUnscheduled adds posts without a date. |
| get_post | (postId)Full post: caption, script, status, media (video versions, cover, images), targeted accounts and publishing state, TikTok options, comments, publication numbers. |
| create_post | (brandId, title, type?, caption?, script?, scheduledAt?, accountIds?, labelIds?, status?)Creates a post (default format VIDEO_VERTICALE, default status A_ECRIRE). An account that doesn't support the format is rejected. |
| update_post | (postId, title?, type?, caption?, script?, scheduledAt?, accountIds?, labelIds?, status?, correctionReason?)Only changes the fields you pass. accountIds replaces the whole list. Changing the date also moves a scheduled publication (refused if published or less than 30 min away). scheduledAt: null removes the date. |
| delete_post | (postId, confirm: true)Permanently deletes a post and its files, after the user confirms. |
| attach_media | (postId, role, url | uploadId | jobId, accountId?, filename?)role: final (published video, new version), cover (replaces), slide (carousel image, appended), rush (raw footage). One source only: public URL, uploadId (upload_media) or jobId of a finished video tool. accountId = account-specific version. |
| add_comment | (postId, text, internal?)Comments on a post. internal (default true) = team note the client never sees; false = visible in their portal. |
| get_publish_options | (postId, accountId)For one account of the post: what blocks or warns before scheduling or publishing, and for TikTok the allowed privacy levels and creator settings. |
| schedule_publication | (postId, accountId, tiktok?)Turns on auto-publishing for the account at the post's date (at least 30 min ahead, video posts, connected accounts). tiktok = { privacyLevel (required, chosen by the user), allowComments, allowDuet, allowStitch, discloseCommercial, yourBrand, brandedContent }. |
| publish_now | (postId, accountId, tiktok?)Publishes on this account right away (replaces a pending schedule). Same TikTok options. |
| cancel_publication | (postId, accountId)Cancels an account's scheduled publication; the post stays in the calendar. |
| mark_published | (postId, accountId, permalink?, published?)For a manual publication: marks the account as published (with the link); published: false undoes it. |
| get_brand_stats | (brandId, range? (24h | 7d | 30d | 1y | all))Analytics of connected accounts: followers, views, likes, posts gained over the period vs the previous period, and per-account numbers. Default 30d. |
| get_account_posts | (accountId, sort? (recent | best | worst), limit? (1-50))Posts of a connected account with their numbers and the account's average views. accountId comes from get_brand_stats. |
| get_today | ()Across all brands: to-dos (failed publications, posts to publish manually or overdue, reports), recent client feedback, this week's calendar. |
Reference
| Statuses (status) | A_ECRIRE to writeA_TOURNER to shootA_MONTER to editA_VALIDER sent to the client for approvalA_CORRIGER changes requested (correctionReason)VALIDE approvedPUBLIE published |
| Formats (type) | VIDEO_VERTICALE 9:16 video (Reel, TikTok, Shorts)VIDEO_HORIZONTALE 16:9 videoCARROUSEL image carouselIMAGE single imageSTORY storyTEXTE text only (LinkedIn, X, Threads…) |
| Two kinds of account id | A brand's accounts (get_brand, list_brands) are used by create_post, update_post, attach_media and publishing. The connected accounts returned by get_brand_stats are only used by get_account_posts. |
| Errors | A refusal comes back as a tool error (isError) with a message saying what to do and giving the link (top up, change plans, verify the email…), in the account's language: the assistant relays it as is. |
Example requests
- "Look at Maison Fleur's strategy and draft 5 posts for next week, one a day at 6 pm, on Instagram and TikTok."
- "Caption this video in the brand's colors, add it to Thursday's post and schedule it on TikTok."
- "Send all of this week's ready posts to my client for approval."
- "Give me a summary of this month's TikTok analytics and tell me which formats work best."
Claude Code & AI agents
To use EditCaption as a tool in Claude Code (or any other MCP client: Claude Desktop, Cursor, Codex…), add a remote HTTP MCP server. The CLI handles OAuth for you (opens your browser, same flow as Claude.ai):
# The CLI opens your browser so you can sign in and authorize access:
# no key to type, same flow as Claude.ai / Claude Desktop.
claude mcp add --transport http editcaption https://editcaption.com/api/mcpWithout a browser (headless, CI…), pass a static key as a header instead:
# Without a browser (headless/CI environment), use a static key
# created from My account → Developer:
claude mcp add --transport http editcaption \
https://editcaption.com/api/mcp \
--header "Authorization: Bearer ecap_live_YOUR_KEY"Exact option syntax may vary with your version (claude mcp add --help). For Claude Desktop or any config-file client, paste the mcpServers block from the MCP server section instead.
Once connected, just ask the agent what you want in plain language (see the examples above): it picks the right MCP tools, uploads the media, starts the job, waits for the result and adds it to your calendar.
OpenAPI / Swagger
The complete OpenAPI 3.1 schema (all REST endpoints, request/response schemas, errors) is available for download. Import it into Postman, Insomnia or the Swagger editor to generate a request collection or a client in the language of your choice.
Download openapi.jsonAppendix: language codes
ISO 639-1 codes accepted by the language parameters (transcription, voice change). Use auto to let the API detect the language automatically.
autoDétection automatiqueafAfrikaanssqAlbanaisamAmhariquearArabehyArménienasAssamaisazAzerbaïdjanaisbaBachkireuBasquebeBiélorussebnBengalibsBosnienbrBretonbgBulgaremyBirmancaCatalanzhChinoishrCroatecsTchèquedaDanoisnlNéerlandaisenAnglaisetEstonienfoFéroïenfiFinnoisfrFrançaisglGalicienkaGéorgiendeAllemandelGrecguGujaratihtCréole haïtienhaHaoussahawHawaïenheHébreuhiHindihuHongroisisIslandaisidIndonésienitItalienjaJaponaisjwJavanaisknKannadakkKazakhkmKhmerkoCoréenloLaotienlaLatinlvLettonlnLingalaltLituanienlbLuxembourgeoismkMacédonienmgMalgachemsMalaismlMalayalammtMaltaismiMaorimrMarathimnMongolneNépalaisnoNorvégiennnNorvégien (Nynorsk)ocOccitanpsPachtofaPersanplPolonaisptPortugaispaPendjabiroRoumainruRussesaSanskritsrSerbesnShonasdSindhisiCingalaisskSlovaqueslSlovènesoSomaliesEspagnolsuSoundanaisswSwahilisvSuédoistlTagalogtgTadjiktaTamoulttTatarteTélougouthThaïboTibétaintrTurctkTurkmèneukUkrainienurOurdouuzOuzbekviVietnamiencyGalloisyiYiddishyoYorubaA question, a specific use case?
contact@editcaption.com