Skip to main content

ytscrape ytscrape

ytscrape — Free YouTube Scraper for Python

Scrape YouTube search results, video & channel metadata, comments and transcripts — no API key, no quota, no browser.

PyPI version Python versions Downloads License: MIT Stars

from ytscrape import YouTube

with YouTube() as yt:
    for video in yt.search("python", max_results=5):
        print(video.title, video.url)

ytscrape talks to YouTube's internal InnerTube API — the same endpoints the web app uses — and turns responses into typed, frozen dataclasses with transparent pagination. Pure HTTP: no Selenium, no Playwright, no API key. Sync (YouTube) and async (AsyncYouTube) share the same surface.

⚠️ This library uses YouTube's private endpoints. Use it responsibly and at your own risk — the endpoints may change over time.

Why ytscrape?

  • 🔑 No API key, no quota — nothing to register, no billing project.
  • 🧊 No browser — pure HTTP only.
  • 🧩 Typed models (Video, Channel, Comment, Transcript, …) + py.typed.
  • 💬 Every comment — replies included; CommentSort.NEWEST does not hide any.
  • 📄 Transparent pagination — just iterate; continuation tokens are handled for you.
  • 🌍 Localisationlanguage (hl) and region (gl), validated ISO codes.
  • Sync & async — optional httpx extra for AsyncYouTube.
  • 🖥️ CLI includedytscrape search "python" --max 10.
  • 📤 JSON / CSV exportvideo.to_json(), dumps_csv(results), or ytscrape search "python" --format json.

Installation

pip install ytscrape            # or: uv add ytscrape
pip install "ytscrape[async]"   # optional async API (httpx)

Requires Python 3.10+. Runtime deps: requests, pycountry, defusedxml (+ httpx with the async extra).

Quick start

from ytscrape import YouTube, SearchFilter, CommentSort

with YouTube(language="en", region="US") as yt:
    for video in yt.search("python", filter=SearchFilter.VIDEOS, max_results=20):
        print(video.title, "-", video.url)

    details = yt.video("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    print(details.title, details.channel, details.views, details.length_seconds)

    for comment in yt.comments(
        "https://youtu.be/dQw4w9WgXcQ",
        include_replies=True,
        sort=CommentSort.NEWEST,
        max_results=100,
    ):
        marker = "  ↳" if comment.is_reply else "-"
        print(f"{marker} {comment.author}: {comment.text}")

Async (same methods, await / async for):

import asyncio
from ytscrape import AsyncYouTube, SearchFilter


async def main() -> None:
    async with AsyncYouTube(max_concurrency=8) as yt:
        async for video in await yt.search(
            "python", filter=SearchFilter.VIDEOS, max_results=5
        ):
            print(video.title)


asyncio.run(main())

📂 Runnable sync + async snippets for every feature: examples/ · full guides: vsmutok.github.io/ytscrape

Feature coverage

Area Status Notes
Search — videos / channels / playlists / Shorts / movies SearchFilter.*
Video & channel metadata video(), channel() (id, URL, @handle)
Comments + replies comments(), CommentSort.NEWEST for every comment
Transcripts / captions transcript() / transcripts()
Pagination Transparent for search and comments
Localisation (hl / gl) Validated ISO codes
Typed models + py.typed PEP 561
CLI ytscrape / python -m ytscrape
Async API AsyncYouTube via ytscrape[async]
Channel tabs, playlist items, related / trending 🚧 Planned

ytscrape vs. the alternatives

ytscrape YouTube Data API yt-dlp Browser automation
API key required
Daily quota
Browser / driver needed
Search / metadata / comments
Typed Python models
Async (asyncio) API varies
Downloads media
Install size tiny medium large huge

How it works

High-level flow — sync and async share the same models and parsing layer:

graph LR
    User[User code / CLI] --> Facade[YouTube / AsyncYouTube]
    Facade --> Client[InnerTubeClient / AsyncInnerTubeClient]
    Facade --> Results[Lazy results / comment threads]
    Client --> InnerTube[YouTube InnerTube API]
    Client --> Context[InnerTube context]
    Results --> Client
    Client --> Parsing[parsing helpers]
    Results --> Parsing
    Parsing --> Models[Frozen dataclasses]
    Models --> User
  1. Load youtube.com once and extract the InnerTube context (API key, client version, visitor data).
  2. POST to youtubei/v1/search, player, browse, next, etc. with that context.
  3. Parse responses into frozen dataclasses; continuation tokens are followed while you iterate.

Documentation

Deep dives live on the docs site (not duplicated here):

Topic Link
Installation docs
Quickstart docs
Search, details, comments, transcripts guides
Language & region, pagination, errors guides
Async API (concurrency, retries, fan-out) async guide
Proxies, custom sessions advanced
CLI CLI overview
API reference API
Examples (sync + async) examples/

CLI cheatsheet

ytscrape CLI demo

The CLI prints colourful boxed tables under a mini ▶ ytscrape wordmark, with a spinner while requests are in flight:

ytscrape search "python tutorial" --filter videos --max 10
ytscrape --language uk --region UA search "музика" --max 10
ytscrape video https://www.youtube.com/watch?v=dQw4w9WgXcQ
ytscrape channel @RickAstleyYT
ytscrape transcript dQw4w9WgXcQ --lang en
ytscrape comments https://youtu.be/dQw4w9WgXcQ --replies --sort newest --max 20

python -m ytscrape … works too. Pass --max 0 to comments for no limit.

FAQ

Do I need a YouTube Data API key?

No. ytscrape uses the same internal endpoints as the YouTube web app.

Why are some comments missing?

YouTube's default "Top comments" view hides less relevant comments and "potential spam". Pass sort="newest" (or CommentSort.NEWEST) to collect every comment.

Why is like_count None?

YouTube abbreviates large counts (1.2K). The raw string is always in like_count_text.

Can I use a proxy?

Yes — inject your own requests.Session into InnerTubeClient, or httpx.AsyncClient into AsyncInnerTubeClient. See the advanced guide.

Will I get rate limited?

There is no published quota, but YouTube may throttle aggressive traffic. Reuse one client, cap work with max_results, and add delays for large crawls.

Does it download videos?

No — metadata only. Use yt-dlp for media.

Is scraping YouTube legal?

Private endpoints may conflict with YouTube's Terms of Service. The library is for research and educational use; you are responsible for how you use it.

Contributing

Bug reports, ideas and PRs are welcome — see CONTRIBUTING.md.

git clone https://github.com/vsmutok/ytscrape && cd ytscrape
uv sync --dev
uv run pytest
uv run pre-commit run --all-files

License

MIT

Release files for ytscrape 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ytscrape 1.0.1
File Size Uploaded
ytscrape-1.0.1.tar.gz 46.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ytscrape 1.0.1
File Interpreter ABI Platform
ytscrape-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 103.5 kB

Release files / ytscrape-1.0.1.tar.gz

Download URL ytscrape-1.0.1.tar.gz
Size 46.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b466d07f456d7a630fbe5bba8ecf3c84558759a5041a8738b9f884d3e1d8a8e3
BLAKE2b-256 checksum
How to use checksums
9eb69c3b327326d34d0347acce2530c2c602dfa0d9a25d694c876d667b96dc78
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release files / ytscrape-1.0.1-py3-none-any.whl

Download URL ytscrape-1.0.1-py3-none-any.whl
Size 56.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2fc828543480092e37c0f6aa591df674ad373ca7c06719532c5b2959f5a2ebad
BLAKE2b-256 checksum
How to use checksums
65935ae936bd8204a355dfb75013275c91f775a381872ea6634ce73e6038178a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page