Convert SRT subtitles to WebVTT online
Turn an SRT subtitle file into WebVTT that web players accept: 00:00:01,250 becomes 00:00:01.250, the WEBVTT header is added and multiline captions stay intact, with a count of the cues produced. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Turn SubRip subtitle text into standards-friendly WebVTT without manually replacing punctuation or rebuilding cue blocks. The converter adds the required WEBVTT header, changes SRT timestamp commas to WebVTT decimal points, preserves multiline caption text, and reports how many cues were produced. Every timing line is validated before a result is returned, so a broken subtitle entry becomes a clear input error instead of an unreliable caption file that fails later in a browser or media workflow.
Move SRT captions into a WebVTT workflow
SRT and WebVTT look similar, but they are not interchangeable files. Web players normally expect a file beginning with the WEBVTT signature, while SRT commonly begins with a numbered cue. Their timestamps also use different millisecond separators: SRT writes a comma and WebVTT uses a period.
Paste or send the complete SRT document in the srt field. The converter normalizes common line endings, removes a leading Unicode byte-order mark when present, reads each cue in order, and emits one WebVTT document. Caption lines remain in their original order, including cues containing two or more lines.
Numeric SRT cue indexes are consumed as structure rather than copied into the WebVTT body, producing a compact output that browsers can load as a text track. The response includes the converted text in vtt and a cue count that is useful for quick checks in automated jobs. No upload, media decoding, or network lookup is involved.
Catch malformed timing before publishing
Subtitle failures are often subtle: one cue may use a period where the source format requires a comma, omit a millisecond digit, miss the arrow, or contain an impossible minute value. This capability validates every timing line against the SRT form HH:MM:SS,mmm --> HH:MM:SS,mmm. Minutes and seconds must stay between 00 and 59, milliseconds must contain exactly three digits, and an end timestamp cannot occur before its start timestamp.
When any entry violates those rules, the entire request returns an invalid-input error identifying the affected entry instead of quietly producing a partly broken file. Correct the reported SRT entry and submit the document again to obtain deterministic output.
Use deterministic output in sites and automation
The returned WebVTT text is ready to save with a .vtt extension and serve using the text/vtt media type. In an HTML video page, reference that file from a track element and choose the appropriate language and label in your application. The conversion is deterministic: identical input produces identical output, with Unix line endings, one blank line after the header, blank lines between cues, and a final newline.
That makes the result suitable for source control, repeatable content builds, migration scripts, and tests that compare generated caption assets. The capability focuses only on structural conversion. It does not translate dialogue, transcribe audio, retime cues, merge overlapping captions, or inspect a video file.
Keeping those concerns separate protects existing wording and timing while making format changes predictable.
What you can do with it
Prepare captions for HTML5 video
Convert an editor's SRT delivery into WebVTT before linking it from an HTML track element.
Validate subtitle exports in CI
Fail a content build when any cue contains a malformed timestamp instead of publishing broken captions.
Migrate a subtitle library
Generate stable WebVTT assets from existing SRT text as part of a repeatable media migration.
FAQ
What does the conversion change?
It adds the WEBVTT header, changes timestamp millisecond commas to periods, removes numeric SRT cue indexes, and preserves caption text.
What timing format is accepted?
Each cue must use HH:MM:SS,mmm --> HH:MM:SS,mmm, with valid minute and second values and exactly three millisecond digits.
Are multiline captions preserved?
Yes. Every caption line after a cue's timing line is retained in the same order.
Does this translate or retime subtitles?
No. It changes the container text format only; caption wording and valid timing values remain unchanged.
What if one timing line in my SRT file is broken?
The whole conversion stops and names the faulty entry instead of handing back a partly broken file. Correct that line, for example a missing arrow or a minute above 59, and convert the document again.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/video/subtitle-srt-to-vtt \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"srt":"1\n00:00:01,250 --> 00:00:03,500\nWelcome to the presentation.\n\n2\n00:00:04,000 --> 00:00:06,125\nLet us begin."}'const res = await fetch("https://api.kit.forhosting.com/video/subtitle-srt-to-vtt", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"srt": "1\n00:00:01,250 --> 00:00:03,500\nWelcome to the presentation.\n\n2\n00:00:04,000 --> 00:00:06,125\nLet us begin."
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/video/subtitle-srt-to-vtt",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"srt": "1\n00:00:01,250 --> 00:00:03,500\nWelcome to the presentation.\n\n2\n00:00:04,000 --> 00:00:06,125\nLet us begin."
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/video/subtitle-srt-to-vtt", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"srt":"1\\n00:00:01,250 --> 00:00:03,500\\nWelcome to the presentation.\\n\\n2\\n00:00:04,000 --> 00:00:06,125\\nLet us begin."}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"srt":"1\n00:00:01,250 --> 00:00:03,500\nWelcome to the presentation.\n\n2\n00:00:04,000 --> 00:00:06,125\nLet us begin."}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/video/subtitle-srt-to-vtt", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"srt": "1\n00:00:01,250 --> 00:00:03,500\nWelcome to the presentation.\n\n2\n00:00:04,000 --> 00:00:06,125\nLet us begin."
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "video.subtitle_srt_to_vtt",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 500 |
max_minutes | 60 |
max_megapixels | 3.9 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Catch malformed timing before publishing
It also rejects empty caption payloads and unexpected nonnumeric cue indexes. That fail-fast behavior is helpful in build pipelines because success means every emitted cue passed the same checks. It does not guess at ambiguous timestamps or repair corrupted timing, since an automatic guess could shift dialogue to the wrong scene.
Use deterministic output in sites and automation
You can test the conversion in the browser at no charge or call the API for $0.002 per item when a publishing system needs to transform subtitle documents programmatically.
Technical questions
What happens when one timing line is malformed?
The request fails with an invalid-input error that identifies the SRT entry. No partial WebVTT document is returned.
How much does the API conversion cost?
Each API request costs $0.002 per item. The browser version is available free on the page.