DEV Community

SteadyScrape
SteadyScrape

Posted on

Make YouTube Videos Searchable by What Was Said: Timestamped Transcripts to Jump Links (Python)

You remember a speaker explaining something in one of forty conference talks, but not which talk or where. YouTube's own search won't find a phrase inside a video. Transcripts will, and if you keep the timestamps, you can link to the exact second.

This post builds a small "search inside videos" tool:

  1. getting captions yourself, and why that breaks,
  2. pulling timestamped captions in bulk with a hosted Actor, with real output,
  3. building a phrase search that returns &t= jump links, plus cost and caveats.

The DIY version

Open-source transcript libraries call YouTube's internal caption endpoints. They work on a laptop, then start failing from cloud servers when requests get blocked, and they break when YouTube changes its page. The official Data API only lets you download captions for videos you own.

The hosted version

YouTube Transcript API: Captions with Timestamps returns the captions that already exist on public videos, whether creator-uploaded or YouTube auto-generated. Disclosure: I build this Actor at SteadyScrape. It doesn't do AI or speech-to-text transcription. If a video has no captions, you get a no_captions row with no transcript charge.

Input matching the current schema:

{
  "videos": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/9bZkp7q19f0"
  ],
  "includeSegments": true
}
Enter fullscreen mode Exit fullscreen mode

videos accepts watch, youtu.be, shorts, embed and live links, or plain 11-character IDs, up to 200 per run. Duplicates are removed.

Real output from a verified run (trimmed):

{
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "status": "ok",
  "language": null,
  "requestedLanguage": "en",
  "chars": 2089,
  "segmentCount": 61,
  "durationSec": 211.32,
  "segments": [
    {"start": 1.36, "end": 3.04, "text": "[♪♪♪]"},
    {"start": 18.64, "end": 21.88, "text": "♪ We're no strangers to love ♪"}
  ],
  "chargeEvent": "transcript"
}
Enter fullscreen mode Exit fullscreen mode

language is deliberately null: the data source doesn't reliably report which caption track it returned, so the Actor doesn't guess.

Phrase search with jump links

import os
from apify_client import ApifyClient

VIDEOS = open("talks.txt").read().split()      # one URL or ID per line

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("steady-scrape/youtube-transcripts").call(
    run_input={"videos": VIDEOS, "includeSegments": True})

segments, skipped = [], []
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["status"] != "ok":
        skipped.append((row.get("input"), row["status"]))
        continue
    for s in row["segments"]:
        segments.append((row["videoId"], s["start"], s["text"]))

def search(phrase):
    phrase = phrase.lower()
    for vid, start, text in segments:
        if phrase in text.lower():
            yield f"https://www.youtube.com/watch?v={vid}&t={int(start)}s  {text}"

for hit in search("rate limit"):
    print(hit)
print("no transcript:", skipped)
Enter fullscreen mode Exit fullscreen mode

Caption lines are short, so a phrase can be split across two segments. For better recall, join each segment with its neighbour before matching, or embed windows of about 30 seconds for semantic search in a RAG index. Store videoId and start with every chunk so each answer can cite a timestamp.

What it costs

From the Store pricing:

  • $0.005 per transcript returned (status ok)
  • $0.003 per run, only if the run returned at least one transcript
  • Apify's standard $0.00005 start event

So 40 talks that all return captions cost $0.20305 in one run (40 × $0.005 + $0.003 + $0.00005). Videos without captions, unavailable or invalid videos, timeouts and skipped videos incur no transcript charge, and a run with no transcripts doesn't pay the $0.003 run fee; Apify's $0.00005 start event still applies to every run.

Caveats

  • Captions only. Auto-generated captions contain recognition errors and usually no punctuation, so quote carefully.
  • no_captions is ambiguous. The data source often can't tell a video without captions from a deleted, private or mistyped ID. None of those incur a transcript charge.
  • No translation in this version.
  • Public videos only: no logins, cookies, or private or members-only videos.
  • Videos still pending after 5 minutes come back as timeout (no transcript charge) so you can retry them. The free Apify plan allows up to 10 videos per run.

Integrations

Run it via the Apify API, on a schedule (for example, new uploads from a channel list), or from Make, Zapier or n8n through Apify's integrations. AI agents can call it through the Apify MCP server.

SteadyScrape (steady-scrape) builds small, pay-per-result data tools on Apify.

Top comments (0)