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:

  1. API Mode (recommended) — uses YouTube Data API v3 via curl. Requires a free YOUTUBE_API_KEY. Structured, reliable, and quota-managed.
  2. yt-dlp Mode (no API key) — uses yt-dlp for 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

  • curl and jq installed (standard on most systems)
  • yt-dlp installed: brew install yt-dlp or pip install yt-dlp
  • (Optional but recommended) YOUTUBE_API_KEY environment 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:

ResultMeaningAction
curl: not foundcurl is missingAsk user to install curl
jq: not foundjq is missingAsk user to run brew install jq (macOS) or apt install jq (Linux)
yt-dlp: not foundyt-dlp is NOT installedYou 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 SETNo API key configuredYou must use yt-dlp mode (if yt-dlp is installed). Tell the user that API mode is unavailable.
YOUTUBE_API_KEY: setAPI key is availableUse 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:

OperationCost
search.list100 units
videos.list1 unit
channels.list1 unit
commentThreads.list1 unit
playlistItems.list1 unit
playlists.list1 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=channel or &type=playlist to 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 (from nextPageToken in 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=VALUE to get the next page
  • prevPageToken — for going back
  • pageInfo.totalResults — total count
  • pageInfo.resultsPerPage — items per page

Common Patterns

Get all videos from a channel

  1. Get the channel's uploads playlist ID from the channel endpoint (uploads_playlist)
  2. Paginate through playlistItems using that playlist ID

Search then get details

  1. Search to find video IDs (100 quota units)
  2. 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 exhausted
  • 403 forbidden — API key invalid or API not enabled
  • 404 videoNotFound — video doesn't exist or is private
  • 400 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-comments can 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}]'