# How to make SRT subtitles from a video with an API

> Send your video to POST /transcribe with output_format set to srt and word_timestamps set to false. Once the job finishes, save the returned string as a .srt file. If you want subtitles in another language, pass translate_to. This guide walks through the request, what the SRT output looks like, how to build clean cues from word timings, and how to generate WebVTT or burn the subtitles into the video.

Last updated: 2026-10-18 · HTML: https://console.kolwrite.com/guides/srt-subtitles-api

## The request

Two calls: start the job, then poll it. The `result` of a finished `srt` job is a string, not JSON. This version needs only curl and jq:

```bash
# 1. Start the job. "word_timestamps": false gives one cue per segment.
JOB=$(curl -s https://app.kolwrite.com/transcribe \
  -H "x-api-key: $KOLWRITE_API_KEY" -H "Content-Type: application/json" \
  -d '{"audio_url": "https://example.com/talk.mp3", "model": "oracle", "output_format": "srt", "word_timestamps": false}' \
  | jq -r .jobId)

# 2. Poll until it is done and save the result string as a .srt file.
while true; do
  REPLY=$(curl -s "https://app.kolwrite.com/transcribe/$JOB" -H "x-api-key: $KOLWRITE_API_KEY")
  STATUS=$(echo "$REPLY" | jq -r .status)
  if [ "$STATUS" = "COMPLETED" ]; then echo "$REPLY" | jq -r .result > talk.srt; break; fi
  if [ "$STATUS" = "FAILED" ]; then echo "$REPLY" | jq -r .error; break; fi
  sleep 5
done
```

You can pass a video file directly and the API will read its audio track. Upload local files up to 100 MiB through `POST /upload` first. For long videos, extract the audio with ffmpeg before uploading. The Python helper in the [video editing guide](https://console.kolwrite.com/guides/video-editing-with-ai) handles both steps.

## What the SRT contains

Setting `"word_timestamps": false` gives you one cue per segment, like a standard subtitle file:

```text
1
00:00:01,000 --> 00:00:03,750
Hello everyone and welcome to the show.

2
00:00:05,000 --> 00:00:07,750
Today we talk about editing video from a transcript.
```

By default, word timestamps are turned on. The API outputs a separate cue for every word, repeating the full segment text and wrapping the active word in a yellow `<font>` tag. This creates a read-along highlight instead of ordinary subtitles:

```text
1
00:00:01,000 --> 00:00:01,350
<font color="yellow">Hello</font> everyone and welcome to the show.

2
00:00:01,400 --> 00:00:01,750
Hello <font color="yellow">everyone</font> and welcome to the show.
```

Set `"word_timestamps": false` if you want standard subtitles, or build your own cues from the word timings in the JSON output as shown below.

## Build custom cues from word timings

Ask for `output_format: "json"` and group the words into cues yourself. Keep each cue to at most 42 characters and 6 seconds, and start a new one after a pause. That gives you control over line length, which matters when burning captions into video.

srt_tools.py

```python
# srt_tools.py — neat subtitle cues from word timings
def srt_time(seconds):
    ms = round(seconds * 1000)
    h, ms = divmod(ms, 3_600_000)
    m, ms = divmod(ms, 60_000)
    s, ms = divmod(ms, 1000)
    return f"{h:02}:{m:02}:{s:02},{ms:03}"


def cues_from_words(words, max_chars=42, max_seconds=6.0, max_gap=0.8):
    """Group timed words into subtitle cues: (start, end, text)."""
    cues, line = [], []
    for word in words:
        text = word["word"].strip()
        if not text:
            continue
        if line:
            length = sum(len(w["word"]) + 1 for w in line) + len(text)
            if (length > max_chars
                    or word["end"] - line[0]["start"] > max_seconds
                    or word["start"] - line[-1]["end"] > max_gap):
                cues.append(line)
                line = []
        line.append({"start": word["start"], "end": word["end"], "word": text})
    if line:
        cues.append(line)
    return [(c[0]["start"], c[-1]["end"], " ".join(w["word"] for w in c)) for c in cues]


def cues_from_result(result):
    """Cues for a whole JSON result; a segment without word times becomes one cue."""
    cues = []
    for segment in result["segments"]:
        if segment.get("words"):
            cues += cues_from_words(segment["words"])
        elif segment["text"].strip():
            cues.append((segment["start"], segment["end"], segment["text"].strip()))
    return cues


def write_srt(cues, path):
    with open(path, "w", encoding="utf-8") as out:
        for number, (start, end, text) in enumerate(cues, 1):
            out.write(f"{number}\n{srt_time(start)} --> {srt_time(end)}\n{text}\n\n")
```

`cues_from_result(result)` pulls `(start, end, text)` tuples for the full result, falling back to segment times if the model didn't return word times. Then `write_srt(cues, "talk.srt")` writes the file to disk.

## Subtitles in another language

Pass a language code in `translate_to` and pick an Oracle model (`oracle_mini` is the cheapest one with translation). The cue text comes back in the target language. Keep `word_timestamps` set to false so you get one cue per segment.

```json
{
  "audio_url": "https://example.com/lecture-he.mp4",
  "model": "oracle_mini",
  "language": "he",
  "translate_to": "en",
  "output_format": "srt",
  "word_timestamps": false
}
```

If you need several languages for the same video, run a separate job for each target language. Treat machine translation as a draft and check names, numbers, and specific terms before publishing.

## WebVTT, ASS, and other formats

The API returns `json`, `txt` or `srt` only. Convert SRT to WebVTT for an HTML5 `<video>` track with ffmpeg:

```bash
ffmpeg -i video.srt video.vtt
```

ffmpeg converts between most subtitle formats the same way: just change the output file extension.

## Burn in or attach the subtitles

Burned into the video frames:

```bash
ffmpeg -i edited.mp4 -vf "subtitles=edited.srt" -c:a copy burned.mp4
```

Attached as an MP4 subtitle track that viewers can toggle on and off:

```bash
ffmpeg -i edited.mp4 -i edited.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng soft.mp4
```

YouTube Studio and most video players and editors take `.srt` files directly.

## Hebrew and Arabic subtitles

- The API returns plain Unicode text. Right-to-left layout is handled by the video player or by ffmpeg's subtitle renderer, which renders both Hebrew and Arabic properly as long as the font includes the glyphs.
- Pick a font that covers the script and check a single rendered frame before encoding the whole video. In our tests, Arial rendered Hebrew fine, but missed a glyph on one Arabic word, so use a different font for Arabic.

```bash
ffmpeg -i video.mp4 -vf "subtitles=video.he.srt:force_style='FontName=Arial,FontSize=24'" -c:a copy burned.mp4
```

- If a line ends with punctuation or Latin characters like a brand name or a number, players without proper bidirectional text support may put them on the wrong side. Test on the players your viewers actually use.

## Pricing

| Job | Model | Cost |
| --- | --- | --- |
| 45-minute lecture, subtitles | `oracle` | US$1.08 |
| The same lecture, translated subtitles | `oracle_mini` | US$0.72 |
| One hour of clear audio, subtitles | `base` | US$0.48 |

Billed by the second with no minimum. Full rates: [/llms/pricing.md](https://console.kolwrite.com/llms/pricing.md).

## Questions and answers

### How do I get an SRT file from a video?

Send the video URL to `POST /transcribe` with `output_format: "srt"` and `word_timestamps: false`. Poll the job until it finishes, then save the `result` string as a `.srt` file.

### Does the API return WebVTT?

No, only `json`, `txt`, or `srt`. You can convert an SRT file to WebVTT with `ffmpeg -i video.srt video.vtt`.

### Why does my SRT have one cue per word?

Word timestamps are enabled by default, so the SRT output has a separate cue for every word with the active word highlighted. Pass `word_timestamps: false` if you want one cue per segment, or build the cues yourself from the JSON.

### Can I get subtitles in another language?

Yes. Set `translate_to` to the target language code and use an Oracle model. The subtitle cues will come back translated.

### What is the difference between captions and subtitles?

Subtitles transcribe or translate speech for viewers who can hear the audio, while captions also include non-speech sound descriptions for deaf or hard-of-hearing viewers. The API gives you the timed speech text; if you need full captions, you will need to add the sound descriptions yourself.

## Related pages

- [Edit video with AI agents](https://console.kolwrite.com/guides/video-editing-with-ai)
- [Word timestamps API](https://console.kolwrite.com/guides/word-timestamps-api)
- [Podcast and YouTube captions](https://console.kolwrite.com/guides/podcast-youtube-captions)
- [Media, podcasts & subtitles](https://console.kolwrite.com/use-cases/media)
- [Universities & students](https://console.kolwrite.com/use-cases/education)
- [API documentation](https://console.kolwrite.com/public_api_docs)

---
Get an API key: https://console.kolwrite.com/auth · API documentation: https://console.kolwrite.com/public_api_docs · llms.txt: https://console.kolwrite.com/llms.txt
