# SRT Subtitles Generator — video & audio to SRT/VTT captions (`amanatools/srt-subtitles-generator`) Actor

Turn audio or video into ready-to-use .srt and .vtt subtitle files. Whisper runs inside the Actor — no API key. Cues are re-segmented to your characters-per-line, lines-per-cue and max-duration limits, with a sync offset. 99+ languages. Failed or silent files are never billed.

- **URL**: https://apify.com/amanatools/srt-subtitles-generator.md
- **Developed by:** [Dos](https://apify.com/amanatools) (community)
- **Categories:** AI, Videos, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#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

## SRT Subtitles Generator — video & audio to SRT/VTT captions

Send audio or video URLs and get back **finished `.srt` and `.vtt` files**,
ready to drop into a player, an editor or a CDN. Whisper runs *inside* the
Actor (faster-whisper, int8, CPU), so there is **no API key, no external
service, and no per-provider markup.** 99 languages, auto-detected. Optional
translation to English.

**The point of this Actor is the cues, not the transcription.** Raw Whisper
segments are transcription units — they routinely run 15–20 seconds and 200+
characters, which is unreadable as an on-screen caption. This Actor
re-segments the transcript into real subtitle cues that obey a
characters-per-line limit, a lines-per-cue limit and a maximum cue duration,
splitting on word boundaries and using per-word timings so the cut lands
where the speaker actually pauses.

### What you get

One dataset item per media file, plus the files themselves in the run's
key-value store:

```json
{
  "url": "https://example.com/interview.mp4",
  "status": "ok",
  "srt_url": "https://api.apify.com/v2/key-value-stores/…/001-interview.mp4.srt",
  "vtt_url": "https://api.apify.com/v2/key-value-stores/…/001-interview.mp4.vtt",
  "language": "en",
  "duration_seconds": 612.4,
  "billed_minutes": 11,
  "n_cues": 138,
  "n_words": 1642,
  "cues": [
    { "start": 0.0, "end": 3.24, "text": "Welcome back to the show.",
      "lines": ["Welcome back to the show."] }
  ],
  "srt": "1\n00:00:00,000 --> 00:00:03,240\nWelcome back to the show.\n",
  "vtt": "WEBVTT\n\n00:00:00.000 --> 00:00:03.240\nWelcome back to the show.\n"
}
```

### Subtitle shape

| Field | Default | What it does |
|---|---|---|
| `max_chars_per_line` | `42` | broadcast width; use `32` for narrow mobile players |
| `max_lines_per_cue` | `2` | `1` is common for social video |
| `max_cue_seconds` | `6` | longer cues are split on a word boundary |
| `min_cue_seconds` | `1` | stretches flash-cues, never into the next one |
| `offset_seconds` | `0` | shift every timestamp; negative pulls subtitles earlier |

Every word survives re-segmentation — splitting never drops or truncates
text. A single word longer than the line width gets its own overlong line
rather than being cut in half.

`offset_seconds` is the fix for a caption track that drifts against the
video: set `-1.5` to pull everything 1.5 seconds earlier. Timestamps never go
negative.

### Sources

Direct media files — mp3, m4a, aac, ogg, opus, wav, flac, mp4, webm and
anything else FFmpeg can decode. Up to 250 MB per file, 25 files per run.

Not supported, deliberately: downloading from YouTube, TikTok or other
social platforms. Send direct media files you have the right to process.

### Pricing — pay only for delivered subtitles

| Event | Price | Charged when |
|---|---|---|
| `subtitles-generated` | $0.005 | a file produced at least one cue |
| `transcription-minute` | $0.008 | per started minute of audio transcribed |

A 10-minute video costs **$0.085**. A one-hour talk costs **$0.485**.

**There is no actor-start fee** — a start fee bills you for failed runs, and
this family never charges one.

The billing rule is deliberate: **files that fail to download, cannot be
decoded, or contain no recognizable speech are never billed — not even the
per-file fee.** If you got no subtitles, you pay nothing. Your cost ceiling
per file = $0.005 + $0.008 × *Max minutes per file* (default 120).

### Transcription options

| Field | Default | Notes |
|---|---|---|
| `model` | `base` | `tiny` / `base` / `small` — accuracy vs speed |
| `language` | `auto` | ISO code (`en`, `ar`, `es`, …) skips detection |
| `task` | `transcribe` | `translate` = English subtitles from any language |
| `vad_filter` | `true` | skip silence — faster, fewer hallucinated captions |
| `max_minutes_per_file` | `120` | hard cost/length cap; longer files flagged `truncated` |

`tiny` and `base` are baked into the image and start instantly; `small`
downloads once at the start of the run (~1 minute extra).

Word-level timestamps are always computed — they are what makes cue
boundaries land on real word edges, so there is no option to turn them off.

### Notes on quality

- For noisy audio, strong accents or lower-resource languages, switch to
  `small` — the most accurate model offered here.
- Set `language` explicitly for very short clips; auto-detection has less
  signal to work with under ~10 seconds.
- Narrower lines mean more cues, not lost words: at `max_chars_per_line: 24`
  the same speech simply splits more often.

### Limits, stated plainly

- CPU transcription: roughly 5–15 seconds of processing per audio minute
  with `base`, more with `small`. The default 60-minute run timeout
  accommodates a full 2-hour file with `base`; raise it for `small` on very
  long media.
- 250 MB per file, 25 URLs per run, 480 minutes max per file.
- Live streams are not supported — the URL must be a finite file.
- Cue text is not re-punctuated or re-cased beyond what Whisper produces.

### The family

| Actor | Use it for |
|---|---|
| **SRT Subtitles Generator** (this one) | media → finished .srt / .vtt caption files |
| [Whisper Transcriber](https://apify.com/amanatools/whisper-transcriber) | media or podcast feeds → transcript text |
| [PDF Text Extractor](https://apify.com/amanatools/pdf-text-extractor) | fast clean text from digital PDFs |
| [PDF OCR Extractor](https://apify.com/amanatools/pdf-ocr-extractor) | scanned PDFs, Arabic + English OCR |

### Issues and requests

Open an issue on the actor's Issues tab — I read all of them.

# Actor input Schema

## `media_urls` (type: `array`):

Audio/video file URLs (mp3, m4a, wav, ogg, flac, mp4, webm, …). Up to 25 per run. Note: social-platform pages (YouTube, TikTok, …) are not supported — send direct media files you have the right to process.

## `max_chars_per_line` (type: `integer`):

Maximum characters on one subtitle line. 42 is the usual broadcast width; 32 suits narrow mobile players.

## `max_lines_per_cue` (type: `integer`):

Maximum lines shown on screen at once. Two is standard; one is common for social video.

## `max_cue_seconds` (type: `integer`):

A cue longer than this is split on a word boundary. Keeps captions from sitting on screen too long.

## `min_cue_seconds` (type: `integer`):

Very short cues are stretched to at least this long, but never far enough to overlap the next cue. Set 0 to disable.

## `offset_seconds` (type: `number`):

Shift every timestamp by this many seconds — positive delays the subtitles, negative pulls them earlier. Use it when the track drifts against the video. Example: -1.5

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

Bigger models are more accurate and slower. 'base' is the sweet spot for clear speech; use 'small' for noisy audio, heavy accents or hard languages ('small' downloads on first use, adding ~1 minute once per run).

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

ISO code like 'en', 'ar', 'es', 'ru' — or 'auto' to detect. Setting it explicitly is slightly faster and more reliable for short clips.

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

'Transcribe' keeps the original language. 'Translate' produces English subtitles regardless of the spoken language.

## `vad_filter` (type: `boolean`):

Voice-activity detection skips silent stretches — faster and fewer hallucinated captions on quiet audio. Turn off only if speech is being missed.

## `max_minutes_per_file` (type: `integer`):

Hard cap on subtitled audio per file — this is also your cost ceiling: max spend per file = $0.005 + $0.008 x this number. Longer files are subtitled up to the cap and flagged 'truncated'.

## `include_failed_files` (type: `boolean`):

If enabled, files that fail to download or decode still appear in the dataset with their status and error.

## Actor input object example

```json
{
  "media_urls": [
    "https://raw.githubusercontent.com/openai/whisper/main/tests/jfk.flac"
  ],
  "max_chars_per_line": 42,
  "max_lines_per_cue": 2,
  "max_cue_seconds": 6,
  "min_cue_seconds": 1,
  "offset_seconds": 0,
  "model": "base",
  "language": "auto",
  "task": "transcribe",
  "vad_filter": true,
  "max_minutes_per_file": 120,
  "include_failed_files": true
}
```

# Actor output Schema

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

Default dataset: one item per file with srt\_url, vtt\_url, cues, srt, vtt, language and duration.

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

The key-value store holding every generated .srt and .vtt file.

# 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 = {
    "media_urls": [
        "https://raw.githubusercontent.com/openai/whisper/main/tests/jfk.flac"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("amanatools/srt-subtitles-generator").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 = { "media_urls": ["https://raw.githubusercontent.com/openai/whisper/main/tests/jfk.flac"] }

# Run the Actor and wait for it to finish
run = client.actor("amanatools/srt-subtitles-generator").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 '{
  "media_urls": [
    "https://raw.githubusercontent.com/openai/whisper/main/tests/jfk.flac"
  ]
}' |
apify call amanatools/srt-subtitles-generator --silent --output-dataset

```

## MCP server setup

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

```

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/NAtcz94x8eJNoDrEa/builds/caFNZJ5V5ClgfecUZ/openapi.json
