# How do I build a RAG chatbot over YouTube video transcripts?

> Collect YouTube videos on a topic, fetch their timed transcripts, chunk by timestamp and answer with links to the exact second. Pipeline, costs and Python code.

Canonical page: https://okdata.online/answers/youtube-transcripts-rag-chatbot/

**Short answer: **Search YouTube for the videos you want ($0.004 per page of about 20), fetch each transcript with timestamps ($0.004 a video), cut the segments into time windows and embed them in any vector store. A 200-video knowledge base costs about $0.84 to collect, and every answer can link to the exact second it came from.

A fitness coaching company has 200 long videos of its own coaches answering questions, plus the best talks from others in the field. They want a chatbot that answers "how should I warm up for heavy squats?" with two sentences and a link that jumps to 14:32 in the right video. That is a RAG pipeline with one twist: keep the timestamps all the way through, because a link to the moment is worth more than a summary.

## The pipeline

1. **Collect video IDs.** Use [YouTube video search](/apis/youtube/videos/search/) with topic keywords, or with a channel's name plus a topic. There is no endpoint that lists every upload of a channel, so for your own channel copy the IDs from YouTube Studio.
2. **Fetch transcripts.** [YouTube video transcript](/apis/youtube/videos/transcript/get/) takes an 11-character video ID and an optional `lang`, and returns `segments` (start, end, text) plus the full `text`.
3. **Chunk by time.** Merge segments into windows of about 60 to 90 seconds. Keep `video_id`, title and the window's start second on every chunk.
4. **Embed and store** with whatever you already use: LlamaIndex, LangChain, pgvector, Qdrant.
5. **Answer with citations.** Build `https://youtu.be/<id>?t=<start>` from the chunk metadata and make the model cite it.

## Steps 1 to 3 in Python

```python
import json, os, requests

API = "https://api.okdata.online"
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}

def get(path, **params):
    body = requests.get(API + path, headers=H, params=params, timeout=90).json()
    if body["code"] != "ok":
        raise RuntimeError(f"{body['code']}: {body['message']}")
    return body["data"]

videos = get("/v1/youtube/videos/search", query="squat warm up coaching",
             fields="id,title,url")["items"]

with open("chunks.jsonl", "w", encoding="utf-8") as out:
    for v in videos:
        try:
            tr = get("/v1/youtube/videos/transcript/get", id=v["id"], lang="en")["item"]
        except RuntimeError as err:  # no captions, private, removed: not charged
            print("skip", v["id"], err)
            continue
        window, start = [], None
        for seg in tr["segments"]:
            start = seg["start"] if start is None else start
            window.append(seg["text"])
            if seg["end"] - start >= 75:
                out.write(json.dumps({"video_id": v["id"], "title": v["title"], "start": int(start),
                                      "text": " ".join(window)}, ensure_ascii=False) + "\n")
                window, start = [], None
        if window:
            out.write(json.dumps({"video_id": v["id"], "title": v["title"], "start": int(start),
                                  "text": " ".join(window)}, ensure_ascii=False) + "\n")
```

Each line of `chunks.jsonl` is ready to embed.

## What collection costs

| Knowledge base | Calls | Cost |
|---|---|---|
| 50 videos | 3 searches + 50 transcripts | $0.212 |
| 200 videos | 10 searches + 200 transcripts | $0.84 |
| 1,000 videos | 50 searches + 1,000 transcripts | $4.20 |

Updating is cheaper than building: once a month, fetch transcripts only for IDs you have not stored yet.

## Things that bite

- Auto-generated captions misspell names and jargon. Add a glossary to your answer prompt, or fix terms before embedding.
- Videos without captions fail and are not charged. If they matter, transcribe their audio separately.
- Ask for the language you embed in with `lang`; mixing languages in one index hurts retrieval.
- The transcripts belong to their creators. Answering questions internally is one thing; republishing them is another.

## Alternatives

LlamaIndex's YoutubeTranscriptReader and LangChain's YoutubeLoader are free and build on youtube-transcript-api. They work well from a laptop, while their issue trackers have many reports of them failing from cloud and serverless IPs. For your own channel, YouTube Studio and the YouTube Data API give you the captions you uploaded. Whisper on the audio covers videos with no captions at all, at the cost of downloading and compute. More on this job on the [transcripts page](/solutions/transcripts/).

## Endpoints used

- [YouTube video search](https://okdata.online/apis/youtube/videos/search/) `youtube.videos.search`: $0.004 per call · about 20 videos per page
- [YouTube video transcript](https://okdata.online/apis/youtube/videos/transcript/get/) `youtube.videos.transcript.get`: $0.004 per call

Updated 2026-11-12
