$5 free credits when you sign up Claim now
Marlin 2b now available Test it!
MiniMax H3 now available! Test it!
Browse all Models
Browse all
Browse all
How to Transcribe a Twitch VOD from a Link
admin Oct 16, 2026 6 min read

How to Transcribe a Twitch VOD from a Link

A one-hour stream holds around 10,000 spoken words, and nobody scrubs through that to find the two minutes worth clipping. A transcript makes the VOD searchable, so you can jump to the moment a guest told the story you remember, or pull every line where the streamer mentioned a sponsor. The VOD also has an expiry date. Twitch keeps past broadcasts for 7 days on regular channels, 14 days for Affiliates and 60 days for Partners, and after that the link returns nothing to transcribe.

This guide transcribes a Twitch VOD straight from its link with the deAPI transcription endpoint. You send the URL and get back text in which every word has a timestamp and a speaker label. We tested it on two public VODs, and every price below comes from those runs or from the price endpoint.

Transcribe a Twitch VOD in Python

The endpoint takes the VOD URL as it appears in your browser, in the form twitch.tv/videos/<id>. The endpoint fetches the video from Twitch itself, and you don’t need a Twitch API key. Transcription runs as a background job, so the script submits it and polls until the result is ready:

import json
import os
import sys
import time

import requests

API = "<https://api.deapi.ai/api/v2>"
HEADERS = {
    "Authorization": f"Bearer {os.environ['DEAPI_API_KEY']}",
    "Accept": "application/json",
}

def transcribe(vod_url):
    """Send a VOD link and return the job id."""
    body = {
        "source_url": vod_url,
        "model": "WhisperLargeV3Ct2",
        "include_ts": True,
        "ts_level": "word",
        "diarize": True,
        "include_metadata": True,
        "return_result_in_response": True,
    }
    response = requests.post(f"{API}/audio/transcriptions", headers=HEADERS, json=body)
    response.raise_for_status()
    return response.json()["data"]["request_id"]

def wait(request_id, every=10):
    """Poll the job until it finishes. An hour of VOD took about six minutes."""
    while True:
        job = requests.get(f"{API}/jobs/{request_id}", headers=HEADERS).json()["data"]
        if job["status"] == "done":
            return job
        if job["status"] == "error":
            raise RuntimeError(job.get("error_reason"))
        time.sleep(every)

def turns(transcript):
    """Group words into speaker turns. Segments can span two speakers, words can't."""
    result = []
    for segment in transcript["segments"]:
        for word in segment["words"]:
            speaker = word.get("speaker", "?")
            if result and result[-1]["speaker"] == speaker:
                result[-1]["text"] += " " + word["word"]
                result[-1]["end"] = word["end"]
            else:
                result.append({"speaker": speaker, "start": word["start"],
                               "end": word["end"], "text": word["word"]})
    return result

def clock(seconds):
    m, s = divmod(int(seconds), 60)
    h, m = divmod(m, 60)
    return f"{h}:{m:02d}:{s:02d}"

if __name__ == "__main__":
    url = sys.argv[1]
    job = wait(transcribe(url))
    transcript = json.loads(job["result"])
    meta = job.get("metadata") or {}
    print(f"{meta.get('title')} | {clock(meta.get('duration_seconds') or 0)} | ${job['price']['amount']:.3f}")

    keyword = sys.argv[2].lower() if len(sys.argv) > 2 else None
    for turn in turns(transcript):
        if keyword is None or keyword in turn["text"].lower():
            print(f"[{clock(turn['start'])}] {turn['speaker']}: {turn['text']}")

Run it with python transcribe_vod.py <vod-url> for the full transcript, or add a keyword to print only the turns that contain it. We ran it on Let’s Chat Episode 13, a 57-minute podcast on the official Twitch channel in which Dan Clancy talks to the streamer 7suave about going viral. The job finished in 6 minutes 16 seconds and cost $0.386. It returned 10,134 words, with English detected at a confidence of 0.9995.

Timestamps and Speaker Labels

With ts_level set to word, every word carries its own start and end time, so a search hit points to the exact second rather than to a 30-second block. Segment timestamps are free, and they’re enough if you only need rough positions in the stream.

diarize labels each word with a speaker. On the Let’s Chat VOD it separated the host from the guest as SPEAKER_00 and SPEAKER_01. The transcript also comes split into segments, which averaged 27 seconds in our run, and a segment that long often holds both sides of a quick exchange. That’s why the script builds speaker turns from words, and the hour came back as 338 of them. Thirteen short turns, most of them one to five words of crosstalk, went to speakers who don’t exist (SPEAKER_02 to SPEAKER_04), so filter out labels that hold only a handful of words. The labels are anonymous, and you map them to names yourself. For more on how speaker labels work, see our guide to Whisper Large V3 CT2.

Proper names need a check. In a shorter test on an 11-minute TwitchDev VOD with developers from Larian Studios, the transcript gave their forum address as “larry.com“. If your streams name products, guests or games, check those words before you publish the text.

Stream Metadata

include_metadata adds the VOD’s title, upload date, view count, thumbnail and duration to the job result for $0.005 per job. For the Let’s Chat episode it returned the full title, 42,832 views and a duration of 3,454 seconds. Twitch doesn’t expose every field, so fields such as the channel name and like count come back as null. Keep the metadata with the transcript and you can sort a backlog of VODs by date or by views without another call.

Kick VODs

Kick works the same way. Swap the Twitch URL for a Kick VOD link in the form kick.com/<channel>/videos/<id>, and leave the rest of the request as it is. Kick VODs are priced at the same hourly rate.

What a Transcript Misses

A transcript covers only what someone says out loud. The Let’s Chat VOD opens with almost ten minutes of a countdown and event promos before anyone speaks, so its transcript starts at 9:59. Gameplay without commentary drops out for the same reason.

For those parts, the same VOD link can go to a second endpoint for video understanding. Marlin 2B returns a timestamped timeline of what happens in the video, at $0.28 per hour of Twitch VOD, and its clock lines up with the transcript’s. We cover how to combine the two in How to Analyze Videos with AI.

Price per Hour of VOD

Twitch VODs are priced by length. Segment timestamps are included in the base price, while word timestamps and speaker labels each switch the job to the higher rate.

VOD lengthSegment timestampsWord timestamps or speaker labels
1 hour$0.26$0.40
2 hours$0.52$0.79
3 hours$0.78$1.17

Metadata adds $0.005 per job. To check the price of a job before you send it, call POST /api/v2/audio/transcriptions/price with the VOD’s duration_seconds and platform: "twitch".

Twitch VOD Transcription at Scale

Above 1,000 hours of video a month, we offer volume pricing with an SLA, and your jobs can run on a dedicated worker. Tell us the platforms and the hours of video you process a month on the sales page. For the API reference and the full list of parameters, see Twitch & Kick VOD Transcription API.

Transcribe Your First VOD

A new account comes with $5 in credit, which covers about 19 hours of Twitch VOD with segment timestamps or 12 hours with word timestamps and speaker labels. Paste a VOD link into the transcription playground, or talk to sales if you run it at volume.

No subscription No credit card required

Start building with AI in under a minute

Access all models from this article through a single REST API. Start with $5 free credits — no subscription, no credit card.

Migration assistance available talk to an engineer