Scrape YouTube.
No API key needed.
Search videos, browse channels, fetch transcripts, pull comments, and scrape playlists using YouTube's InnerTube API. Sync + async. Python 3.10+.
Search
Search videos, channels, and shorts with filters for date, duration, type, and features.
Channels
Browse videos, streams, shorts, playlists, search within a channel, and get about info.
Transcripts
Fetch transcripts with language selection, translation, and export to SRT, VTT, JSON.
Comments
Get comments with likes, replies, author info, hearted status, and pagination.
Video Info
Full metadata: likes, chapters, AI summary, people mentioned, subscriber count.
No API Key
Uses YouTube's InnerTube internal API. No Google account or API key needed.
# Installation
# Core SDK pip install tubescrape # With CLI (click + rich) pip install tubescrape[cli] # With REST API (FastAPI) pip install tubescrape[api] # Everything pip install tubescrape[all]
Requires Python 3.10+. Only dependency is httpx.
# Quick Start
from tubescrape import YouTube yt = YouTube() # Search results = yt.search('python tutorial') # Channel videos videos = yt.get_channel_videos('@lexfridman') # Video info with likes, chapters, AI summary info = yt.get_video_info('https://youtube.com/watch?v=dQw4w9WgXcQ') # Transcript transcript = yt.get_transcript('dQw4w9WgXcQ') # Comments with replies comments = yt.get_comments('dQw4w9WgXcQ', replies=True) # All methods accept video/channel URLs, IDs, or @handles
yt.close() when done, or use as a context manager:
with YouTube() as yt: ...
# Video Search
Search YouTube and get videos, channels, and shorts in one call.
search(query, max_results=20) -> SearchResult
result = yt.search('machine learning', max_results=10) for v in result.videos: print(v.title, v.view_count, v.is_short) # Shorts are included with is_short=True shorts = [v for v in result.videos if v.is_short] # Channel results (when searching for channels) for c in result.channels: print(c.title, c.handle, c.is_verified)
Pass max_results=0 to get all available results (paginated automatically).
# Search Filters
Filter search results by type, date, duration, sort order, and features.
# Long videos uploaded today, sorted by views yt.search('podcast', type='video', duration='long', upload_date='today', sort_by='view_count') # 4K HDR content yt.search('nature', features=['4k', 'hdr']) # Search for channels yt.search('tech', type='channel')
| Parameter | Options |
|---|---|
| sort_by | relevance, upload_date, view_count, rating |
| upload_date | last_hour, today, this_week, this_month,
this_year |
| type | video, channel, playlist, movie |
| duration | short (<4min), medium (4-20min), long (>20min) |
| features | live, 4k, hd, subtitles, cc,
creative_commons, 360, vr180, 3d, hdr |
# Channel Search Results
When searching with type='channel', results include handle and verified status.
result = yt.search('lex fridman', type='channel') for ch in result.channels: print(ch.title) # "Lex Fridman" print(ch.handle) # "@lexfridman" print(ch.is_verified) # True print(ch.subscriber_count) # "5.01M subscribers"
# Channel Videos
Get uploads from a channel's Videos tab. Accepts channel IDs, @handles, or URLs.
get_channel_videos(channel, max_results=30) -> BrowseResult
result = yt.get_channel_videos('@lexfridman', max_results=5) print(result.channel) # "Lex Fridman" print(result.channel_id) # "UCSHZKyawb77ixDdsGog4iWA" for v in result.videos: print(v.title, v.duration, v.view_count)
get_channel_streams() for live content and get_channel_shorts() for shorts.
# Channel Streams
Fetch past broadcasts, premieres, and scheduled live streams from the Live tab.
get_channel_streams(channel, max_results=0) -> BrowseResult
streams = yt.get_channel_streams('@CNN', max_results=5) for v in streams.videos: print(v.title, v.is_live)
Returns the same BrowseResult with VideoResult objects. Currently live streams have
is_live=True.
# Channel Shorts
Get shorts from a channel's Shorts tab.
get_channel_shorts(channel, max_results=0) -> ShortsResult
shorts = yt.get_channel_shorts('@CNN', max_results=10) for s in shorts.shorts: print(s.title, s.view_count, s.url)
Returns ShortResult objects with video_id, title,
view_count, and thumbnail_url.
# Channel Playlists
List playlists from a channel's Playlists tab.
get_channel_playlists(channel) -> ChannelPlaylistsResult
result = yt.get_channel_playlists('@lexfridman') for p in result.playlists: print(p.title, p.video_count, p.url)
# Search Within Channel
Search for videos within a specific channel.
search_channel(channel, query, max_results=0) -> SearchResult
result = yt.search_channel('@lexfridman', 'elon musk') for v in result.videos: print(v.title)
# Channel About
Get channel description, country, join date, total views, and external links.
get_channel_about(channel) -> ChannelAbout
about = yt.get_channel_about('@ycombinator') print(about.title) # "Y Combinator" print(about.description) # Full channel description print(about.country) # "United States" print(about.joined_date) # "Oct 24, 2013" print(about.subscriber_count) # "2.32M subscribers" print(about.view_count) # "103,989,970 views" print(about.video_count) # "880 videos" for link in about.links: print(link.title, link.url) # "Apply to YC" "ycombinator.com/apply"
# Video Info
Get full video metadata including likes, chapters, AI summary, and people mentioned.
get_video_info(video, enrich=True) -> VideoInfo
info = yt.get_video_info('https://youtube.com/watch?v=dQw4w9WgXcQ') # Basic metadata info.title # Video title info.channel # Channel name info.description # Full description info.view_count # 119556 (int) info.duration_seconds # 2940 info.publish_date # "2026-07-26T13:32:05-07:00" info.category # "Science & Technology" info.keywords # ["YC", "Y Combinator"] # Enriched data (from /next endpoint, requires enrich=True) info.like_count # "3.8K" info.comment_count # 148 info.subscriber_count # "2.32M subscribers" info.date_text # "Jul 26, 2026" info.ai_summary # YouTube's AI-generated summary # Chapters for ch in info.chapters: print(ch.time_description, ch.start_seconds, ch.title) # People mentioned for p in info.people_mentioned: print(p.name, p.description)
enrich=False for a single API call with basic metadata only
(no likes, chapters, or AI summary). Default enrich=True uses 2 API calls.
# Transcripts
Fetch video transcripts with language selection, translation, and export to multiple formats.
get_transcript(video, languages=None, timestamps=True, translate_to=None) -> Transcript
# Basic transcript t = yt.get_transcript('dQw4w9WgXcQ') print(t.text) # Full text joined # With segments (timestamps) for s in t.segments: print(s.start, s.duration, s.text) # Specific language t = yt.get_transcript('video_id', languages=['es', 'en']) # Translate to another language t = yt.get_transcript('video_id', translate_to='fr') # Plain text (no timestamps) t = yt.get_transcript('video_id', timestamps=False)
list_transcripts(video) -> list[TranscriptListEntry]
tracks = yt.list_transcripts('dQw4w9WgXcQ') for t in tracks: print(t.language, t.language_code, t.is_generated, t.is_translatable)
Formatting & Saving
# Format as SRT, VTT, JSON, or text srt = YouTube.format_transcript(transcript, 'srt') vtt = YouTube.format_transcript(transcript, 'vtt') # Save to file (format inferred from extension) transcript.save('subtitles.srt') transcript.save('output.json') transcript.save('captions.vtt')
# Playlists
Fetch videos from any YouTube playlist with full pagination.
get_playlist(playlist, max_results=0) -> PlaylistResult
result = yt.get_playlist('PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf') # Also accepts full URLs result = yt.get_playlist('https://youtube.com/playlist?list=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf') print(result.title) # Playlist title print(result.channel) # Playlist owner for v in result.videos: print(v.title, v.channel, v.duration)
# Async Support
Every method has an async counterpart prefixed with a.
import asyncio from tubescrape import YouTube async def main(): async with YouTube() as yt: result = await yt.asearch('python') info = await yt.aget_video_info('dQw4w9WgXcQ') transcript = await yt.aget_transcript('dQw4w9WgXcQ') comments = await yt.aget_comments('dQw4w9WgXcQ') asyncio.run(main())
| Sync | Async |
|---|---|
| search() | asearch() |
| get_channel_videos() | aget_channel_videos() |
| get_channel_streams() | aget_channel_streams() |
| get_channel_shorts() | aget_channel_shorts() |
| get_channel_playlists() | aget_channel_playlists() |
| get_channel_about() | aget_channel_about() |
| search_channel() | asearch_channel() |
| get_video_info() | aget_video_info() |
| get_comments() | aget_comments() |
| get_transcript() | aget_transcript() |
| list_transcripts() | alist_transcripts() |
| get_playlist() | aget_playlist() |
# Proxies
Configure proxies for scraping at scale. Supports proxy rotation and separate transcript proxies.
# Single proxy yt = YouTube(proxy='http://user:pass@host:port') # Proxy rotation (round-robin) yt = YouTube(proxies=[ 'http://proxy1:8080', 'http://proxy2:8080', 'http://proxy3:8080', ]) # Separate residential proxies for transcripts # (YouTube's player endpoint is stricter about datacenter IPs) yt = YouTube( proxies=['http://dc-proxy:8080'], # search/browse transcript_proxies=['http://resi-proxy:8080'], # transcripts )
| Parameter | Description |
|---|---|
| proxy | Single proxy URL for all requests |
| proxies | List of proxy URLs for rotation |
| timeout | Request timeout in seconds (default: 30) |
| max_retries | Max retry attempts on failure (default: 3) |
| cookies | Additional cookies dict for all requests |
| transcript_proxy | Residential proxy for transcript endpoints |
| transcript_proxies | List of residential proxies for transcript rotation |
# URL Parsing
Extract IDs from any YouTube URL format. All methods accept URLs, IDs, or @handles directly, but these utilities are available if needed.
from tubescrape import YouTube # Video ID from any format YouTube.extract_video_id('https://youtube.com/watch?v=dQw4w9WgXcQ') # 'dQw4w9WgXcQ' YouTube.extract_video_id('https://youtu.be/dQw4w9WgXcQ') # 'dQw4w9WgXcQ' YouTube.extract_video_id('dQw4w9WgXcQ') # 'dQw4w9WgXcQ' # Channel ID YouTube.extract_channel_id('https://youtube.com/@lexfridman') # '@lexfridman' YouTube.extract_channel_id('UCxxxxxx') # 'UCxxxxxx' # Playlist ID YouTube.extract_playlist_id('https://youtube.com/playlist?list=PLxxx') # 'PLxxx'
# Pagination
All list methods support max_results. Set to 0 to get everything.
| Method | Default | Get all |
|---|---|---|
| search() | 20 | max_results=0 |
| get_channel_videos() | 30 | max_results=0 |
| get_channel_streams() | 0 (all) | default |
| get_channel_shorts() | 0 (all) | default |
| get_comments() | 20 | max_results=0 |
| get_playlist() | 0 (all) | default |
| search_channel() | 0 (all) | default |
# Serialization
Every model has a to_dict() method for JSON serialization. Output is sparse: fields
with empty or default values are excluded.
import json info = yt.get_video_info('dQw4w9WgXcQ') print(json.dumps(info.to_dict(), indent=2)) comments = yt.get_comments('dQw4w9WgXcQ') print(json.dumps(comments.to_dict(), indent=2))
# Comments
Fetch top-level comments with author info, likes, and optional replies.
get_comments(video, max_results=20, replies=False) -> CommentsResult
replies=Truetriggers one extra API call per comment that has replies.