2026-11-03 · 4 min read

Captions for Vimeo Videos with an API

Vimeo's own subtitle track works well on vimeo.com, but it depends on the viewer turning it on, and it does not always survive embeds, downloads or reposts. When the words need to be part of the video itself, burning them in is the reliable option.

The caption.sh API takes a video file at a URL and returns the same video with captions rendered into the frames. Upload the captioned version back to Vimeo as a replacement, and every embed of it shows the captions automatically.

Submit the video

curl -X POST https://api.caption.sh/v1/videos \
  -H "Authorization: Bearer $CAPTION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source_url": "https://example.com/video.mp4", "options": {"font": "inter"}}'

# response
# {"job_id": "job_8f3k..."}

Poll for the finished render

Rendering is asynchronous, so the submit response is a job id. Poll the job endpoint until it reports done or error, with your own timeout as the third exit.

curl https://api.caption.sh/v1/jobs/job_8f3k... \
  -H "Authorization: Bearer $CAPTION_API_KEY"

# poll every few seconds until status is done or error
# {"job_id": "job_8f3k...", "status": "done", ...}

What to know before you batch

  • Submissions are rate limited per API key. A 429 response means wait a moment and retry, it does not mean the job failed.
  • Rendering is prepaid at $0.10 per minute of video. A 402 response means the balance is empty, so top up before retrying.
  • Every render needs a bounded poll loop with three exits: done, error, and your own timeout. Do not poll forever.
  • The API documents no webhook surface, so polling is the intended way to learn a job finished.

Vimeo's replace video feature keeps the same URL and embed code when you swap in the captioned file, so existing embeds update without touching the pages that host them.

Back to all articles