# SRT to VTT Converter - Subtitles, Resync & Plain Text (`eliai/srt-to-vtt-converter`) Actor

Convert SRT to VTT or VTT to SRT, strip timings to a plain text transcript, and resync cues by any offset. Paste text or pass up to 50 file URLs. Handles optional VTT hours, comma vs dot decimals and NOTE blocks. $0.005 per file; files with no cues are free.

- **URL**: https://apify.com/eliai/srt-to-vtt-converter.md
- **Developed by:** [Broke to Built](https://apify.com/eliai) (community)
- **Categories:** Videos, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 converted subtitles

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

## SRT to VTT Converter — Subtitles, Resync & Plain Text

Convert **SRT to VTT**, **VTT to SRT**, or strip the timings entirely and get a **plain text
transcript**. Paste the subtitle text or pass file URLs in bulk. Cues that run early or late can be
**resynced by any offset** in the same pass.

No account anywhere, no upload step, no API key. **$0.005 per subtitle file converted** — a file
that produces no cues is recorded and **not charged**.

### What it does

| You have | You want | This does it |
|---|---|---|
| `.srt` from a transcription tool | `.vtt` for an HTML5 `<track>` or YouTube | `outputFormat: "vtt"` |
| `.vtt` from a browser player | `.srt` for Premiere, Resolve, VLC | `outputFormat: "srt"` |
| Either | A readable transcript for a description, blog post or summary | `outputFormat: "text"` |
| Subtitles 2.5s out of sync | The same file, corrected | `shiftSeconds: -2` |

### Input

```json
{
  "subtitle": "1\n00:00:01,000 --> 00:00:04,000\nWelcome to the show.\n",
  "outputFormat": "vtt",
  "shiftSeconds": 0
}
```

Or in bulk, from URLs:

```json
{
  "urls": [
    "https://example.com/episode-01.srt",
    "https://example.com/episode-02.srt"
  ],
  "outputFormat": "vtt"
}
```

| Field | Type | Notes |
|---|---|---|
| `subtitle` | string | Raw `.srt` or `.vtt` contents. Source format is detected automatically. |
| `urls` | string\[] | Direct links to subtitle files, up to 50 per run, 5 MB each. |
| `outputFormat` | `vtt` | `srt` | `text` | Defaults to `vtt`. |
| `shiftSeconds` | integer | −3600 to 3600. Negative if subtitles run late. Defaults to 0. |

### Output

One record per file:

```json
{
  "ok": true,
  "source": { "type": "raw" },
  "sourceFormat": "srt",
  "outputFormat": "vtt",
  "cueCount": 2,
  "durationMs": 8000,
  "durationHms": "00:00:08",
  "shiftSeconds": 0,
  "characters": 118,
  "content": "WEBVTT\n\n00:00:01.000 --> 00:00:04.000\nWelcome to the show.\n\n…",
  "warnings": []
}
```

`content` is the finished subtitle file — save it straight to disk.

### What it gets right

Subtitle conversion looks trivial and is full of small traps. This handles them:

- **WebVTT hours are optional.** `00:01.000` is one second in VTT, not one minute. Treating the
  first field as hours turns a one-second cue into a one-minute one; the parser decides by field
  count, never by position.
- **Decimal separators differ.** SRT uses `00:00:01,000`, VTT uses `00:00:01.000`. Both are read,
  and output is always written in the target format's convention.
- **Short fractions.** `00:00:01.5` means 500 ms, not 5 ms.
- **VTT cue settings** (`align:start line:90%`) after the end time are parsed and dropped rather
  than corrupting the timing.
- **`NOTE`, `STYLE` and `REGION` blocks** are metadata and never leak into subtitle text.
- **CRLF line endings and a UTF-8 BOM** are handled.
- **Cue numbering is rebuilt** from 1 on SRT output, so a shifted or filtered file is still valid.
- **A negative shift clamps at zero** instead of producing negative timestamps, and the cue count
  stays the same — which is what you are billed on and what you diff against.
- **Plain text output strips** `<i>` tags and `{\an8}` positioning, because those are presentation,
  not words.

### Pricing

**$0.005 per subtitle file converted.** No start fee. A file that yields no cues — an HTML error
page, a mislabelled download — produces an `ok: false` record explaining why and **is not charged**.

### Limits

- 50 files per run, 5 MB per file.
- SubRip (`.srt`) and WebVTT (`.vtt`) in, the same two plus plain text out. SSA/ASS, TTML and SCC
  are not supported.
- Styling and positioning are preserved inside cue text on `srt`/`vtt` output, and removed on
  `text` output.

### FAQ

**Does it need an API key or an account?** No. Paste text or pass a URL.

**Can it fix subtitles that drift progressively?** No — `shiftSeconds` applies one constant offset.
Progressive drift is a frame-rate mismatch and needs rescaling, which this does not do.

**Is my subtitle text stored?** It goes to your own run's dataset on Apify, the same as any Actor
output, and nowhere else.

**Where do the subtitles come from?** If you need to generate them first, see
[Audio & Video to Text (Whisper)](https://apify.com/eliai/audio-transcriber-whisper) — it outputs
SRT, which this converts.

### Changelog

- **0.1 (2026-09-10)** — first release. SRT ↔ VTT, plain-text transcript, constant resync, bulk
  URLs. 30 local parser assertions cover the format traps listed above.

### Who uses it

- Video developers converting SRT captions to WebVTT for an HTML5 `<track>` element
- Podcast and video editors shifting subtitles that run two seconds late
- Course creators turning caption files into a plain-text transcript for show notes
- Localization vendors converting a batch of VTT files back to SRT for an editing tool
- Accessibility teams preparing caption files in the format their player requires

# Actor input Schema

## `subtitle` (type: `string`):

Paste the contents of an .srt or .vtt file. The source format is detected automatically. Leave empty if you are using a URL instead.

## `urls` (type: `array`):

One or more direct links to .srt or .vtt files. Up to 50 per run.

## `outputFormat` (type: `string`):

vtt = WebVTT for HTML5 video and YouTube. srt = SubRip for players and editors. text = plain transcript with no timings, ready for a description or a summary.

## `shiftSeconds` (type: `integer`):

Shift every cue in time. Use a negative number if the subtitles run late, positive if they run early. Leave at 0 for a straight conversion.

## Actor input object example

```json
{
  "subtitle": "1\n00:00:01,000 --> 00:00:04,000\nWelcome to the show.\n\n2\n00:00:05,250 --> 00:00:08,000\nToday we are converting subtitles.\n",
  "urls": [],
  "outputFormat": "vtt",
  "shiftSeconds": 0
}
```

# Actor output Schema

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

Every item this run produced, as JSON.

## `resultsCsv` (type: `string`):

The same items as a spreadsheet-ready CSV.

# 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 = {
    "subtitle": `1
00:00:01,000 --> 00:00:04,000
Welcome to the show.

2
00:00:05,250 --> 00:00:08,000
Today we are converting subtitles.`,
    "urls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("eliai/srt-to-vtt-converter").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 = {
    "subtitle": """1
00:00:01,000 --> 00:00:04,000
Welcome to the show.

2
00:00:05,250 --> 00:00:08,000
Today we are converting subtitles.
""",
    "urls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("eliai/srt-to-vtt-converter").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 '{
  "subtitle": "1\\n00:00:01,000 --> 00:00:04,000\\nWelcome to the show.\\n\\n2\\n00:00:05,250 --> 00:00:08,000\\nToday we are converting subtitles.\\n",
  "urls": []
}' |
apify call eliai/srt-to-vtt-converter --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,eliai/srt-to-vtt-converter"
        }
    }
}
```

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/VIyNIfOTKPtXviT7I/builds/9HhA2orN2xUI6Q33k/openapi.json
