2026-10-10 · 4 min read

Caption videos in a GitHub Actions workflow

If your videos live in a repo or get built in CI, you can caption them in the same pipeline. The caption.sh API is three HTTP calls, so a plain shell step with curl and jq is enough. No action to install.

The workflow

name: caption
on:
  workflow_dispatch:
    inputs:
      video_url:
        required: true
jobs:
  caption:
    runs-on: ubuntu-latest
    steps:
      - name: Caption video
        env:
          CAPTION_API_KEY: ${{ secrets.CAPTION_API_KEY }}
        run: |
          JOB=$(curl -sf https://api.caption.sh/v1/videos \
            -H "Authorization: Bearer $CAPTION_API_KEY" \
            -H 'Content-Type: application/json' \
            -d "{\"source_url\":\"${{ inputs.video_url }}\",\"options\":{\"max_words\":3}}" | jq -r .job_id)
          for i in $(seq 1 60); do
            S=$(curl -sf https://api.caption.sh/v1/jobs/$JOB -H "Authorization: Bearer $CAPTION_API_KEY" | jq -r .status)
            [ "$S" = done ] && break
            [ "$S" = error ] && exit 1
            sleep 5
          done
          curl -sf -o captioned.mp4 https://api.caption.sh/v1/jobs/$JOB/result -H "Authorization: Bearer $CAPTION_API_KEY"
      - uses: actions/upload-artifact@v4
        with:
          name: captioned
          path: captioned.mp4

Keep the key in a secret

Create an API key in the caption.sh dashboard and store it as a repository secret named CAPTION_API_KEY. The workflow reads it from the environment, so it never shows up in the file or in logs.

Things worth knowing

  • The video has to be reachable by URL. For a file in the runner, use the upload flow: the submit call returns a signed upload URL, you PUT the bytes to it, and the job starts when the upload finishes.
  • Cap the poll loop. 60 passes of five seconds is plenty for short clips, and curl -f makes the step fail on a 4xx instead of silently saving an error page.
  • A 402 on the submit means the prepaid balance is empty and a 422 means the input was rejected. Neither is worth retrying.

When this is the wrong tool

caption.sh burns captions into the video. It does not export SRT or VTT sidecar files. If a step needs a transcript file, produce it with a transcription tool and use caption.sh for the version people watch on a feed.

Try it

Judge the output in the playground at caption.sh first, then run the workflow above. The reference is at caption.sh/docs and the OpenAPI spec is at api.caption.sh/openapi.json.

Back to all articles