youtube-connector
Access YouTube data using curl, jq, and yt-dlp. Use this skill whenever the user wants to search YouTube videos, get video details or statistics, fetch channel information, read video transcripts/captions, browse comments, or explore playlists. Also use when the user mentions YouTube in any research, analysis, or data-gathering context — even if they don't explicitly say "use YouTube API."
YouTube Connector
Access YouTube data from the command line. No MCP server needed.
This skill supports two modes:
- API Mode (recommended) — uses YouTube Data API v3 via
curl. Requires a freeYOUTUBE_API_KEY. Structured, reliable, and quota-managed. - yt-dlp Mode (no API key) — uses
yt-dlpfor everything. Zero configuration, but higher risk of IP-based rate limiting from YouTube.
Choose API Mode when YOUTUBE_API_KEY is set. Fall back to yt-dlp Mode when no API key is available.
Prerequisites
curlandjqinstalled (standard on most systems)yt-dlpinstalled:brew install yt-dlporpip install yt-dlp- (Optional but recommended)
YOUTUBE_API_KEYenvironment variable — get a free key from Google Cloud Console
IMPORTANT: Check Tools Before Running Any Command
Before executing ANY command in this skill, you MUST run these checks first:
# Check all required tools in one command
which curl && which jq && which yt-dlp && echo "---YOUTUBE_API_KEY: ${YOUTUBE_API_KEY:+set (${#YOUTUBE_API_KEY} chars)}${YOUTUBE_API_KEY:-NOT SET}"
How to interpret the results and what to do:
| Result | Meaning | Action |
|---|---|---|
curl: not found | curl is missing | Ask user to install curl |
jq: not found | jq is missing | Ask user to run brew install jq (macOS) or apt install jq (Linux) |
yt-dlp: not found | yt-dlp is NOT installed | You CANNOT use any yt-dlp command. Ask user to run brew install yt-dlp (macOS) or pip install yt-dlp. Do NOT attempt yt-dlp commands — they will fail with "command not found", not "rate limited". |
YOUTUBE_API_KEY: NOT SET | No API key configured | You must use yt-dlp mode (if yt-dlp is installed). Tell the user that API mode is unavailable. |
YOUTUBE_API_KEY: set | API key is available | Use API mode (recommended). |
Common mistake: If yt-dlp is not installed and you run yt-dlp commands, the shell returns "command not found" — this is NOT a rate limit or network error. Do not tell the user they are being rate-limited when the tool is simply not installed.
API Quota Awareness
YouTube Data API v3 has a default quota of 10,000 units/day. Costs vary by endpoint:
| Operation | Cost |
|---|---|
| search.list | 100 units |
| videos.list | 1 unit |
| channels.list | 1 unit |
| commentThreads.list | 1 unit |
| playlistItems.list | 1 unit |
| playlists.list | 1 unit |
Search is expensive. Prefer direct video/channel lookups when IDs are known. A single search costs 100x more than a detail lookup.
Base URL
All API requests use:
https://www.googleapis.com/youtube/v3
Endpoints
Search Videos
curl -s "https://www.googleapis.com/youtube/v3/search?part=snippet&q=QUERY&type=video&maxResults=10&key=$YOUTUBE_API_KEY" | jq '.items[] | {videoId: .id.videoId, title: .snippet.title, channel: .snippet.channelTitle, published: .snippet.publishedAt, description: .snippet.description}'
Additional search parameters:
&type=channelor&type=playlistto search for channels/playlists&order=date(default: relevance) — also:rating,viewCount&publishedAfter=2024-01-01T00:00:00Z— filter by date&channelId=CHANNEL_ID— search within a specific channel&maxResults=N— 1 to 50 (default 5)
Get Video Details
curl -s "https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics,contentDetails&id=VIDEO_ID&key=$YOUTUBE_API_KEY" | jq '.items[0] | {title: .snippet.title, channel: .snippet.channelTitle, published: .snippet.publishedAt, description: .snippet.description, duration: .contentDetails.duration, views: .statistics.viewCount, likes: .statistics.likeCount, comments: .statistics.commentCount}'
Multiple videos: &id=ID1,ID2,ID3 (comma-separated, up to 50).
The duration field is ISO 8601 format (e.g., PT1H2M30S = 1 hour, 2 minutes, 30 seconds).
Get Channel Info
By channel ID:
curl -s "https://www.googleapis.com/youtube/v3/channels?part=snippet,statistics,contentDetails&id=CHANNEL_ID&key=$YOUTUBE_API_KEY" | jq '.items[0] | {title: .snippet.title, description: .snippet.description, subscribers: .statistics.subscriberCount, videos: .statistics.videoCount, views: .statistics.viewCount, uploads_playlist: .contentDetails.relatedPlaylists.uploads}'
By handle (e.g., @mkbhd):
curl -s "https://www.googleapis.com/youtube/v3/channels?part=snippet,statistics,contentDetails&forHandle=@HANDLE&key=$YOUTUBE_API_KEY" | jq '.items[0] | {title: .snippet.title, description: .snippet.description, subscribers: .statistics.subscriberCount, videos: .statistics.videoCount, views: .statistics.viewCount, uploads_playlist: .contentDetails.relatedPlaylists.uploads}'
The uploads_playlist ID can be used with the playlist items endpoint to list all channel uploads.
Get Video Comments
curl -s "https://www.googleapis.com/youtube/v3/commentThreads?part=snippet,replies&videoId=VIDEO_ID&maxResults=20&order=relevance&key=$YOUTUBE_API_KEY" | jq '.items[] | {author: .snippet.topLevelComment.snippet.authorDisplayName, text: .snippet.topLevelComment.snippet.textDisplay, likes: .snippet.topLevelComment.snippet.likeCount, published: .snippet.topLevelComment.snippet.publishedAt, replyCount: .snippet.totalReplyCount}'
Parameters:
&order=relevance(default) or&order=time&maxResults=N— 1 to 100 (default 20)&pageToken=TOKEN— for pagination (fromnextPageTokenin response)
Get Playlist Items
curl -s "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=PLAYLIST_ID&maxResults=50&key=$YOUTUBE_API_KEY" | jq '.items[] | {position: .snippet.position, title: .snippet.title, videoId: .snippet.resourceId.videoId, channel: .snippet.channelTitle, published: .snippet.publishedAt}'
Use &pageToken=TOKEN for pagination (from nextPageToken in response).
List Channel Playlists
curl -s "https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&channelId=CHANNEL_ID&maxResults=50&key=$YOUTUBE_API_KEY" | jq '.items[] | {playlistId: .id, title: .snippet.title, description: .snippet.description, videoCount: .contentDetails.itemCount, published: .snippet.publishedAt}'
Get Video Transcript
Transcripts are not available via the Data API. Use yt-dlp instead:
yt-dlp --write-sub --write-auto-sub --sub-lang en --skip-download --sub-format json3 -o "/tmp/yt-sub" "https://www.youtube.com/watch?v=VIDEO_ID"
This downloads the subtitle file to /tmp/yt-sub.en.json3. Parse it:
cat /tmp/yt-sub.en.json3 | jq '[.events[] | select(.segs) | {time: (.tStartMs / 1000), text: ([.segs[].utf8] | join(""))}]'
For plain text only (no timestamps):
cat /tmp/yt-sub.en.json3 | jq '[.events[] | select(.segs) | [.segs[].utf8] | join("")] | join(" ")' -r
To list available subtitle languages:
yt-dlp --list-subs "https://www.youtube.com/watch?v=VIDEO_ID"
Clean up after reading:
rm -f /tmp/yt-sub.en.json3
Pagination
Endpoints that return lists support pagination via pageToken. The response includes:
nextPageToken— pass as&pageToken=VALUEto get the next pageprevPageToken— for going backpageInfo.totalResults— total countpageInfo.resultsPerPage— items per page
Common Patterns
Get all videos from a channel
- Get the channel's uploads playlist ID from the channel endpoint (
uploads_playlist) - Paginate through playlistItems using that playlist ID
Search then get details
- Search to find video IDs (100 quota units)
- Batch-fetch details with comma-separated IDs (1 quota unit for up to 50 videos)
Extract video ID from URL
YouTube URLs come in several forms. Extract the video ID:
# From various URL formats (works on both macOS and Linux)
echo "https://www.youtube.com/watch?v=dQw4w9WgXcQ" | sed -n 's/.*[?&]v=\([^&]*\).*/\1/p'
echo "https://youtu.be/dQw4w9WgXcQ" | sed -n 's/.*youtu\.be\/\([^?]*\).*/\1/p'
Error Handling
API errors return JSON with an error object:
curl -s "..." | jq '.error // empty | {code, message: .message, reason: .errors[0].reason}'
Common errors:
403 quotaExceeded— daily quota exhausted403 forbidden— API key invalid or API not enabled404 videoNotFound— video doesn't exist or is private400 invalidParameter— check parameter values
yt-dlp Mode (No API Key)
Prerequisite: Verify yt-dlp is installed before using ANY command below. Run which yt-dlp — if it returns "not found", you MUST ask the user to install it first (brew install yt-dlp or pip install yt-dlp). Do NOT proceed without it.
When YOUTUBE_API_KEY is not set, use yt-dlp for all operations. This avoids needing any API key but comes with trade-offs.
Risk Warning: yt-dlp scrapes YouTube directly without official API authorization. YouTube may:
- Rate limit or block your IP after too many requests in a short period
- Break without notice when YouTube changes its frontend (yt-dlp updates usually fix this within days)
- Return incomplete data compared to the official API (e.g., exact subscriber counts may be rounded)
For occasional/personal use this is fine. For heavy or production use, get a free API key instead.
Search Videos (yt-dlp)
yt-dlp "ytsearch10:QUERY" --dump-json --flat-playlist 2>/dev/null | jq -s '[.[] | {videoId: .id, title: .title, channel: .channel, views: .view_count, duration: .duration_string, url: .url}]'
Replace 10 in ytsearch10 with the desired number of results (max ~50).
Get Video Details (yt-dlp)
yt-dlp --dump-json --skip-download "https://www.youtube.com/watch?v=VIDEO_ID" 2>/dev/null | jq '{title, channel, upload_date, duration_string, view_count, like_count, comment_count, description: (.description[:500])}'
Get Channel Info (yt-dlp)
yt-dlp --dump-json --flat-playlist --playlist-items 0 "https://www.youtube.com/@HANDLE" 2>/dev/null | jq '{channel, channel_id, channel_url, channel_follower_count}'
List Channel Videos (yt-dlp)
yt-dlp --dump-json --flat-playlist "https://www.youtube.com/@HANDLE/videos" 2>/dev/null | jq -s '[.[:20] | .[] | {videoId: .id, title: .title, duration: .duration_string, views: .view_count}]'
Get Video Comments (yt-dlp)
yt-dlp --write-comments --dump-json --skip-download "https://www.youtube.com/watch?v=VIDEO_ID" 2>/dev/null | jq '[.comments[:20] | .[] | {author: .author, text: (.text[:500]), likes: .like_count, time: .timestamp}]'
Note:
--write-commentscan be very slow on videos with many comments. Consider limiting with the API mode for comment-heavy videos.
Get Video Transcript (yt-dlp)
Same as API mode — transcripts always use yt-dlp:
yt-dlp --write-sub --write-auto-sub --sub-lang en --skip-download --sub-format json3 -o "/tmp/yt-sub" "https://www.youtube.com/watch?v=VIDEO_ID"
cat /tmp/yt-sub.en.json3 | jq '[.events[] | select(.segs) | {time: (.tStartMs / 1000), text: ([.segs[].utf8] | join(""))}]'
rm -f /tmp/yt-sub.en.json3
Get Playlist Videos (yt-dlp)
yt-dlp --dump-json --flat-playlist "https://www.youtube.com/playlist?list=PLAYLIST_ID" 2>/dev/null | jq -s '[.[] | {videoId: .id, title: .title, duration: .duration_string, channel: .channel}]'