# Add Subtitles to Video – Auto Captions, Burn SRT (Whisper) (`gazidev/video-auto-subtitles`) Actor

Add subtitles to video automatically: Whisper auto captions in 99 languages burned into MP4, or burn your own SRT/VTT. TikTok-style word highlight, uppercase, box, colours, font size, position. Get MP4 + SRT + VTT links. From $0.04 per video minute.

- **URL**: https://apify.com/gazidev/video-auto-subtitles.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Categories:** Videos, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $40.00 / 1,000 subtitled minute (up to 720p)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Add Subtitles to Video – Auto Captions & Burn SRT (Whisper)

**Add subtitles to any video automatically.** Give the Actor direct links to your videos (MP4, MOV, WebM, MKV). It
transcribes the speech with **OpenAI Whisper** (99 languages, or translate to English), splits it into short,
readable captions and **burns them into the video** with FFmpeg. Already have subtitles? Supply an **SRT or VTT** file
and the Actor just burns it in, cheaper. You get a ready-to-post **MP4 with hard-coded captions** plus **.srt and
.vtt** files and the full transcript, all as download links.

- 🎬 **Auto captions**: Whisper `tiny` / `base` / `small`, automatic language detection, word-level timing
- 🔥 **Burn SRT / VTT** you already have (hardsub), with the same styling options
- 📱 **TikTok / Reels / Shorts style**: big UPPERCASE words in the middle, the spoken word highlighted in yellow
- 🎨 Style it: 4 presets + font, font size, position (bottom / middle / top), max words per line, lines per caption,
  text / outline / highlight colours, background box and opacity, uppercase, bold
- 📄 Outputs: subtitled **MP4** (H.264/AAC, up to 1080p, landscape or vertical), **SRT**, **VTT**, transcript text,
  detected language
- 💸 Pay per video minute, from **$0.04**. Errors are free.

### What can I use it for?

- **Social media captions**: add animated-style subtitles to TikToks, Reels and YouTube Shorts in bulk, so videos
  watched without sound still get the message across.
- **Accessibility**: open captions for training videos, webinars, product demos and lectures.
- **Translate videos**: English subtitles for videos in Spanish, German, Turkish, Japanese… (`task: translate`).
- **Hardsub existing subtitles**: burn the SRT from your editor, translator or another Apify transcription Actor
  into the video, for platforms that do not support subtitle files.
- **Pipelines**: run after an Actor that outputs video file URLs, or from Make, Zapier, n8n or your backend via API.

### How to use

1. Paste one or more **direct video file links** into *Video file URLs* (Dropbox and Google Drive share links are
   converted automatically).
2. Pick a **caption style preset** (classic, boxed, social, bold-yellow) and optionally fine-tune it.
3. Optional: to burn your own subtitles, write `video-url | subtitle-url` on one line, or fill *Your SRT / VTT
   subtitle URLs* in the same order as the videos.
4. Click **Start**. Download the MP4/SRT/VTT from the dataset links or the key-value store.

With no input, the Actor subtitles a 23-second public-domain NASA interview clip so you can see the result in ~10 s.

### Input example

```json
{
  "videoUrls": [
    "https://cdn.example.com/reels/clip-01.mp4",
    "https://cdn.example.com/webinar.mov | https://cdn.example.com/webinar-en.srt"
  ],
  "captionStyle": "social",
  "marginPercent": 20,
  "position": "bottom",
  "highlightColor": "#00E5FF",
  "model": "base",
  "language": "auto",
  "maxResolution": "1080",
  "maxDurationMinutes": 30
}
```

| Preset | Look |
|---|---|
| `classic` | white bold Poppins, black outline, bottom, up to 2 lines × 6 words |
| `boxed` | white text on a 65% black box, bottom (YouTube/Netflix look) |
| `social` | 3 big UPPERCASE words, middle of the frame, spoken word in yellow (TikTok/Reels/Shorts) |
| `bold-yellow` | tall yellow Anton font, uppercase, lower third |

Font size and outline are given in pixels for a 1080-pixel short side and scale automatically, so the same settings
look the same on 1920×1080, 1080×1920 and 1280×720 videos.

### Output example

One dataset row per video (errors are separate, free rows):

```json
{
  "url": "https://upload.wikimedia.org/wikipedia/commons/b/b8/The_Numbers_Are_In-…_Gavin_Canned_Interview_pt_2%29.webm",
  "status": "ok",
  "mode": "auto-transcribed",
  "videoFileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/001-The-Numbers-Are-In-Where-Does-2021-Rank-subtitled.mp4",
  "srtFileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/001-The-Numbers-Are-In-Where-Does-2021-Rank.srt",
  "vttFileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/001-The-Numbers-Are-In-Where-Does-2021-Rank.vtt",
  "language": "en",
  "languageProbability": 0.78,
  "durationSeconds": 23.11,
  "outputSeconds": 23.11,
  "truncated": false,
  "output": {"width": 1920, "height": 1080, "fps": 10.0, "sizeBytes": 3326586, "format": "mp4"},
  "captionStyle": "classic",
  "captionCount": 5,
  "wordCount": 52,
  "text": "Last year was the sixth warmest year on record, which is slightly down from the warmest year on record that we had last year. …",
  "srt": "1\n00:00:05,710 --> 00:00:09,030\nLast year was the sixth warmest\nyear on record, which is slightly\n…",
  "billableMinutes": 1,
  "billedEvents": {"burned-minute-1080p": 1, "transcribed-minute": 1},
  "processingSeconds": 6.0
}
```

The run summary (videos, minutes, events, stop reason) is in the key-value store record `OUTPUT`.

### Pricing

Pay per started minute of the output video. No subscription; failed videos are never charged.

| Job | up to 720p | 1080p |
|---|---|---|
| Burn your own SRT/VTT | **$0.04 / min** | **$0.06 / min** |
| Auto captions (Whisper base/tiny) + burn | **$0.06 / min** | **$0.08 / min** |
| Auto captions (Whisper small) + burn | $0.09 / min | $0.11 / min |

Events: `burned-minute` $0.04, `burned-minute-1080p` $0.06, `transcribed-minute` $0.02, `transcribed-minute-small`
$0.05. A 30-second 1080p Reel with auto captions costs $0.08; a 10-minute 720p webinar $0.60, or $0.40 with your SRT.
Set *Maximum cost per run* and the Actor stops cleanly (it even cuts the last video at the affordable minute).

How this compares with other subtitle burners in the Apify Store (public prices, October 2026):

| Actor | Pricing model | 1-minute 1080p video with auto captions |
|---|---|---|
| **This Actor** | per output minute | **$0.08** (720p $0.06, own SRT from $0.04) |
| kantolabs/auto-captions-lyric-video | per output minute | $0.08 |
| dami_studio/auto-caption-burner | per 30-s block | $0.08 |
| lumaxys/media-to-professional-subtitles | start fee + per minute | $0.11 |
| seemuapps/social-video-downloader-transcriber | per video + per minute | ~$0.18 |
| agentutilitylabs/short-video-assembler | per video | $0.20 |
| dadhalfdev/add-subtitles-to-videos | per video | $0.50 |

### FAQ

**Can I use YouTube, TikTok or Instagram links?** No. Their terms of service forbid downloading videos with
third-party tools, so these links are rejected (free error row). Use a direct link to a file you own or may edit:
your CDN, S3/R2/GCS, Dropbox or Google Drive (shared as "Anyone with the link").

**Which languages are supported?** Whisper's 99 languages with automatic detection. Set `language` (e.g. `es`, `de`,
`tr`, `ja`) for short or noisy clips. `task: translate` gives English subtitles for any language. Captions in
Arabic, Cyrillic, Hindi, Chinese, Japanese and Korean are rendered with Noto fonts.

**How accurate is it?** `base` is good for clear speech; `small` is noticeably better for accents, background noise
and non-English. Add names and jargon to *Vocabulary hint*. You also get the SRT, so you can fix a word and re-run
with your corrected SRT at the cheaper burn-only price.

**Does it keep the original quality?** Subtitles are burned into the picture, so the video is re-encoded (H.264, CRF
23 `balanced`; choose `high` for bigger, sharper files). 4K inputs are scaled to 1080p; frame rates above 30 fps are
reduced to 30 (configurable). Audio is copied when it is already AAC.

**How long can a video be?** 30 minutes by default (`maxDurationMinutes`, up to 180). Longer videos are cut (or
skipped) and you pay only for the output minutes. Processing takes roughly 0.3–0.5× the video length at 1080p.

**Vertical 9:16 videos?** Yes, 1080×1920 and 720×1280 keep their shape; font sizes scale with the short side. For
TikTok/Reels set `marginPercent` to ~20 to keep captions above the app's buttons.

**Where are the files?** In the run's key-value store, with public links in each dataset row (`videoFileUrl`,
`srtFileUrl`, `vttFileUrl`). They follow your Apify data-retention settings.

### Use with AI agents / Apify MCP

The Actor works as a tool for AI agents through the [Apify MCP server](https://mcp.apify.com) (Claude, Cursor, VS Code,
ChatGPT connectors): add `gazidev/video-auto-subtitles`, then ask e.g. *"Add TikTok-style captions to these three
videos and give me the download links"*. Input is plain JSON (`videoUrls`, `captionStyle`…), output rows contain the
MP4/SRT/VTT links and the transcript, so agents can chain it with publishing or translation steps.
Via API: `POST https://api.apify.com/v2/acts/gazidev~video-auto-subtitles/run-sync-get-dataset-items?token=…` with
the input JSON.

### Related Actors

- **Audio & Video Transcriber** (`gazidev/media-transcriber`): transcripts and SRT/VTT for audio, video and podcast
  RSS feeds, without burning.
- **Video Converter & Compressor** (`gazidev/media-converter`): compress, convert, MP4 to MP3, GIFs.

Fonts: Poppins, Anton and Bebas Neue (SIL Open Font License, see `fonts/`), Noto and DejaVu (system packages).

# Actor input Schema

## `videoUrls` (type: `array`):

Direct links to video files: MP4, MOV, WebM, MKV, M4V, AVI (your CDN, S3/R2/GCS bucket, Dropbox or Google Drive file shared as 'Anyone with the link'). To burn your own subtitles for a video, put both links on one line: `https://…/video.mp4 | https://…/subtitles.srt`. **YouTube, TikTok, Instagram, Facebook, X, Vimeo… page links are rejected**: their terms of service forbid downloading. Only submit videos you have the right to edit.

## `subtitleUrls` (type: `array`):

Burn existing subtitles instead of auto-transcribing (cheaper, no Whisper). Matched to `Video file URLs` **by position**: the 1st subtitle URL goes with the 1st video, and so on. Use `-` for a video that should be auto-transcribed. Leave empty to auto-caption every video.

## `captionStyle` (type: `string`):

`classic`: white bold text with a black outline at the bottom, up to 2 lines. `boxed`: white text on a semi-transparent black box (YouTube/Netflix look). `social`: TikTok/Reels/Shorts style, 3 big UPPERCASE words in the middle with the spoken word highlighted in yellow. `bold-yellow`: big yellow Anton font, uppercase, lower third. Every option below overrides the preset.

## `fontName` (type: `string`):

Empty = preset font. Non-Latin scripts (Arabic, Cyrillic, Hindi, Chinese, Japanese, Korean…) automatically fall back to Noto fonts.

## `fontSize` (type: `integer`):

Text height in pixels for a 1080-pixel short side (1920×1080 or 1080×1920); scaled automatically for 720p/480p. Presets: classic 62, boxed 56, social 96, bold-yellow 80.

## `position` (type: `string`):

Where the captions sit. Empty = preset (bottom; middle for `social`).

## `marginPercent` (type: `integer`):

For bottom/top captions. Raise to ~18–25 for TikTok/Reels so captions stay above the app's buttons and description.

## `maxWordsPerLine` (type: `integer`):

Presets: classic/boxed 6, social 3, bold-yellow 4. Applies to auto captions and to your own SRT/VTT (long cues are split).

## `maxLines` (type: `integer`):

1–3. Presets: classic/boxed/bold-yellow 2, social 1.

## `uppercase` (type: `boolean`):

Social-media style capital letters. Empty = preset (on for `social` and `bold-yellow`).

## `bold` (type: `boolean`):

Empty = preset.

## `textColor` (type: `string`):

Hex like `#FFFFFF` or a name: white, yellow, red, green, blue, cyan, magenta, pink, orange, purple, lime, black.

## `highlightColor` (type: `string`):

Karaoke-style: the word being spoken is drawn in this colour (auto captions only; uses Whisper word timestamps). `#FFE600` in the `social` preset. Type `none` to turn it off.

## `outlineColor` (type: `string`):

Text border colour (hex or name).

## `outlineWidth` (type: `integer`):

0 = no outline. Presets: classic 3.5, social 6, bold-yellow 5.

## `backgroundBox` (type: `boolean`):

Draw a box behind the text (on in `boxed`).

## `boxColor` (type: `string`):

Hex or name; default black.

## `boxOpacity` (type: `integer`):

0 = invisible, 100 = solid. Preset 65.

## `model` (type: `string`):

`base` (default): good for clear speech. `tiny`: fastest, clean English. `small`: most accurate for accents, noise and non-English; billed as `transcribed-minute-small`.

## `language` (type: `string`):

`auto` detects the language. Or force a code to avoid mistakes on short or noisy clips: en, es, de, fr, pt, it, nl, pl, tr, ru, uk, ar, hi, ja, ko, zh, id, vi… (99 languages).

## `task` (type: `string`):

`transcribe`: subtitles in the spoken language. `translate`: English subtitles for a video in any language (same price).

## `initialPrompt` (type: `string`):

Optional names and jargon to spell correctly, e.g. `Apify, Kubernetes, Dr. Nguyen`.

## `maxResolution` (type: `string`):

Short side of the output MP4 (1080 = 1920×1080 or 1080×1920 vertical). Larger videos (4K) are scaled down; smaller ones are never upscaled. Up to 720p is billed as `burned-minute`, above as `burned-minute-1080p`.

## `videoQuality` (type: `string`):

H.264 quality of the re-encoded MP4 (subtitles are burned into the picture, so the video is always re-encoded). `balanced` looks like the source at a normal file size.

## `maxFps` (type: `integer`):

Videos above this frame rate (e.g. 60 fps) are converted to it.

## `maxDurationMinutes` (type: `integer`):

Longer videos are cut at this length (or skipped, see below). You pay only for the minutes in the output video.

## `longVideoHandling` (type: `string`):

`truncate`: subtitle the first N minutes (`truncated: true`). `skip`: free error row.

## `maxFileSizeMB` (type: `integer`):

Larger downloads are stopped (free error row).

## `downloadTimeoutSecs` (type: `integer`):

Maximum time to download one video. Retried on network errors and HTTP 429/5xx.

## Actor input object example

```json
{
  "videoUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/b/b8/The_Numbers_Are_In-_Where_Does_2021_Rank_for_Global_Temperatures_Live_Shots_%28SVS14069_-_Gavin_Canned_Interview_pt_2%29.webm"
  ],
  "subtitleUrls": [],
  "captionStyle": "classic",
  "model": "base",
  "language": "auto",
  "task": "transcribe",
  "maxResolution": "1080",
  "videoQuality": "balanced",
  "maxFps": 30,
  "maxDurationMinutes": 30,
  "longVideoHandling": "truncate",
  "maxFileSizeMB": 2000,
  "downloadTimeoutSecs": 900
}
```

# Actor output Schema

## `videos` (type: `string`):

No description

## `transcripts` (type: `string`):

No description

## `errors` (type: `string`):

No description

## `files` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "videoUrls": [
        "https://upload.wikimedia.org/wikipedia/commons/b/b8/The_Numbers_Are_In-_Where_Does_2021_Rank_for_Global_Temperatures_Live_Shots_%28SVS14069_-_Gavin_Canned_Interview_pt_2%29.webm"
    ],
    "captionStyle": "classic",
    "language": "auto"
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/video-auto-subtitles").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "videoUrls": ["https://upload.wikimedia.org/wikipedia/commons/b/b8/The_Numbers_Are_In-_Where_Does_2021_Rank_for_Global_Temperatures_Live_Shots_%28SVS14069_-_Gavin_Canned_Interview_pt_2%29.webm"],
    "captionStyle": "classic",
    "language": "auto",
}

# Run the Actor and wait for it to finish
run = client.actor("gazidev/video-auto-subtitles").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "videoUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/b/b8/The_Numbers_Are_In-_Where_Does_2021_Rank_for_Global_Temperatures_Live_Shots_%28SVS14069_-_Gavin_Canned_Interview_pt_2%29.webm"
  ],
  "captionStyle": "classic",
  "language": "auto"
}' |
apify call gazidev/video-auto-subtitles --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gazidev/video-auto-subtitles"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/sxMxjeTxOOT9FJsiZ/builds/A3EYKeLykEujeHTjh/openapi.json
