youtube-search
YouTube Search Skill
Autonomous YouTube data retrieval for agents. No user intervention required.
Method Selection Guide
Choose based on what's configured in the project environment:
| Situation | Best Method |
|---|---|
| Deep scraping needed (default) | Method E – yt-dlp (environment-dependent) |
| No API keys available | Method A – web_search built-in tool |
YOUTUBE_API_KEY set |
Method B – YouTube Data API v3 (richest data) |
SERPAPI_KEY set |
Method C – SerpAPI YouTube engine |
| Video ID known, need transcript | Method D – youtube-transcript-api |
Start with Method A if you're unsure — it requires nothing and always works.
Method A: web_search tool (Zero Setup — Always Available)
Use the built-in web_search tool. Works without any API keys.
Video Search
web_search("site:youtube.com <your query>")
Channel Search
web_search("site:youtube.com/channel <channel name> OR site:youtube.com/@<handle>")
Advanced Filters via Query
# Recent videos (last year)
web_search("site:youtube.com <query> 2024 OR 2025")
# Tutorial videos
web_search("site:youtube.com <topic> tutorial OR guide OR обзор")
# Specific language
web_search("site:youtube.com <query> на русском")
What you get from web_search
- Video title
- Channel name
- URL (extract video ID:
youtube.com/watch?v=VIDEO_ID) - Snippet/description excerpt
- Sometimes view count and publish date (in snippet)
Extract Video ID from URL
import re
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
video_id = re.search(r'v=([^&]+)', url).group(1)
# or from youtu.be links:
video_id = re.search(r'youtu\.be/([^?]+)', url).group(1)
Limitation: No structured JSON, metadata is text-parsed. For richer data, use Method B.
Method B: YouTube Data API v3 (Recommended for Production)
Requires: YOUTUBE_API_KEY environment variable (free, 10,000 units/day quota).
Get key: https://console.cloud.google.com → Enable "YouTube Data API v3" → Create API key.
Search Videos
import requests, os
API_KEY = os.environ.get("YOUTUBE_API_KEY")
BASE = "https://www.googleapis.com/youtube/v3"
def youtube_search(query, max_results=10, order="relevance",
video_duration=None, published_after=None, lang=None):
"""
order: relevance | date | viewCount | rating | title
video_duration: short (<4min) | medium (4-20min) | long (>20min)
published_after: ISO 8601 e.g. "2024-01-01T00:00:00Z"
lang: ISO 639-1 e.g. "ru", "en"
"""
params = {
"part": "snippet",
"q": query,
"maxResults": max_results,
"type": "video",
"order": order,
"key": API_KEY,
}
if video_duration:
params["videoDuration"] = video_duration
if published_after:
params["publishedAfter"] = published_after
if lang:
params["relevanceLanguage"] = lang
r = requests.get(f"{BASE}/search", params=params)
r.raise_for_status()
items = r.json().get("items", [])
return [{
"video_id": item["id"]["videoId"],
"title": item["snippet"]["title"],
"channel": item["snippet"]["channelTitle"],
"channel_id": item["snippet"]["channelId"],
"description": item["snippet"]["description"],
"published_at": item["snippet"]["publishedAt"],
"thumbnail": item["snippet"]["thumbnails"]["high"]["url"],
"url": f"https://youtube.com/watch?v={item['id']['videoId']}"
} for item in items if item["id"].get("videoId")]
Get Video Statistics (views, likes, duration)
def get_video_stats(video_ids: list):
"""Pass list of video IDs, get stats back. Costs 1 quota unit per call."""
ids = ",".join(video_ids[:50]) # max 50 per request
params = {
"part": "statistics,contentDetails,snippet",
"id": ids,
"key": API_KEY,
}
r = requests.get(f"{BASE}/videos", params=params)
r.raise_for_status()
results = []
for item in r.json().get("items", []):
stats = item.get("statistics", {})
content = item.get("contentDetails", {})
results.append({
"video_id": item["id"],
"title": item["snippet"]["title"],
"views": int(stats.get("viewCount", 0)),
"likes": int(stats.get("likeCount", 0)),
"comments": int(stats.get("commentCount", 0)),
"duration_iso": content.get("duration"), # e.g. "PT5M30S"
"tags": item["snippet"].get("tags", []),
})
return results
Parse ISO 8601 Duration
import re
def parse_duration(iso_duration):
"""Convert PT5M30S → 330 seconds"""
match = re.match(r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?', iso_duration)
if not match: return 0
h, m, s = [int(x or 0) for x in match.groups()]
return h * 3600 + m * 60 + s
Channel Search & Stats
def search_channel(channel_name, max_results=5):
params = {
"part": "snippet",
"q": channel_name,
"type": "channel",
"maxResults": max_results,
"key": API_KEY,
}
r = requests.get(f"{BASE}/search", params=params)
channel_ids = [item["id"]["channelId"] for item in r.json().get("items", [])]
# Get channel stats
params2 = {"part": "statistics,snippet", "id": ",".join(channel_ids), "key": API_KEY}
r2 = requests.get(f"{BASE}/channels", params=params2)
return [{
"channel_id": ch["id"],
"name": ch["snippet"]["title"],
"subscribers": int(ch["statistics"].get("subscriberCount", 0)),
"total_views": int(ch["statistics"].get("viewCount", 0)),
"video_count": int(ch["statistics"].get("videoCount", 0)),
"url": f"https://youtube.com/channel/{ch['id']}"
} for ch in r2.json().get("items", [])]
Quota Costs (10,000 units/day free)
| Operation | Cost |
|---|---|
| search.list | 100 units |
| videos.list (stats) | 1 unit |
| channels.list | 1 unit |
| playlists.list | 1 unit |
Tip: Search = 100 units. Get stats for 50 videos = 1 unit. Always batch videos.list calls.
Method C: SerpAPI (Structured Scraping, No Quota Issues)
Requires: SERPAPI_KEY environment variable.
Free tier: 100 searches/month. Paid plans available.
import requests, os
def serpapi_youtube_search(query, max_results=10, lang="ru"):
params = {
"engine": "youtube",
"search_query": query,
"api_key": os.environ.get("SERPAPI_KEY"),
"hl": lang, # interface language
}
r = requests.get("https://serpapi.com/search", params=params)
r.raise_for_status()
results = []
for item in r.json().get("video_results", [])[:max_results]:
results.append({
"title": item.get("title"),
"video_id": item.get("id") or item.get("link", "").split("v=")[-1],
"url": item.get("link"),
"channel": item.get("channel", {}).get("name"),
"views": item.get("views"),
"duration": item.get("length"),
"published": item.get("published_date"),
"description": item.get("description"),
"thumbnail": item.get("thumbnail", {}).get("static"),
})
return results
Advantage over YouTube API: Returns views, duration, publish date directly from search — no extra API calls needed.
Method D: youtube-transcript-api (Transcripts by Video ID)
Requires: pip install youtube-transcript-api --break-system-packages
No API key needed.
from youtube_transcript_api import YouTubeTranscriptApi, TranscriptsDisabled, NoTranscriptFound
def get_transcript(video_id, languages=["ru", "en"]):
"""
Returns full transcript as string.
languages: preference order, falls back to auto-generated.
"""
try:
transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)
# Try preferred languages first
try:
transcript = transcript_list.find_transcript(languages)
except NoTranscriptFound:
# Fall back to any available
transcript = transcript_list.find_generated_transcript(
transcript_list._generated_transcripts.keys()
)
entries = transcript.fetch()
full_text = " ".join([e["text"] for e in entries])
return {
"video_id": video_id,
"language": transcript.language_code,
"is_generated": transcript.is_generated,
"text": full_text,
"entries": entries # list of {text, start, duration}
}
except TranscriptsDisabled:
return {"error": "Transcripts disabled for this video"}
except Exception as e:
return {"error": str(e)}
def get_available_languages(video_id):
"""List all available transcript languages for a video."""
tl = YouTubeTranscriptApi.list_transcripts(video_id)
return [{"code": t.language_code, "name": t.language, "generated": t.is_generated}
for t in tl]
Use case: After finding video IDs via Method A or B, extract full text content for analysis, summarization, or content research.
Method E: yt-dlp (Deep Metadata + Transcripts)
Requires: pip install yt-dlp --break-system-packages
No API key. May be blocked in sandboxed environments — test first.
import yt_dlp, json
def ytdlp_search(query, max_results=10):
"""Search YouTube with yt-dlp. Returns rich metadata."""
ydl_opts = {
"quiet": True,
"no_warnings": True,
"extract_flat": True,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
results = ydl.extract_info(f"ytsearch{max_results}:{query}", download=False)
return [{
"title": v.get("title"),
"video_id": v.get("id"),
"url": f"https://youtube.com/watch?v={v.get('id')}",
"duration": v.get("duration"),
"view_count": v.get("view_count"),
"channel": v.get("channel"),
"upload_date": v.get("upload_date"),
} for v in results.get("entries", []) if v]
def ytdlp_get_video_info(video_url):
"""Get full metadata for a single video."""
ydl_opts = {"quiet": True, "no_warnings": True}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(video_url, download=False)
return info
def ytdlp_get_subtitles(video_url, lang="ru"):
"""Download and return subtitle text."""
import tempfile, os
with tempfile.TemporaryDirectory() as tmpdir:
ydl_opts = {
"quiet": True,
"writesubtitles": True,
"writeautomaticsub": True,
"subtitleslangs": [lang, "en"],
"skip_download": True,
"outtmpl": f"{tmpdir}/%(id)s.%(ext)s",
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([video_url])
for f in os.listdir(tmpdir):
if f.endswith(".vtt") or f.endswith(".srt"):
return open(os.path.join(tmpdir, f)).read()
return None
Note: yt-dlp makes direct requests to YouTube — may be blocked in restricted network environments. Always test with a quick yt-dlp --version call first.
Recommended Workflow for Automation Projects
Pattern 1: Competitor/Topic Research
# 1. Search for videos
results = youtube_search("AI сервисы обзор", max_results=20,
order="viewCount", lang="ru")
# 2. Enrich with stats
video_ids = [v["video_id"] for v in results]
stats = get_video_stats(video_ids)
# 3. Get transcripts for top videos
top_videos = sorted(stats, key=lambda x: x["views"], reverse=True)[:5]
for v in top_videos:
transcript = get_transcript(v["video_id"], languages=["ru"])
# analyze, summarize, extract keywords...
Pattern 2: Zero-Config Content Discovery (web_search only)
# No setup required - use built-in web_search tool
# web_search("site:youtube.com AI сервисы обзор 2025")
# Parse results, extract video IDs, then use transcript API if needed
Pattern 3: Channel Monitoring
# Find channel
channels = search_channel("название канала")
channel_id = channels[0]["channel_id"]
# Get latest videos from channel
params = {
"part": "snippet",
"channelId": channel_id,
"order": "date",
"maxResults": 10,
"type": "video",
"key": API_KEY,
}
r = requests.get(f"{BASE}/search", params=params)
Environment Setup Checklist
# Required for Method B (YouTube Data API)
export YOUTUBE_API_KEY="AIza..."
# Required for Method C (SerpAPI)
export SERPAPI_KEY="..."
# Required for Methods D & E (Python libraries)
pip install youtube-transcript-api yt-dlp --break-system-packages
See Also
references/youtube-api-quota.md— Quota optimization strategiesreferences/parsing-examples.md— Real-world parsing examples for Russian-language content