# Add Subtitles to Videos (`dadhalfdev/add-subtitles-to-videos`) Actor

Burn karaoke-style word-highlighted subtitles into videos. Upload a clip or paste a URL, choose font, size, and highlight color, and download a captioned MP4.

- **URL**: https://apify.com/dadhalfdev/add-subtitles-to-videos.md
- **Developed by:** [Marco Rodrigues](https://apify.com/dadhalfdev) (community)
- **Categories:** Automation, Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $500.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 🎬 Add Subtitles to Videos

Turn a talking video into a captioned MP4 with word highlighting.

The input is the **video**. Upload a file or paste a direct URL, pick a font and colors, and download the subtitled clip plus an SRT and a word-level transcript. Each video can be **at most 3 minutes** and **50 MB**.

A **transcript JSON file is optional**. Most runs only need the video — speech is transcribed automatically. If you already have a transcript (for example you fixed a few words in a previous output), attach that file and it **replaces** automatic transcription. Captions are burned from your JSON instead.

### 💡 Perfect for

- 📣 **Influencers and creators:** Caption TikToks, Reels, and Shorts so the hook still lands on mute.
- 🎥 **UGC content:** Subtitle product demos, testimonials, and creator ads before they go to brands or ad accounts.
- 🛍️ **Ecommerce and brands:** Caption unboxings, how-tos, and shoppable clips for paid social.
- 🧑‍💼 **Agencies:** Caption client videos through the Apify API instead of editing by hand.
- 🎙️ **Podcasts and YouTube:** Burn captions onto clips, trailers, and audiograms.
- 🎓 **Courses and explainers:** Make lessons readable without the sound on.
- 🤖 **Avatar and talking-head videos:** Overlay karaoke-style subtitles on generated spokespeople.
- 🔗 **Automations:** Call the Actor from Make, Zapier, n8n, or the Apify API.

### ✨ What you get

For the input video the Actor:

1. Gets word-level captions — either by transcribing the audio, or by using your optional transcript file.
2. Burns captions into the video, highlighting the word being spoken.
3. Saves a captioned **MP4**, an **SRT**, a **JSON transcript**, and a dataset row with download links.

The output keeps the source frame rate. Clips larger than 1080p on the long side are scaled down so rendering stays fast. Captions are drawn on top.

### 📄 Optional transcript file

Leave `transcriptJson` empty unless you want to control the wording.

| Input | What happens |
|-------|----------------|
| Video only | Audio is transcribed, then captions are burned in. |
| Video + transcript JSON | Transcription is skipped. Your JSON is burned in as captions. |

Use the transcript file when you want to perfect the output: download `transcript-1.json` from a previous result, change only the wrong `word` values, and pass that file with the same video. Leave `start` and `end` unchanged so highlighting still matches the audio. Segment `text` is rebuilt from the words.

```json
{
  "language": "en",
  "text": "Hello world this is a demo",
  "segments": [
    {
      "text": "Hello world",
      "start": 0.2,
      "end": 1.1,
      "words": [
        { "word": "Hello", "start": 0.2, "end": 0.5 },
        { "word": "world", "start": 0.55, "end": 1.1 }
      ]
    }
  ]
}
```

You can upload the file in `transcriptJson`, or paste the same JSON into `transcriptJsonContent`.

### 🔤 Font family

Choose one of these 20 typefaces with `fontFamily`:

| Value | Preview |
|-------|---------|
| `montserrat` | ![Montserrat](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/montserrat.png) |
| `poppins` | ![Poppins](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/poppins.png) |
| `oswald` | ![Oswald](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/oswald.png) |
| `inter` | ![Inter](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/inter.png) |
| `roboto` | ![Roboto](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/roboto.png) |
| `rubik` | ![Rubik](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/rubik.png) |
| `nunito` | ![Nunito](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/nunito.png) |
| `anton` | ![Anton](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/anton.png) |
| `bebas_neue` | ![Bebas Neue](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/bebas_neue.png) |
| `archivo_black` | ![Archivo Black](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/archivo_black.png) |
| `raleway` | ![Raleway](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/raleway.png) |
| `lato` | ![Lato](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/lato.png) |
| `work_sans` | ![Work Sans](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/work_sans.png) |
| `dm_sans` | ![DM Sans](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/dm_sans.png) |
| `outfit` | ![Outfit](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/outfit.png) |
| `kanit` | ![Kanit](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/kanit.png) |
| `teko` | ![Teko](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/teko.png) |
| `russo_one` | ![Russo One](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/russo_one.png) |
| `righteous` | ![Righteous](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/righteous.png) |
| `barlow_condensed` | ![Barlow Condensed](https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/barlow_condensed.png) |

### 🎨 Colors

`textColor`, `highlightColor`, and `outlineColor` accept:

- Hex: `#FFF` or `#FFFFFF`
- Names: `white`, `black`, `red`, `orange`, `yellow`, `green`, `blue`, `purple`, `pink`, `cyan`, `magenta`, `gray`, `navy`, `teal`, `gold`, `brown`, and similar CSS color names

`highlightColor` is the background overlay on the word currently being spoken.

### 🚀 How to use

1. Add a video to `video` — **upload a file** or **paste a direct URL**.
2. Optionally add a transcript JSON if you want to skip transcription and use your own wording.
3. Set font family, size, and colors.
4. Start the Actor and download `subtitled-video-1.mp4` from the key-value store.

**One video per run**, at most **3 minutes** and **50 MB**. Start another run for each extra clip.

### 📥 Input example

```json
{
  "video": "https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/qa-speech.mp4",
  "fontFamily": "montserrat",
  "fontSize": 40,
  "textColor": "white",
  "highlightColor": "#FFCC00",
  "outlineColor": "black",
  "uppercase": true,
  "position": "bottom",
  "maxWordsPerCaption": 4,
  "language": "en"
}
```

With an optional transcript file:

```json
{
  "video": "https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/qa-speech.mp4",
  "transcriptJson": "https://api.apify.com/v2/key-value-stores/.../records/transcript-1.json",
  "fontFamily": "montserrat",
  "highlightColor": "#FFCC00"
}
```

### 📤 Output example

```json
{
  "source_video": "https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/qa-speech.mp4",
  "subtitled_video_url": "https://api.apify.com/v2/key-value-stores/.../records/subtitled-video-1.mp4",
  "srt_url": "https://api.apify.com/v2/key-value-stores/.../records/subtitles-1.srt",
  "transcript_url": "https://api.apify.com/v2/key-value-stores/.../records/transcript-1.json",
  "transcript_text": "Hello world this is a demo",
  "language": "en",
  "duration_seconds": 12.4,
  "word_count": 6,
  "font_family": "Montserrat",
  "font_size": 40,
  "highlight_color": "#FFCC00",
  "used_transcript_json": false
}
```

The first captioned file is also stored as `subtitled-video-1.mp4` in the default key-value store.

### ⚙️ Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `video` | file or URL | Yes | Video to caption. Upload a file or paste a direct link. One per run, 3 minutes and 50 MB max. |
| `transcriptJson` | JSON file or URL | No | Optional transcript file. When set, replaces automatic transcription. |
| `transcriptJsonContent` | JSON object | No | Optional. Paste the same transcript JSON in the console. |
| `fontFamily` | string | No | Caption typeface. One of the 20 fonts listed above. |
| `fontSize` | integer | No | Caption size in pixels (16–120). Default `40`. |
| `textColor` | hex or name | No | Color of idle words. Default `white`. |
| `highlightColor` | hex or name | No | Overlay color for the spoken word. Default `#003399`. |
| `outlineColor` | hex or name | No | Caption stroke color. Default `black`. |
| `uppercase` | boolean | No | Force uppercase captions. Default `true`. |
| `position` | string | No | `bottom`, `center`, or `top`. |
| `maxWordsPerCaption` | integer | No | Words shown together on screen. Default `5`. |
| `language` | string | No | Spoken language for transcription. Default `en`. |
| `mistralApiKey` | secret | No | Optional Mistral key override. |

# Actor input Schema

## `video` (type: `string`):

Upload a video file or paste a direct URL (mp4, mov, webm, mkv). One video per run, at most 3 minutes and 50 MB.

## `transcriptJson` (type: `string`):

Optional. Leave empty to transcribe the video automatically. Upload a transcript JSON (from a previous output, after fixing words) to skip speech-to-text and burn these captions instead.

## `transcriptJsonContent` (type: `object`):

Optional. Same as the transcript file, but pasted in the console. Ignored if you also upload a transcript file.

## `fontFamily` (type: `string`):

Typeface used for the burned-in captions.

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

Caption size in pixels. 36–48 works well for vertical 1080×1920 videos; use 24–32 for landscape.

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

Color of words that are not currently spoken. Use hex (#FFF or #FFFFFF) or a name such as white, black, yellow, or orange.

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

Background color overlaid on the word being spoken. Use hex (#FF0 or #FFCC00) or a name such as yellow, orange, or green.

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

Stroke around each word so captions stay readable on light or busy backgrounds. Hex (#000 or #000000) or a name such as black.

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

Render every word in uppercase.

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

Vertical placement of the subtitle block.

## `maxWordsPerCaption` (type: `integer`):

How many words appear on screen at once. 3–5 is typical for short-form captions.

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

Language of the video audio, used by Mistral Voxtral for transcription.

## `mistralApiKey` (type: `string`):

Optional override. A Mistral key is already configured. Set this only if you want to use a different account.

## Actor input object example

```json
{
  "video": "https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/qa-speech.mp4",
  "fontFamily": "montserrat",
  "fontSize": 40,
  "textColor": "#FFFFFF",
  "highlightColor": "#003399",
  "outlineColor": "#000000",
  "uppercase": true,
  "position": "bottom",
  "maxWordsPerCaption": 5,
  "language": "en"
}
```

# Actor output Schema

## `overview` (type: `string`):

Dataset row with download links for the captioned video.

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

All dataset items without view transformation.

## `subtitledVideo` (type: `string`):

MP4 with burned-in captions.

# 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 = {
    "video": "https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/qa-speech.mp4"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dadhalfdev/add-subtitles-to-videos").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 = { "video": "https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/qa-speech.mp4" }

# Run the Actor and wait for it to finish
run = client.actor("dadhalfdev/add-subtitles-to-videos").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 '{
  "video": "https://api.apify.com/v2/key-value-stores/3ZzUVcATxmN3HCTaI/records/qa-speech.mp4"
}' |
apify call dadhalfdev/add-subtitles-to-videos --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dadhalfdev/add-subtitles-to-videos"
        }
    }
}

```

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/rJvVRZlXTUTOPYXz5/builds/OvfWf8QyS3ZwmTk4Z/openapi.json
