2026-10-26 · 6 min read

Batch Captioning a Back Catalog: Add Captions to Hundreds of Videos with One API

If you have a back catalog of videos, a course library, a podcast archive, a folder of product demos, captioning them one at a time in an editor is not a real option. It is the kind of work that gets planned and never done. The only way a catalog of a few hundred videos actually gets captioned is with a script and an API.

caption.sh is built for exactly this loop. You POST each video, poll the job until it finishes, and download a new MP4 with word-timed, styled captions burned in. No timeline UI, no per-video manual work, and at $0.10 per minute the cost of a whole catalog is a known number before you start.

The shape of the job

Batch captioning is a map over a list of files. For each video you submit one job with the source URL, your caption style, and the font and animation you want. The API does transcription, word timing, styling, and burn-in. When the job completes, the result redirects to a signed URL for the finished MP4.

#!/bin/bash
# caption every mp4 already uploaded to your public bucket
KEY=$CAPTION_API_KEY
for f in catalog/*.mp4; do
  src="https://cdn.example.com/$f"
  job=$(curl -s https://api.caption.sh/v1/jobs \
    -H "Authorization: Bearer $KEY" \
    -H "Content-Type: application/json" \
    -d "{\"video_url\":\"$src\",\"style\":{\"font\":\"inter\",\"animation\":\"word-highlight\"}}" \
    | jq -r .id)
  while true; do
    st=$(curl -s https://api.caption.sh/v1/jobs/$job -H "Authorization: Bearer $KEY")
    status=$(echo "$st" | jq -r .status)
    [ "$status" = "done" ] && break
    [ "$status" = "failed" ] && { echo "FAILED $f"; break; }
    sleep 5
  done
  # result is a 302 to a signed URL; -L follows it
  curl -sL "$(echo "$st" | jq -r .result_url)" -o "captioned-$f"
done

A few things worth noticing in that loop. Jobs are independent, so you can run several in parallel instead of strictly one at a time. There are no webhooks to configure: you poll GET /v1/jobs/{id} until the status flips. And the captions are burned into the pixels of the output video, which means they render identically on TikTok, YouTube, Instagram, a course player, or an email client with no player-side support required.

Cost and style stay predictable

  • Pricing is $0.10 per minute of video, prepaid. A 300-video catalog of five-minute videos is 1,500 minutes, so $150, and you know that before the first job runs.
  • Style is set per job: pick a font (inter, roboto, luckiest-guy and others), colors, position, and word-level animation, and every video in the batch comes out identical.
  • Failed jobs are visible in the status field, so a retry loop is a few lines, not a support ticket.

The usual reason a back catalog stays uncaptioned is that the work is boring and unbounded. Turning it into a bounded, priced, scriptable batch is most of the battle. If you want to see the output before writing the loop, get an API key at caption.sh and run one video through first.

Back to all articles