youtube-search

SKILL.md

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 Eyt-dlp (environment-dependent)
No API keys available Method Aweb_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 Dyoutube-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 strategies
  • references/parsing-examples.md — Real-world parsing examples for Russian-language content
Weekly Installs
3
GitHub Stars
1
First Seen
5 days ago
Installed on
opencode3
gemini-cli3
antigravity3
junie3
qwen-code3
claude-code3