ytscrape — Free YouTube Scraper for Python
Free, open-source YouTube scraper library for Python. Scrape YouTube search results — videos, channels, playlists and Shorts — plus detailed video metadata, without an official YouTube Data API key and without any quota limits.
ytscrape is a free YouTube scraper and crawler for Python built on top of
the internal YouTube InnerTube API. Use it to search YouTube, extract
video, channel and playlist data, and fetch video metadata — all with
transparent pagination and no API key required. It is a pure-HTTP YouTube
data extractor with a simple, Pythonic interface and a clean, extensible
architecture, so whether you want to scrape YouTube videos, collect
channel data, mine YouTube search results, or build a YouTube
dataset, you can get started in just a few lines of code.
⚠️ This library talks to YouTube's private endpoints. Use it responsibly and at your own risk; the endpoints and
paramsvalues may change over time.
Table of Contents
- Why ytscrape?
- Features
- Installation
- Quick start
- ytscrape vs. YouTube Data API
- How it works
- Search filters
- Language & country
- Pagination
- Comments
- Use cases
- Command line
- License
Why ytscrape?
- ✅ Free & open source (MIT) — no paid plans, no sign-up, no rate-limit tiers.
- 🔑 No YouTube Data API key and no quota — nothing to register or manage.
- 🐍 Pure Python with full type hints and a tiny, dependency-light install.
- ⚡ Simple, Pythonic API — start scraping YouTube in just a few lines.
Features
- 🔑 No YouTube Data API key required and no quota to worry about.
- 📄 Transparent pagination — just iterate; continuation tokens are handled for you.
- 🎬 Fetch detailed video metadata from an id or any YouTube URL.
- 💬 Scrape all comments (and replies) of a video from an id or URL, with the same transparent pagination as search.
- 🔎 Search videos, channels and playlists with a clean
SearchFilterenum (no magicEgIQAQ==strings in your code). - 🌍 Language & country support — localise results with the
LanguageandCountryvalue objects (thin wrappers around raw ISO codes, no hard-coded lists) bundled in a smallLocaleobject; codes are validated withpycountry, so typos fail fast. - 🐍 Pythonic API with an extensible OOP design (facade + factory methods
- strategy) and full type hints — easy to build on.
- 🖥️ A tiny CLI:
python -m ytscrape ....
Installation
pip install ytscrape
or with uv:
uv add ytscrape
From source:
pip install .
Quick start
from ytscrape import YouTube
with YouTube() as yt:
for video in yt.search("python", max_results=5):
print(video.title)
📂 See more runnable examples in
examples/— searching videos/channels/playlists, fetching video details, pagination, localisation and error handling.
Need more control? Filter by type, search channels and fetch full video metadata:
from ytscrape import YouTube, SearchFilter
with YouTube() as yt:
# Search videos and iterate over as many pages as needed.
for video in yt.search("python", filter=SearchFilter.VIDEOS, max_results=20):
print(video.title, "-", video.url)
# Search channels.
for channel in yt.search("python", filter=SearchFilter.CHANNELS, max_results=5):
print(channel.title, channel.url)
# Fetch details for a single video (id or URL both work).
details = yt.video("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(details.title, details.channel, details.views)
# Collect all comments of a video (id or URL both work).
for comment in yt.comments(
"https://www.youtube.com/watch?v=dQw4w9WgXcQ", max_results=20
):
print(comment.author, "-", comment.text)
ytscrape vs. YouTube Data API
| Feature | ytscrape | YouTube Data API |
|---|---|---|
| API key | ❌ | ✅ |
| Quota | ❌ | ✅ |
| Pagination | ✅ | ✅ |
| Video metadata | ✅ | ✅ |
| Search | ✅ | ✅ |
(❌ = not needed / no limit, ✅ = required / applies.)
How it works
ytscrape is built on top of the private YouTube InnerTube API — the same
endpoints the YouTube web and mobile apps use internally.
- No browser.
- No Selenium.
- No Playwright.
- Pure HTTP.
Search filters
| Filter | Description |
|---|---|
SearchFilter.ALL |
Everything (default) |
SearchFilter.VIDEOS |
Videos only |
SearchFilter.CHANNELS |
Channels only |
SearchFilter.PLAYLISTS |
Playlists only |
SearchFilter.SHORTS |
Shorts only |
SearchFilter.MOVIES |
Movies only |
You can also pass the string form: yt.search("python", filter="videos").
Language & country
YouTube localises results by interface language (hl) and content region
(gl). Configure both when creating YouTube, passing plain ISO codes (or the
Language / Country value objects, which wrap the same codes):
from ytscrape import YouTube, Language, Country, Locale
# Just pass raw ISO codes — they are validated and normalised for you.
with YouTube(language="uk", region="UA") as yt:
for video in yt.search("музика", max_results=10):
print(video.title, video.url)
# The Language / Country value objects are equivalent (and reusable).
yt = YouTube(language=Language("de"), region=Country("DE"))
# Invalid codes are rejected early (validated with pycountry).
YouTube(region="XX") # ValueError: Unknown country code 'XX'. ...
# Or pass a ready-made Locale.
yt = YouTube(locale=Locale(language="fr", country="FR"))
print(yt.locale.language.code, yt.locale.country.code) # fr FR
Language and Country are thin, self-validating value objects around a raw
ISO 639-1 / ISO 3166-1 alpha-2 code — there is no hard-coded list of languages
or countries, so any valid code works. Codes are validated with
pycountry: an unknown language or
country code raises a ValueError instead of silently producing a broken
request. The chosen locale is sent both in the request context (hl / gl)
and as the Accept-Language HTTP header.
Pagination
Pagination is transparent — iterating over the result object automatically loads the next page:
results = yt.search("python")
for item in results: # loads pages on demand
print(item.title)
You can also page manually:
results = yt.search("python")
print(len(results.fetch_next_page())) # explicitly load one more page
print(results.has_more) # is there another page?
Use max_results to cap how many items you consume.
Comments
Collect the comments of a video with YouTube.comments(). Pass a video id or
any YouTube URL; the returned CommentThread is a lazy iterable that
transparently pages through every comment, just like search results:
from ytscrape import YouTube
with YouTube() as yt:
for comment in yt.comments("https://youtu.be/dQw4w9WgXcQ", max_results=50):
marker = " ↳" if comment.is_reply else "-"
print(f"{marker} {comment.author}: {comment.text}")
By default only top-level comments are collected. Pass
include_replies=True to also collect the replies of every thread; each
reply has is_reply=True and is yielded right after the comment it replies to:
with YouTube() as yt:
for comment in yt.comments("https://youtu.be/dQw4w9WgXcQ", include_replies=True):
marker = " ↳" if comment.is_reply else "-"
print(f"{marker} {comment.author}: {comment.text}")
Each Comment exposes:
| Field | Description |
|---|---|
comment_id |
Unique comment id. |
text |
The comment body. |
author |
Display name of the author. |
author_channel_id |
Channel id of the author (when available). |
author_thumbnail |
URL of the author's avatar. |
published |
Human-readable published time (e.g. 2 days ago). |
like_count |
Like count as an int (None when abbreviated, e.g. 1.2K). |
like_count_text |
Like count as YouTube renders it, keeping abbreviations (e.g. 1.2K, 894). |
reply_count |
Number of replies (top-level comments only). |
reply_count_text |
Reply count as a raw display string (e.g. 64). |
heart |
True if the video's creator hearted the comment. |
is_reply |
True for replies, False for top-level comments. |
Omit max_results to iterate over all comments; pagination is handled for
you. When include_replies=True, max_results counts replies too. A
ParseError is raised if the video has comments disabled.
Collecting every comment (sort order)
YouTube's default "Top comments" view quietly hides some comments
(less relevant ones and "potential spam"), so collecting in that order will
appear to skip comments. To get every comment, switch the sort order to
"Newest first" with the sort parameter:
from ytscrape import YouTube, CommentSort
with YouTube() as yt:
# `CommentSort.NEWEST` (or the string "newest") returns every comment.
for comment in yt.comments("https://youtu.be/dQw4w9WgXcQ", sort=CommentSort.NEWEST):
print(comment.author, "-", comment.text)
sort accepts a CommentSort (TOP / NEWEST) or its string value
("top" / "newest"); it defaults to CommentSort.TOP to mirror YouTube's
default view.
Use cases
ytscrape is a great fit when you want to:
- Scrape YouTube search results for a keyword or topic.
- Extract YouTube video data (title, channel, views, duration, thumbnails).
- Scrape YouTube comments (and replies) for sentiment or audience analysis.
- Collect YouTube channel and playlist listings at scale.
- Build research datasets for analytics or machine learning.
- Build recommendation engines on top of real YouTube data.
- Monitor competitors and their channels without hitting API quotas.
- Run trend analysis across topics, keywords and regions.
Command line
# Search
python -m ytscrape search "python tutorial" --filter videos --max 10
# Localised search (Ukrainian interface, Ukrainian region)
python -m ytscrape --language uk --region UA search "музика" --max 10
# Video details
python -m ytscrape video https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Collect comments (pass 0 to --max for no limit)
python -m ytscrape comments https://www.youtube.com/watch?v=dQw4w9WgXcQ --max 20
# Collect comments together with their replies
python -m ytscrape comments https://www.youtube.com/watch?v=dQw4w9WgXcQ --replies
# Collect EVERY comment (the default "top" order hides some)
python -m ytscrape comments https://www.youtube.com/watch?v=dQw4w9WgXcQ --sort newest
After installing, a ytscrape console script is also available:
ytscrape search "python" --filter channels --max 5
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ytscrape-0.1.3.tar.gz.
File metadata
- Download URL: ytscrape-0.1.3.tar.gz
- Upload date:
- Size: 26.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
633833678a90eb73a071728575ff6d2a634eec57edaf8fa9a6ad66ca9315d324
|
|
| MD5 |
1035da6e86a9d38f4046481da9d58a51
|
|
| BLAKE2b-256 |
3b53effe85b92ee2909b9255de7065632716a92634d62dcd971e175089505af3
|
Provenance
The following attestation bundles were made for ytscrape-0.1.3.tar.gz:
Publisher:
publish.yml on vsmutok/ytscrape
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ytscrape-0.1.3.tar.gz -
Subject digest:
633833678a90eb73a071728575ff6d2a634eec57edaf8fa9a6ad66ca9315d324 - Sigstore transparency entry: 2343441545
- Sigstore integration time:
-
Permalink:
vsmutok/ytscrape@33cb8f92faaa61b6ae5265ad4306f491c59101ac -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/vsmutok
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@33cb8f92faaa61b6ae5265ad4306f491c59101ac -
Trigger Event:
push
-
Statement type:
File details
Details for the file ytscrape-0.1.3-py3-none-any.whl.
File metadata
- Download URL: ytscrape-0.1.3-py3-none-any.whl
- Upload date:
- Size: 31.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74cb6aee2836689912cca130d3cf58d78bcdd941705c66391b909735e71bc8a0
|
|
| MD5 |
0dee5f402173edbce429cb2c89a902a9
|
|
| BLAKE2b-256 |
f6d0ef1b104550c653bac56afcd2440a68e3bba3d96fce14da93f4cfeba080ec
|
Provenance
The following attestation bundles were made for ytscrape-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on vsmutok/ytscrape
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ytscrape-0.1.3-py3-none-any.whl -
Subject digest:
74cb6aee2836689912cca130d3cf58d78bcdd941705c66391b909735e71bc8a0 - Sigstore transparency entry: 2343441575
- Sigstore integration time:
-
Permalink:
vsmutok/ytscrape@33cb8f92faaa61b6ae5265ad4306f491c59101ac -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/vsmutok
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@33cb8f92faaa61b6ae5265ad4306f491c59101ac -
Trigger Event:
push
-
Statement type: