Endpoints

Captions API

POST /v1/captions burns word-by-word animated captions into any video up to 15 minutes long. It transcribes the speech with Whisper large-v3-turbo, or uses word timings you send, and styles the text with 3 presets you can fine-tune.

You get the captioned MP4 back, plus the transcript with word timings and ready-made SRT, VTT and ASS subtitle files from the same job.

POST/v1/captions
Typical render
13 to 40 s
7 s and 42 s clips in our tests
Price
$0.02
per output minute
Input
Up to 15 minutes
500 MB per file

Example#

Send a public link to your video, or the URL of an upload, and a preset. The call waits up to 60 seconds for the render, so a short clip comes back finished.

curl -sS "https://api.reelsdk.com/v1/captions" \
  -H "Authorization: Bearer $REELSDK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://example.com/interview.mp4",
    "style": "karaoke"
  }'

Parameters#

Send a JSON body. Fields we don't recognize are ignored, so check the spelling of optional fields.

ParameterDescription
video_urlstringRequired

The video to caption. Any public URL works, including a /f/ URL from POST /v1/uploads. The file can be up to 500 MB and 15 minutes long.

Public http(s) URL, up to 4,096 characters

stylestring or object

A preset name, or a style object that starts from a preset and changes only the fields you set. See Caption styles.

bolddefaultkaraokeminimal
languagestring

Language of the speech. Leave it out and we detect it from the audio. Region tags are dropped, so pt-BR is read as pt.

A code like en, pt-BR or jpn

wordsarray

Word timings to burn in instead of transcribing, as { word, start, end } objects with times in seconds. See Your own word timings.

Up to 50,000 items

promptstring

Names and terms the transcriber should spell correctly, such as product or people names. Ignored when you send words.

Up to 500 characters

qualitystring

H.264 encoder quality: draft is CRF 26, standard CRF 21 and high CRF 18. It changes detail and file size, not resolution.

draftstandarddefaulthigh
webhook_urlstring

We POST the finished job to this URL, signed with your webhook secret. See Webhooks.

Public http(s) URL

Caption styles#

Pass a preset name like bold, karaoke or minimal to style your captions right away. To change how they look, send a style object instead. It starts from the preset you name, or bold, and overrides only the fields you set.

PresetFontLayoutColors
boldMontserrat, uppercaseMiddle, 3 words at a timeWhite text, the spoken word in #FFE14D yellow
karaokePoppinsBottom, 5 words at a timeWhite text, the spoken word in #3DDCFF cyan
minimalPoppins at 0.85 sizeBottom, 7 words at a timeWhite text, no highlight

All three presets draw a black outline. Colors accept #RRGGBB, #RGB or one of 15 names: white, black, yellow, gold, orange, red, pink, magenta, purple, blue, cyan, green, lime, gray and grey.

Style fieldDescription
style.presetstring

The preset the other fields start from.

bolddefaultkaraokeminimal
style.fontstring

Caption font. Words in scripts these fonts can't draw, such as Japanese or Arabic, switch to a matching Noto font.

MontserratAntonBebas NeuePoppins
style.positionstring

Where the captions sit. center is accepted as middle.

topmiddlebottom
style.max_wordsinteger

The most words on screen at once.

1 to 8

style.uppercaseboolean

Show every word in capitals.

style.text_colorstring

Color of the caption text.

#RRGGBB, #RGB or a color name

style.highlight_colorstring

Color of the word being spoken.

#RRGGBB, #RGB or a color name

style.outline_colorstring

Color of the outline around each word.

#RRGGBB, #RGB or a color name

style.sizenumber

Text scale, where 1 is the standard size.

0.5 to 2

style object
{
  "style": {
    "preset": "bold",
    "font": "Anton",
    "position": "bottom",
    "max_words": 2,
    "highlight_color": "lime"
  }
}

Languages and fonts#

We detect the language from the audio, or you can set language with a code like en or pt-BR to skip detection. Three-letter codes such as jpn work too.

Japanese, Chinese, Korean, Arabic, Hebrew, Thai, Devanagari, Lao and Myanmar text switches to a matching Noto font automatically. Chinese, Japanese and Korean captions break lines between words instead of at spaces, and right-to-left scripts render right to left.

Your own word timings#

If you already have word timings from your own transcriber or an edited transcript, pass them in words and we skip transcription. Each item is a word with start and end in seconds. An end earlier than its start is moved up to the start.

Request body with words
{
  "video_url": "https://example.com/interview.mp4",
  "style": {
    "preset": "karaoke",
    "highlight_color": "#FF5A36"
  },
  "words": [
    { "word": "Ship", "start": 0.12, "end": 0.41 },
    { "word": "it", "start": 0.41, "end": 0.58 },
    { "word": "today.", "start": 0.58, "end": 1.1 }
  ]
}

When we transcribe, send prompt with names and terms so the transcriber spells them right.

Response#

A finished job answers 200 and its outputs hold five file URLs. If the render takes longer than your wait, you get 202 with the same job and a poll_url. See Jobs for waiting and polling.

200 OK
{
  "job": {
    "id": "job_q33902h8shjtevc5j2cd",
    "op": "captions",
    "status": "succeeded",
    "stage": null,
    "created_at": "2026-10-26T01:15:43.902Z",
    "finished_at": "2026-10-26T01:15:56.165Z",
    "outputs": {
      "video": "https://api.reelsdk.com/f/out/job_q33902h8shjtevc5j2cd/video.mp4",
      "transcript": "https://api.reelsdk.com/f/out/job_q33902h8shjtevc5j2cd/transcript.json",
      "srt": "https://api.reelsdk.com/f/out/job_q33902h8shjtevc5j2cd/captions.srt",
      "vtt": "https://api.reelsdk.com/f/out/job_q33902h8shjtevc5j2cd/captions.vtt",
      "ass": "https://api.reelsdk.com/f/out/job_q33902h8shjtevc5j2cd/captions.ass"
    },
    "result": null,
    "duration_s": 7.2,
    "error": null,
    "usage": {
      "output_seconds": 7.2,
      "audio_seconds": 7.211,
      "tts_characters": 0,
      "images": 0
    },
    "poll_url": "https://api.reelsdk.com/v1/jobs/job_q33902h8shjtevc5j2cd"
  }
}
OutputFileContents
videovideo.mp4The captioned H.264 video in your framing, with the short side capped at 1080 px and the frame rate at 60 fps. AAC audio is kept as is; other audio becomes AAC at 192 kbps.
transcripttranscript.jsonFull text, language, duration and every word with start and end in seconds
srtcaptions.srtSubRip subtitles for players and upload forms
vttcaptions.vttWebVTT subtitles for the HTML <track> element
asscaptions.assThe styled subtitle file we burned in

transcript.json

transcript.json (first 5 words)
{
  "text": "Three days by the water, mountains at sunrise, forests by noon, and a lake that held the whole sky.",
  "language": "en",
  "duration_s": 7.2,
  "words": [
    { "word": "Three", "start": 0, "end": 0.44 },
    { "word": "days", "start": 0.44, "end": 0.66 },
    { "word": "by", "start": 0.66, "end": 0.88 },
    { "word": "the", "start": 0.88, "end": 0.98 },
    { "word": "water,", "start": 0.98, "end": 1.32 }
  ]
}

captions.srt

captions.srt
1
00:00:00,000 --> 00:00:05,500
Three days by the water, mountains at
sunrise, forests by noon, and a lake that

2
00:00:05,500 --> 00:00:07,080
held the whole sky.

usage.output_seconds is the length of the captioned video and usage.audio_seconds the speech we transcribed. Output files stay up for 7 days.

Errors#

Validation problems return 400 before a job starts. Problems with the video itself fail the job with 422, and the body carries the job next to the error. The full list is in Errors.

CodeHTTPWhen it happens
INVALID_REQUEST400A field is missing or invalid, for example video_url: must be an http(s) URL
INPUT_FETCH_FAILED422We couldn't download the video, for example Could not fetch video_url: HTTP 404
INPUT_TOO_LARGE422The video is over 500 MB
INPUT_UNSUPPORTED422The file isn't readable media, runs longer than 15 minutes, or has no audio track and you sent no words
TRANSCRIBE_FAILED500Speech-to-text failed, even after our automatic retry. Send the request again.

Tips#

  • Captions render in about the length of the clip. Clips longer than about a minute can outlast the default 60 s wait, so raise ?wait or use a webhook.
  • Your video needs an audio track with speech, unless you send words.
  • The srt and vtt files work with YouTube, video players and platforms that take subtitle files, so one job gives you burned-in captions and a subtitle track.
  • Use quality: "draft" while you tune a style, then render the final cut with standard or high.

Last updated 26 October 2026