RAG over YouTube videos
Let users ask questions about a channel, a course or a conference, and answer with a link to the exact second where it was said. The hard part is getting clean, timestamped transcripts at scale; the rest is a standard RAG pipeline.
The pipeline
- List the videos of a channel or playlist with
/v1/videos. - Fetch transcripts 50 at a time with
/v1/batch. - Chunk the timestamped segments into passages of a few hundred words, keeping each passage's start time.
- Embed the passages and store them with
video_idandstart. - Retrieve and answer, citing
https://youtu.be/VIDEO_ID?t=START.
1-2. Get the transcripts
import os, requests
API = "https://api.transkrio.com/v1"
H = {"x-api-key": os.environ["TRANSKRIO_API_KEY"]}
videos = requests.get(f"{API}/videos", headers=H,
params={"url": "@lexfridman", "limit": 100}).json()["videos"]
urls = [v["url"] for v in videos]
transcripts = []
for i in range(0, len(urls), 50):
res = requests.post(f"{API}/batch", headers=H, timeout=300,
json={"urls": urls[i:i + 50]}).json()
transcripts += [t for t in res["results"] if t["ok"]]
Videos without captions come back with ok: false and cost nothing.
3. Chunk with timestamps
def chunks(t, max_words=400):
buf, start = [], None
for s in t["segments"]:
start = s["start"] if start is None else start
buf.append(s["text"])
if sum(len(x.split()) for x in buf) >= max_words:
yield {"video_id": t["video_id"], "start": int(start), "text": " ".join(buf)}
buf, start = [], None
if buf:
yield {"video_id": t["video_id"], "start": int(start), "text": " ".join(buf)}
passages = [c for t in transcripts for c in chunks(t)]
4-5. Embed, retrieve, cite
Use any embedding model and vector store (pgvector, Qdrant, Chroma, a NumPy array for a prototype). At query time, retrieve the top passages and put them in the prompt with their link:
context = "\n\n".join(
f"[https://youtu.be/{p['video_id']}?t={p['start']}]\n{p['text']}" for p in top_passages
)
prompt = f"Answer using only these excerpts and cite the links.\n\n{context}\n\nQuestion: {question}"
Tips
- Prefer manual captions (
is_generated: false) when both exist; Transkrio picks them by default. - Auto-generated captions have no punctuation. Embeddings handle it fine; for display, ask the LLM to clean the quoted passage.
- Store the transcript once. Re-fetching is cheap (cached), but you don't need to.
- Add podcasts to the same index:
/v1/videosand/v1/batchaccept podcast feeds too.
No-code version: the n8n node plus n8n's vector store nodes. Agent version: give your model the MCP server. More code in YouTube transcripts in Python.