How to get YouTube video transcripts from a keyword search
Search with YouTube video search, then fetch each chosen video's captions with YouTube video transcript. The worked example is 6 calls and costs at most $0.024; a failed call is not charged, and a video without captions comes back not_found at no charge.
Updated
The job: you want to know what the popular videos on a topic actually say, not just their titles. Search YouTube, keep the videos worth reading, and pull their transcripts. You end with one JSON Lines file: per video, its title, channel, views, the language of the track you got, the full text for a model to read, and timed segments for quoting with a timestamp.
No Google API key, OAuth consent or proxy is involved. Each call is an HTTPS request with your key.
Step by step
Step 1: find the videos
Call YouTube video search with query. sort takes relevance (the default), latest or most_viewed. One call returns about 20 videos for $0.004, each with the 11-character id, title, url, published_at, the channel in author and metrics.views. Add view=full and each video carries ext.length_s, its length in seconds, which helps you skip Shorts when you want long explanations.
curl -G "https://api.okdata.online/v1/youtube/videos/search" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
--data-urlencode 'query=sourdough starter' \
-d sort=most_viewed -d view=full
Search results leave text empty and metrics.likes and metrics.comments null; YouTube video detail has them if you need them. published_at is worked out from labels such as "3 weeks ago", so treat it as approximate.
Step 2: fetch each transcript
Pass a video's id to YouTube video transcript. One call returns one transcript for $0.004.
langis your preferred language, such asen,esorja. It is a preference: the transcript's ownlangfield says which track came back, and it isundwhen the track's language cannot be told. Check it before you file the text under a language.segmentsis the list of timed pieces, each withstartandendin seconds and itstext. Use it for quotes, chapter notes, subtitles or links that jump to a moment (&t=plus the start in whole seconds).textis all segments joined into plain text. Hand this to a model when you want a summary.
curl -G "https://api.okdata.online/v1/youtube/videos/transcript/get" \
-H "Authorization: Bearer $OKDATA_API_KEY" \
-d id=Qm7rT2vXa9c -d lang=en
A video without captions returns not_found, which is free, so the bill is lower for every captionless video in your batch.
Step 3: put it together in Python
import json
import os
import requests
API = "https://api.okdata.online"
HEADERS = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
PICK = 5 # transcripts to fetch; each is one call
def get(path, **params):
return requests.get(API + path, headers=HEADERS, params=params, timeout=90).json()
found = get("/v1/youtube/videos/search", query="sourdough starter", sort="most_viewed", view="full")
if found["code"] != "ok":
raise RuntimeError(f"{found['code']}: {found['message']}")
videos = sorted(found["data"]["items"], key=lambda v: v["ext"]["length_s"] or 0, reverse=True)[:PICK]
with open("transcripts.jsonl", "w", encoding="utf-8") as out:
for v in videos:
body = get("/v1/youtube/videos/transcript/get", id=v["id"], lang="en")
if body["code"] == "not_found":
print(v["id"], "no captions, not charged")
continue
if body["code"] != "ok":
print(v["id"], body["code"], body["message"])
continue
t = body["data"]["item"]
out.write(json.dumps({
"id": v["id"], "title": v["title"], "channel": v["author"]["name"], "views": v["metrics"]["views"],
"lang": t["lang"], "text": t["text"], "segments": t["segments"],
}, ensure_ascii=False) + "\n")
A failed transcript is printed and skipped rather than stopping the run, because the other videos are still worth having.
Good to know
- No captions, no transcript. The endpoint reads caption tracks; it does not run speech-to-text. For a captionless video, use Whisper or another speech model on audio you are allowed to use. New uploads sometimes get automatic captions a day later.
- One language per call. Asking for a second language is a second charged call. Ask once, check
lang, and translate segment by segment yourself if needed; the timings stay valid. - Automatic or typed captions. The response does not say which kind of track it is. Treat automatic captions with care when you quote.
- The official API. YouTube's captions.download costs 200 quota units per call and requires permission to edit the video, so it serves your own channel, not other people's videos.
- Retries without paying twice. Send an
Idempotency-Keyheader, such astranscript-plus the video ID. A repeated request with the same key within 24 hours returns the stored answer with no new charge.
If a library such as youtube-transcript-api tells you transcripts are disabled, see what TranscriptsDisabled means. For hundreds of videos at once, see bulk YouTube transcripts.
What the example costs
One page of the most viewed YouTube videos about sourdough starters, then the English transcripts of the five longest
| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| YouTube video search | 1 | $0.004 | $0.004 |
| YouTube video transcript | 5 | $0.004 | $0.02 |
| Total | 6 | $0.024 |
Prices in force when this page was built. A failed call costs nothing.
In your AI (MCP)
Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.
SaySearch YouTube for sourdough starter, most viewed, one page. Take the five longest videos, get their English transcripts and summarise what each one recommends, with a timestamp for each point.
What the agent sends to the call tool first
{
"endpoint": "youtube.videos.search",
"params": {
"query": "claude code tutorial"
},
"idempotency_key": "<a new UUID>"
}