Sentiment and Topic Analysis on YouTube Transcripts
Zied · 7/31/2026 · 7 min read
Sentiment and Topic Analysis on YouTube Transcripts
Most video analysis pipelines stop at metadata: title, view count, tags, and description. Those fields tell you almost nothing about what a creator actually said, what opinions they expressed, or how the tone of their content shifts over time. Transcripts fix that.
When you treat YouTube as a text corpus, the same NLP tools you use on articles, reviews, and forum posts become applicable to video content at scale. You can score sentiment per video, extract recurring topics across a channel, flag tone shifts in a creator's archive, or feed structured signals into recommendation and moderation systems. This guide walks through the full pipeline, from fetching and cleaning transcripts to running analysis and interpreting results.
Why Transcripts Beat Metadata for Understanding Real Content
A video title like "My Honest Review of [Product]" could mean anything. The transcript contains the actual sentences where the creator describes what broke, what impressed them, and whether they would buy again. Sentiment models trained on that text surface signals that metadata cannot.
The same gap exists for topic analysis. Tags are chosen for discoverability. The spoken content often covers tangents, counterarguments, and nuanced sub-topics that never make it into a tag. Keyword extraction from transcripts surfaces those latent themes.
For use cases like feeding YouTube transcripts to GPT and Claude, the quality of downstream analysis depends almost entirely on what you put in. A clean, complete transcript is worth far more than a description field.
Fetching and Cleaning Transcripts for NLP Pipelines
The YouTube Transcriber API returns transcripts as JSON arrays of timestamped segments. Fetching a single video is a GET request:
curl "https://getyoutubetranscriber.com/api/v2/transcript?video_url=dQw4w9WgXcQ" \
-H "Authorization: Bearer YOUR_API_KEY"
A typical response looks like this:
{
"transcript": [
{ "text": "Welcome back to the channel.", "start": 0.0, "duration": 2.1 },
{ "text": "Today we're looking at something a bit different.", "start": 2.1, "duration": 3.4 }
],
"language": "en"
}
For bulk analysis across a channel or playlist, use the /api/v2/transcripts-bulk endpoint (POST), which accepts up to 50 video IDs per request. That batching is critical when you are processing hundreds of videos and need to stay within reasonable API call budgets.
Before any NLP step, clean the raw JSON:
import re
def clean_transcript(segments):
"""Join segments and normalize text for NLP."""
full_text = " ".join(seg["text"] for seg in segments)
# Remove common filler and transcript artifacts
full_text = re.sub(r"\[.*?\]", "", full_text) # [Music], [Applause]
full_text = re.sub(r"\b(uh|um|hmm|like)\b", "", full_text, flags=re.IGNORECASE)
full_text = re.sub(r"\s+", " ", full_text).strip()
return full_text
Skipping this step is a common mistake. Filler words and transcript artifacts dilute keyword extraction results and introduce noise that lowers sentiment model confidence scores.
Running Sentiment Scoring and Keyword Extraction
For sentiment, the practical default is a DistilBERT-based model from Hugging Face. According to Label Your Data's 2026 benchmark, DistilBERT offers the same accuracy as full BERT while running 60% faster and using 40% less memory. That tradeoff matters when you are scoring thousands of transcript chunks.
VADER is tempting because it requires no GPU and runs instantly. It handles informal text reasonably well. But on domains beyond social media short-text, transformer models consistently outperform it by a wide margin. Use VADER for rapid prototyping or when you have no GPU budget at all. For anything production-facing, a fine-tuned transformer is worth the extra setup.
Here is a basic sentiment scoring function using the transformers library:
from transformers import pipeline
# Load once, reuse across all videos
sentiment_pipe = pipeline(
"text-classification",
model="distilbert-base-uncased-finetuned-sst-2-english",
truncation=True,
max_length=512
)
def score_sentiment(text, chunk_size=400):
"""Score long text by chunking and averaging."""
words = text.split()
chunks = [
" ".join(words[i:i+chunk_size])
for i in range(0, len(words), chunk_size)
]
results = sentiment_pipe(chunks)
# Weight by chunk length and average
total_words = len(words)
weighted_score = 0.0
for chunk, result in zip(chunks, results):
chunk_weight = len(chunk.split()) / total_words
score = result["score"] if result["label"] == "POSITIVE" else 1 - result["score"]
weighted_score += score * chunk_weight
return weighted_score # 0.0 (negative) to 1.0 (positive)
For keyword extraction, KeyBERT or simple TF-IDF across a corpus both work. TF-IDF is faster for large batches when you are comparing term frequency across many videos:
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
def extract_keywords(corpus, top_n=10):
"""Extract top keywords across a list of transcript strings."""
vectorizer = TfidfVectorizer(
stop_words="english",
ngram_range=(1, 2),
max_features=500
)
tfidf_matrix = vectorizer.fit_transform(corpus)
feature_names = vectorizer.get_feature_names_out()
scores = tfidf_matrix.sum(axis=0).A1
ranked = sorted(zip(feature_names, scores), key=lambda x: x[1], reverse=True)
return ranked[:top_n]
Aggregating Sentiment Across a Channel or Playlist
Single-video sentiment tells you how one video felt. Aggregated sentiment across a channel tells you how a creator's tone has shifted over months or years. That is where the real analytical value lives.
To pull all video IDs from a channel, first resolve the handle to a channel ID using /api/v2/channel-resolve, then paginate through /api/v2/channel/videos:
import requests
API_KEY = "YOUR_API_KEY"
BASE = "https://getyoutubetranscriber.com"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def get_all_channel_video_ids(channel_handle):
# Step 1: Resolve handle to channel_id
r = requests.get(f"{BASE}/api/v2/channel-resolve", params={"channel": channel_handle}, headers=HEADERS)
channel_id = r.json()["channel_id"]
# Step 2: Paginate through videos
video_ids = []
params = {"channel": channel_id}
while True:
r = requests.get(f"{BASE}/api/v2/channel/videos", params=params, headers=HEADERS)
data = r.json()
video_ids.extend([v["video_id"] for v in data.get("videos", [])])
continuation = data.get("continuation")
if not continuation:
break
params = {"channel": channel_id, "continuation": continuation}
return video_ids
Then fetch transcripts in batches of 50 and run your pipeline:
import time
def fetch_bulk_transcripts(video_ids):
results = {}
for i in range(0, len(video_ids), 50):
batch = video_ids[i:i+50]
r = requests.post(
f"{BASE}/api/v2/transcripts-bulk",
json={"video_ids": batch},
headers=HEADERS
)
for item in r.json().get("transcripts", []):
results[item["video_id"]] = item.get("transcript", [])
time.sleep(0.5) # be courteous to the API
return results
Example in Python with Pandas and a Transformer Model
Putting it all together into a pandas DataFrame gives you a structure you can sort, filter, and chart:
import pandas as pd
def analyze_channel(channel_handle):
video_ids = get_all_channel_video_ids(channel_handle)
transcripts = fetch_bulk_transcripts(video_ids)
rows = []
for vid_id, segments in transcripts.items():
if not segments:
continue
text = clean_transcript(segments)
sentiment = score_sentiment(text)
rows.append({"video_id": vid_id, "sentiment_score": sentiment, "word_count": len(text.split())})
df = pd.DataFrame(rows)
return df
df = analyze_channel("@mkbhd")
# Summary stats
print(df["sentiment_score"].describe())
print(df.nlargest(5, "sentiment_score")[["video_id", "sentiment_score"]])
print(df.nsmallest(5, "sentiment_score")[["video_id", "sentiment_score"]])
The output gives you a distribution of sentiment scores across the channel, the five most positive videos, and the five most negative. You can extend this with publish_date fields from the channel listing endpoint to plot tone over time, or merge with view count data to see whether sentiment correlates with engagement.
For playlist-level analysis, swap the channel video listing call for /api/v2/playlist-videos and the logic stays identical.
Interpreting Results and Avoiding Common Analysis Pitfalls
A few mistakes show up repeatedly in transcript NLP pipelines.
Trusting scores without context. A sentiment score of 0.3 (lean negative) on a product review video might reflect honest criticism. The same score on a cooking tutorial might mean the model is picking up on phrases like "this went wrong" or "don't do this," which are pedagogically intentional, not emotional. Always spot-check a handful of low-scoring videos manually before acting on aggregate signals.
Ignoring transcript language. The API returns a language field in every response. Feeding a Spanish transcript into an English-only sentiment model will return garbage. Check the language field and route multilingual content to an appropriate model like XLM-RoBERTa before scoring.
Treating missing transcripts as neutral. When a video has no transcript available, it should be excluded from aggregates rather than coded as neutral. Silence is not a sentiment.
Conflating topic frequency with topic importance. TF-IDF weights terms by how often they appear across documents, not by how central they are to any single video. A word that appears once in every video will score low even if it is thematically important. Supplement keyword extraction with named entity recognition (NER) to capture proper nouns, product names, and people that pure frequency methods miss.
Chunking without overlap. When splitting long transcripts for models with a 512-token limit, use a small overlap between chunks (50-100 tokens). Without overlap, sentences that span a chunk boundary get truncated, and those sentences often contain the strongest sentiment signals.
The data you produce from this pipeline is well-suited for downstream steps like summarization, topic clustering, or structured input to a language model. For the LLM integration side, the from transcript JSON to clean text: parsing tips post covers the preprocessing steps that make transcript text more reliable as LLM input.
Getting Started
The YouTube Transcriber API gives you 100 free credits on signup with no credit card required. That is enough to pull transcripts for 100 videos and run a first-pass sentiment analysis on a real channel before you commit to anything. The API documentation covers all the endpoints used in this guide, including bulk fetching, channel pagination, and playlist retrieval.
If you already have a list of video IDs, you can skip channel resolution entirely and go straight to bulk transcript fetching. The pipeline above is intentionally modular so you can drop in any video source.