CapsAI

Caption a video from Claude Code

Published October 2026 · By

This is one real session, start to finish: a 30-second 720×1280 clip captioned from the terminal with the CapsAI connector, using nothing but Claude Code and curl. Tool output is the session's own, trimmed, with signatures and tokens removed. You need Claude Code and a CapsAI account (a new email creates one when you sign in).

1. Add the connector

Register the CapsAI MCP server once, then sign in from inside Claude Code:

terminal
claude mcp add --transport http capsai https://mcp.capsai.co/mcp

In a Claude Code session run /mcp, pick capsai and sign in with your CapsAI email and the code it sends you. That is the whole setup; the claude.ai steps are on the connector page if you also want it in the app.

2. Pick a style

Ask Claude to show the CapsAI caption styles, or call list_caption_styles. Each style comes back with its id, a pro flag and whether it is available on your plan:

list_caption_styles (two of the entries)
{
  "styles": [
    { "id": "poppins",  "name": "Pop Out", "animation": "push-pop", "pro": true,  "available": false, ... },
    { "id": "fp-story", "name": "Story",   "animation": "fade up",  "pro": false, "available": true,  ... },
    ...
  ],
  "defaultStyle": "fp-tommy-soft",
  "positions": ["top", "middle", "bottom"],
  "note": "Any style can be made static with animated=false."
}

The id is what you pass as style. This session used fp-story, a free one. All styles with previews are on the styles page if you would rather look before you choose.

3. Start the job

Tell Claude what to caption and how. It calls caption_video with the file name, the exact size in bytes, the spoken language, the style, and how the captions should sit:

caption_video arguments
{
  "file_name": "dev-sample-sync.mp4",
  "file_size_bytes": 3030229,
  "language": "en",
  "style": "fp-story",
  "animated": true,
  "words_per_caption": 2,
  "position": "bottom"
}

Chat attachments are not passed to this tool, so the result is a project plus a presigned upload you can drive yourself. upload.curl is ready to run; only the file path is missing:

caption_video result (trimmed)
{
  "projectId": "59e7e588…",
  "status": "waiting_for_upload",
  "upload": {
    "method": "PUT",
    "url": "https://storage.googleapis.com/…/1791690098_bc27….mp4?X-Goog-Algorithm=…&X-Goog-Expires=3599&X-Goog-Signature=<redacted>",
    "headers": {
      "Content-Type": "video/mp4",
      "x-goog-content-length-range": "3030229,3030229",
      "x-goog-if-generation-match": "0",
      "x-goog-custom-time": "2026-10-12T03:41:38Z"
    },
    "curl": "curl -X PUT --upload-file <path-to-video> -H 'Content-Type: video/mp4' -H 'x-goog-content-length-range: 3030229,3030229' … '<upload.url>'",
    "maxBytes": 500000000
  },
  "uploadPageUrl": "https://mcp.capsai.co/upload/<token>",
  "settings": { "stylePreset": "fp-story", "animated": true, "wordsPerCaption": 2, "position": "bottom" },
  "editorUrl": "https://capsai.co/editor?projectId=59e7e588…"
}

4. Upload with curl

Run upload.curl with the path filled in. The headers must be sent exactly as given; the signed URL is bound to them, and x-goog-content-length-range pins the upload to the byte count you passed as file_size_bytes:

terminal
curl -X PUT --upload-file public/dev-sample-sync.mp4 \
  -H 'Content-Type: video/mp4' \
  -H 'x-goog-content-length-range: 3030229,3030229' \
  -H 'x-goog-if-generation-match: 0' \
  -H 'x-goog-custom-time: 2026-10-12T03:41:38Z' \
  '<upload.url>'

# HTTP 200, 3030229 bytes uploaded in 1.6 s

If you would rather not script the upload, uploadPageUrl opens a browser page for the same project.

5. Poll until it is done

check_caption_job reports the stage and moves the job along: transcription first, then the render. Each result says when to check again.

check_caption_job, three calls
{ "projectId": "59e7e588…", "status": "transcribing", "next": "Upload received. Check again in 20 seconds." }

{ "projectId": "59e7e588…", "status": "rendering", "progress": 0.9, "next": "Check again in 30 seconds." }

{
  "projectId": "59e7e588…",
  "status": "done",
  "style": "fp-story",
  "downloadUrl": "https://mcp.capsai.co/v/<id>",
  "downloadAvailableUntil": "2026-10-12T03:41:38+00:00",
  "editorUrl": "https://capsai.co/editor?projectId=59e7e588…",
  "next": "Share downloadUrl with the user. It keeps working until downloadAvailableUntil, always serving the latest render."
}

Then download it:

terminal
curl -L -o captioned.mp4 'https://mcp.capsai.co/v/<id>'

The file is the clip with the captions burned in, in the style you asked for. The preview below shows what the styles look like on a video; it is the same engine.

See Your Captions in Action

Try a style and language for your next video.

Vertical video with sample captions
Upload Your Video

What it costs, and the limits

Transcription uses your CapsAI minutes, one per minute of video, so this 30-second clip used half a minute. The first render of a project is included; rendering it again in another style costs the video's length again. get_account reports the balance and the limits for your account. As of 11 October 2026 this one looked like this after the job:

get_account (as of this session)
{
  "minutesLeft": 57.07,
  "plan": "free",
  "maxVideoMinutes": 10,
  "maxUploadSize": "500 MB",
  "maxExportHeight": 1080,
  "buyMinutesUrl": "https://capsai.co/pricing"
}

Run get_account yourself for current numbers; plans change and the connector always reports the ones that apply to you. The connector takes mp4, mov, m4v and webm files. It does not fetch videos from a link, so download first and upload the file. The same facts are kept up to date under styles, minutes and limits.

After the first render

  • Change the look without uploading again. restyle_video takes the same projectId with a new style, animated, words_per_caption or position; settings you leave out keep their value.
  • Fix a word or a timing. editorUrl opens the project in the CapsAI editor.
  • Find it later. list_videos lists your recent projects, newest first, with the same editor links.

Set up the connector

One command in Claude Code, or five clicks on claude.ai. Then ask Claude to caption a video.

Connect CapsAI to Claude

Questions

Can Claude Code upload the video itself?

Yes. The caption_video result includes a presigned PUT (upload.url, the exact upload.headers and an equivalent upload.curl). Claude Code can run that curl with the file path, so nothing has to be dragged into a chat. On claude.ai the same job shows an upload card instead.

Why does the file size have to match exactly?

The presigned upload is bound to the file_size_bytes you passed to caption_video (the x-goog-content-length-range header carries the same number twice). Pass the real byte count, or omit file_size_bytes and let the upload page handle it.

Does the download link expire?

The check_caption_job result gives the link a downloadAvailableUntil time. Run check_caption_job again for a fresh link; it always serves the latest render of that project.

Which styles can I use on the free plan?

list_caption_styles marks each style pro: true or false and available: true or false for your account. Pro styles need a paid plan; the free ones render straight away. Any style can also be rendered as plain static subtitles with animated: false.

Related