Skip to main content

ytscrape — Free YouTube Scraper for Python

PyPI version Python versions Release Downloads Ruff License: MIT

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 params values may change over time.

Table of Contents

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 SearchFilter enum (no magic EgIQAQ== strings in your code).
  • 🌍 Language & country support — localise results with the Language and Country value objects (thin wrappers around raw ISO codes, no hard-coded lists) bundled in a small Locale object; codes are validated with pycountry, 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

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ytscrape-0.1.3.tar.gz (26.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ytscrape-0.1.3-py3-none-any.whl (31.6 kB view details)

Uploaded Python 3

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

Hashes for ytscrape-0.1.3.tar.gz
Algorithm Hash digest
SHA256 633833678a90eb73a071728575ff6d2a634eec57edaf8fa9a6ad66ca9315d324
MD5 1035da6e86a9d38f4046481da9d58a51
BLAKE2b-256 3b53effe85b92ee2909b9255de7065632716a92634d62dcd971e175089505af3

See more details on using hashes here.

Provenance

The following attestation bundles were made for ytscrape-0.1.3.tar.gz:

Publisher: publish.yml on vsmutok/ytscrape

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for ytscrape-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 74cb6aee2836689912cca130d3cf58d78bcdd941705c66391b909735e71bc8a0
MD5 0dee5f402173edbce429cb2c89a902a9
BLAKE2b-256 f6d0ef1b104550c653bac56afcd2440a68e3bba3d96fce14da93f4cfeba080ec

See more details on using hashes here.

Provenance

The following attestation bundles were made for ytscrape-0.1.3-py3-none-any.whl:

Publisher: publish.yml on vsmutok/ytscrape

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page