YouTube Channel Scraper — Videos, Playlists and Community Posts
Pricing
from $2.99 / 1,000 results
YouTube Channel Scraper — Videos, Playlists and Community Posts
Collect actionable insights from YouTube channels automatically. This scraper retrieves video metadata, views, likes, descriptions, and upload history. Useful for content strategy, competitive research, analytics dashboards, and large-scale data extraction.
Pricing
from $2.99 / 1,000 results
Rating
0.0
(0)
Developer
SimpleAPI
Maintained by CommunityActor stats
0
Bookmarked
4
Total users
0
Monthly active users
11 days ago
Last modified
Categories
Share
YouTube Channel Scraper — Videos, Playlists and Community Posts
This YouTube channel scraper pulls a channel's entire public surface in one run: the profile/About block, long-form videos, Shorts, live and past streams, community posts, and — the feature that sets this variant apart — the channel's Playlists and Podcasts tabs, opened for last-updated dates, visibility and lifetime views, with an option to list every video inside each series. Every record lands as one dataset row tagged with a stable rowType, with exact view and like counts pulled from YouTube's own player data rather than the abbreviated "2.2M views" string shown on the page. Content strategists, agencies auditing creator catalogues, researchers, and AI pipelines use it to turn a channel URL into structured JSON without touching the YouTube Data API's quota or an OAuth flow.
⬇️ What is the YouTube Channel Scraper?
The YouTube Channel Scraper (youtube-channel-scraper-and-playlists) is an Apify Actor that takes one or more YouTube channel URLs, @handles, or bare UC… channel IDs and returns every public surface of that channel as normalized JSON rows: channel profile, videos, Shorts, live streams, community posts, and the Playlists/Podcasts tabs with their episode lists. No YouTube account, login, cookie, or API key is required — every surface it reads is logged-out and public, confirmed directly in the source (YouTubeClient sends no auth headers, only a CONSENT/SOCS cookie pair to skip the EU consent interstitial).
Key capabilities:
- Discovery by channel identifier — a full channel URL, a bare
@handle, or a bareUC…ID; no keyword channel search - Seven distinct row types in one dataset —
channel,video,short,livestream,post,playlist,playlistVideo— linked byparentId/parentType - Playlist and podcast structure most channel scrapers skip: item count, cover image, last-updated date, visibility, lifetime views and owner name, per series
- Optional playlist expansion into per-video child rows (position, duration, views, publish age)
- Exact view/like counts for every video-type row, recovered from YouTube's InnerTube
/playerendpoint rather than left as an abbreviated string - Per-surface toggles and volume caps so a run can be scoped to exactly the tabs you need
- Output lands in an Apify dataset, exportable as JSON, CSV, Excel, HTML table, or RSS directly from the Apify Console or API
🔽 What data can I extract with the YouTube Channel Scraper?
Every row carries a rowType column plus a shared set of channel-identity and bookkeeping fields; each rowType then adds its own fields. All field names below are exact JSON keys written by src/main.py, src/playlists.py and src/youtube.py.
Shared on every row
| Field | Example Value | Use Case |
|---|---|---|
rowType | "video" | Filter or group the dataset by entity type |
channelId | "UCBJycsmduvYEL83R_U4JriQ" | Stable join key across every row for a channel |
channelName | "Marques Brownlee" | Display name |
channelUsername | "mkbhd" | The @handle |
channelUrl | "https://www.youtube.com/@mkbhd" | Canonical channel link |
numberOfSubscribers | "21.1M" | Abbreviated, as YouTube publishes it — no exact count exists publicly |
inputUrl | "https://www.youtube.com/@MKBHD" | The exact startUrls entry that produced this row |
scrapedAt | "2026-08-04T10:15:03+00:00" | Run timestamp for freshness checks |
order | 1 | Position within its surface (1-indexed) |
title | "iPhone 17 Review!" | Row title (video/short/playlist title, post text, or channel name) |
url | "https://www.youtube.com/watch?v=…" | Canonical link for the row |
viewCount | 4213087 | Exact integer view count, null when YouTube never publishes real digits |
viewCountText | "4.2M views" | View count exactly as YouTube displays it |
publishedTimeText | "2 weeks ago" | Relative publish string, never converted to a fabricated absolute date |
isChild | true | false only for the channel row |
parentType | "channel" | null for the channel row, "playlist" for playlistVideo rows |
parentId | "UCBJycsmduvYEL83R_U4JriQ" | The id of the parent row, for joins |
Channel row (rowType = "channel") adds
| Field | Example Value | Use Case |
|---|---|---|
channelDescription | "I make videos." | Bio/description text |
channelJoinedDate | "Mar 21, 2008" | Account creation date |
channelLocation | "United States" | Self-declared country |
channelAvatarUrl | "https://yt3.ggpht.com/…" | Highest-resolution avatar |
channelBannerUrl | "https://yt3.googleusercontent.com/…" | Channel banner image |
channelDescriptionLinks | [{"text":"Instagram","url":"https://instagram.com/mkbhd"}] | External links published on the About page |
channelTotalVideos / channelTotalVideosText | 1735 / "1.7K videos" | Exact count when digits are published, else null |
channelTotalViews | 15987200341 | Lifetime channel views, exact digits |
isChannelVerified | true | Verification badge |
availableTabs | ["Home","Videos","Shorts","Live","Playlists","Community","About"] | Which tabs YouTube actually rendered for this channel |
playlistsTabAvailable / podcastsTabAvailable | true / false | Whether those tabs exist on this channel |
playlistCount / podcastCount | 66 / 1 | Roll-up counts from this run |
playlistVideosCollected | 220 | Total playlistVideo child rows emitted this run |
Video, Short and livestream rows (rowType = "video" / "short" / "livestream") add
| Field | Example Value | Use Case |
|---|---|---|
videoId | "kMtN9KJHn5Y" | YouTube video id |
duration | "22:55" | Runtime, H:MM:SS for videos over an hour |
thumbnailUrl | "https://i.ytimg.com/vi/kMtN9KJHn5Y/hqdefault.jpg" | Highest-resolution thumbnail |
likesCount | 184213 | Exact like count from the video's own player payload |
publishedAt | "2026-07-21" | Exact upload date, when the player payload publishes one |
isLive (livestream only) | false | Whether the stream is currently live |
isUpcoming (livestream only) | false | Whether the stream is scheduled |
concurrentViewersText (livestream only) | "12K watching" | Live viewer count as displayed |
Community post rows (rowType = "post") add
| Field | Example Value | Use Case |
|---|---|---|
postId | "UgkxAbCDef123" | Post id |
postType | "poll" | text, image, video, poll or shared |
textContent | "Which one should I review next?" | Post body |
imageUrls | ["https://…"] | Attached image URLs |
pollOptions / pollTotalVotesText | ["Option A","Option B"] / "12,400 votes" | Poll structure |
attachedVideoId | "kMtN9KJHn5Y" | Video attached to the post, if any |
likesCountText / commentsCountText | "3.1K" / "412 Comments" | Abbreviated engagement counts |
📚 Playlists and podcasts: the back-catalogue structure
This is the data the standard channel scraper omits. scrapePlaylists and scrapePodcasts each emit one playlist row per series: playlistId, playlistUrl, collectionType ("playlist" or "podcast"), itemCountText/itemCount, and thumbnailUrl come straight from the tab. Turning on playlistDetails opens each playlist individually and adds lastUpdatedText (e.g. "Last updated on Dec 4, 2025"), visibility (public/unlisted), totalViewsText/totalViews (lifetime playlist views, exact digits), videoCountText/videoCount, ownerName, and detailFetched (whether the open actually succeeded). With expandPlaylistVideos on, each series also yields playlistVideo child rows: positionInPlaylist, videoId, title, url, duration, viewCountText/viewCount, publishedTimeText, videoChannelName, and thumbnailUrl — enough to rebuild a course or podcast's episode order without opening YouTube.
🧭 Segmenting the output by row type and parent
Because one run can emit up to seven row types in a single dataset, three fields do the segmenting: rowType filters the dataset down to just channels, videos, playlists, or any other surface; parentType/parentId chain a row back to its owner (playlistVideo.parentId → playlist.playlistId → playlist.parentId = channel.channelId); and collectionType on playlist rows separates ordinary playlists from podcast collections that share the same row shape. availableTabs on the channel row also tells you which tabs actually existed, so an empty short or livestream result can be told apart from a channel that genuinely has no Shorts or Live tab.
⚖️ How does the YouTube Channel Scraper differ from the official YouTube Data API?
The official YouTube Data API v3 is free up to a default quota of 10,000 units/day per Google Cloud project, and covers channels, playlists, playlistItems and videos — but it does not expose an endpoint for Community tab posts at all, and every call requires a Google Cloud project and an API key (developers.google.com, checked 2026-08-04). This Actor needs neither: it reads the same public pages a logged-out browser sees, through Apify Proxy, and returns community posts as a first-class row type the official API cannot produce.
| Feature | YouTube Data API v3 | YouTube Channel Scraper |
|---|---|---|
| Setup | Google Cloud project + API key | Apify account only |
| Daily volume cap | 10,000 units/day by default (channels.list = 1 unit, playlistItems.list = 1 unit; search = 100 units) | No API quota; governed by maxResults/maxPlaylists per run |
| Community posts | No public endpoint | Returned as rowType = "post" |
| Playlist last-updated / visibility | Available via playlists.list with extra parsing | Returned directly as lastUpdatedText / visibility |
| Playlist video listing | playlistItems.list, 50 items/page, paginated by you | playlistVideo rows, paginated automatically up to maxVideosPerPlaylist |
| Output shape | Raw API response per resource type | One flat, joined dataset row per entity, ready for CSV/Excel |
What is the YouTube Data API's daily quota limit, and why does it matter for bulk audits?
A default Google Cloud project gets 10,000 quota units per day, reset at midnight Pacific Time (developers.google.com, checked 2026-08-04). channels.list and playlistItems.list each cost 1 unit, so pulling playlists and their videos is cheap per call, but a search-based discovery call costs 100 units — meaning a project can run out of quota after roughly 100 search calls in a day, long before a full multi-channel content audit finishes. Raising the cap requires a quota-increase request to Google, which is not instant. This Actor sidesteps that ceiling entirely: it makes the same requests a browser makes, through Apify Proxy, with no per-project unit budget — the only caps are the ones you set (maxResults, maxPlaylists, maxVideosPerPlaylist).
Use the official API when you're building a product that needs Google's guaranteed, versioned response contract and can live inside its quota. Use this Actor when you need a fast, no-setup export of a channel's videos, Shorts, streams, community posts, and playlist structure — including the Community tab data the API cannot return at all.
🎯 How to use data extracted from YouTube channels
Content strategists and marketing teams
Pull a competitor or client channel's full catalogue — title, viewCount, publishedTimeText, and duration for every video, Short and stream — to see upload cadence, format mix, and which playlists are actively maintained (lastUpdatedText). Feed the playlist rows into a spreadsheet to spot stale series (ownerName, videoCount, totalViews per playlist) worth relaunching, and use sortOrderVideos: "viewCount" to surface a channel's best-performing uploads first.
Agencies auditing channels for clients
Run the same input across a list of client or prospect channel URLs on a schedule, keeping scrapeChannelInfo, scrapeVideos and scrapePlaylists on with a modest maxResults. Compare numberOfSubscribers, channelTotalViews, playlistCount and isChannelVerified across runs to build a recurring benchmarking report without re-deriving each channel's structure by hand.
Market research and content intelligence
Map how creators in a niche organize their back catalogue — the ratio of playlists to podcasts (collectionType), how many series stay updated, and where long-form vs. Shorts output concentrates — by scraping a set of channels with expandPlaylistVideos on and aggregating playlistVideo.positionInPlaylist and publishedTimeText across the sample.
AI agents and automated pipelines
Because output is a stable, typed JSON schema keyed by rowType, it plugs directly into RAG pipelines (index channelDescription, title, textContent from posts) or automated enrichment jobs that need a channel's current video and playlist inventory as structured input, without screen-scraping HTML themselves.
🔼 Input sample
Every parameter below is reproduced exactly from .actor/actor.json.
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
startUrls | Yes | array | Channel URLs (https://www.youtube.com/@Handle or .../channel/UC…), bare @handles, or bare UC… channel IDs. One per line. No keyword search. | ["https://www.youtube.com/@MKBHD"] |
scrapePlaylists | No | boolean, default true | Emit one playlist row per playlist on the Playlists tab. Channels without one return no rows. | true |
scrapePodcasts | No | boolean, default true | Emit podcast collections as playlist rows with collectionType = "podcast". Skipped when the tab is absent. | true |
maxPlaylists | No | integer, 1–200, default 20 | Cap applied to the Playlists tab and the Podcasts tab separately. | 50 |
playlistDetails | No | boolean, default true | Open each playlist for lastUpdatedText, visibility, exact totalViews, videoCount, ownerName. Costs one extra request per playlist. | true |
expandPlaylistVideos | No | boolean, default false | Emit one playlistVideo row per video inside each playlist. | false |
maxVideosPerPlaylist | No | integer, 1–500, default 50 | Cap on videos listed per playlist, independent of maxResults. | 100 |
scrapeChannelInfo | No | boolean, default true | Emit the channel/About row. | true |
scrapeVideos | No | boolean, default true | Emit rows from the Videos tab. | true |
scrapeShorts | No | boolean, default true | Emit rows from the Shorts tab. | true |
scrapeLiveStreams | No | boolean, default true | Emit rows from the Live tab. | true |
scrapePosts | No | boolean, default true | Emit rows from the Community tab. | true |
maxResults | No | integer, 1–1000, default 10 | Cap applied separately to videos, Shorts, live streams and posts, per channel. | 25 |
sortOrderVideos | No | string enum, default "date" | "date" (newest first), "viewCount" (most viewed first), "oldest" (first uploads first). Only affects the Videos tab. | "viewCount" |
proxyConfiguration | No | object, default {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US"} | Apify Proxy, attached to every request the Actor makes. | see default |
{"startUrls": ["https://www.youtube.com/@MKBHD"],"scrapeChannelInfo": true,"scrapeVideos": true,"scrapeShorts": true,"scrapeLiveStreams": true,"scrapePosts": true,"maxResults": 25,"sortOrderVideos": "date","scrapePlaylists": true,"scrapePodcasts": true,"maxPlaylists": 50,"playlistDetails": true,"expandPlaylistVideos": true,"maxVideosPerPlaylist": 100,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "US"}}
⚠️ Common pitfall: startUrls must resolve to a channel — a bare keyword, a search phrase, or a video URL is silently skipped with a warning ("not a YouTube channel URL, @handle or UC id"), because this Actor does not perform channel discovery by keyword. Paste the channel's own URL, @handle, or UC… id.
🔽 Output sample
Output is typed, normalized JSON — one flat row per entity, no nested video lists buried inside a channel object. From the Apify Console or API you can export the dataset as JSON, CSV, Excel (XLSX), HTML table, or RSS.
{"rowType": "playlistVideo","channelId": "UCBJycsmduvYEL83R_U4JriQ","channelName": "Marques Brownlee","channelUsername": "mkbhd","channelUrl": "https://www.youtube.com/@mkbhd","numberOfSubscribers": "21.1M","inputUrl": "https://www.youtube.com/@MKBHD","scrapedAt": "2026-08-04T10:15:03+00:00","order": 1,"title": "RETRO TECH: CAMCORDER","url": "https://www.youtube.com/watch?v=kMtN9KJHn5Y","viewCount": null,"viewCountText": "9.3M views","publishedTimeText": "6 years ago","isChild": true,"parentType": "playlist","parentId": "PLBsP89CPrMeO7uztAu6YxSB10cRMpjgiY","playlistId": "PLBsP89CPrMeO7uztAu6YxSB10cRMpjgiY","playlistTitle": "Retro Tech","playlistUrl": "https://www.youtube.com/playlist?list=PLBsP89CPrMeO7uztAu6YxSB10cRMpjgiY","positionInPlaylist": 1,"videoId": "kMtN9KJHn5Y","duration": "22:55","thumbnailUrl": "https://i.ytimg.com/vi/kMtN9KJHn5Y/hqdefault.jpg","videoChannelName": "Marques Brownlee"}
🧭 How do you filter and target specific YouTube channel data?
This Actor doesn't filter by category or location — it filters by which surfaces you open and how deep you go into each one, which matters because every additional surface and every extra playlist detail costs additional requests. Three controls do the targeting:
- Section toggles —
scrapeChannelInfo,scrapeVideos,scrapeShorts,scrapeLiveStreams,scrapePosts,scrapePlaylists,scrapePodcastseach turn one surface on or off independently. Turning off the surfaces you don't need is the single biggest lever on run time, since every video-type item costs one extra InnerTube request for its exact view/like count. - Volume caps —
maxResults(1–1000) bounds videos, Shorts, live streams and posts per channel;maxPlaylists(1–200) bounds the Playlists and Podcasts tabs separately;maxVideosPerPlaylist(1–500) bounds how deepexpandPlaylistVideosgoes into each series. YouTube itself serves 30 collections and 100 playlist items per request — this Actor pages past both ceilings automatically up to your cap. - Detail depth —
playlistDetailsdecides whether playlist rows carry only tab-level fields (id, title, item-count badge) or the fuller set from opening each playlist (lastUpdatedText,visibility, exacttotalViews).expandPlaylistVideosdecides whether you get playlist rows only, or the fullplaylistVideochild rows too.
A quick channel snapshot, skipping playlists entirely:
{ "startUrls": ["https://www.youtube.com/@MKBHD"], "scrapePlaylists": false, "scrapePodcasts": false, "maxResults": 10 }
A full playlist/course audit, no video-tab noise:
{ "startUrls": ["https://www.youtube.com/@MKBHD"], "scrapeVideos": false, "scrapeShorts": false, "scrapeLiveStreams": false, "scrapePosts": false, "maxPlaylists": 200, "playlistDetails": true, "expandPlaylistVideos": true, "maxVideosPerPlaylist": 500 }
Podcast episodes only:
{ "startUrls": ["https://www.youtube.com/@MKBHD"], "scrapeChannelInfo": false, "scrapeVideos": false, "scrapeShorts": false, "scrapeLiveStreams": false, "scrapePosts": false, "scrapePlaylists": false, "scrapePodcasts": true, "expandPlaylistVideos": true }
▶️ Want to try other YouTube scrapers?
| Scraper | What it extracts |
|---|---|
| Youtube Most Replayed Scraper With Channel Details | Most-replayed heatmap peaks per video plus the publishing channel's id, handle, avatar and subscriber text |
| YouTube Video Scraper by Hashtag | Videos and Shorts discovered by hashtag, keyword, or @handle search |
Comparable creator/profile scrapers on other platforms:
| Scraper | What it extracts |
|---|---|
| TikTok Data Scraper — Video, Hashtag and Sound Performance Analytics | TikTok profiles, videos, hashtag/music/playlist feeds and comments, with view/like/comment deltas across runs |
| Pinterest Profile Scraper with Email and Website Contacts | Public Pinterest profiles plus emails, phone numbers and social handles found in the bio and linked website |
💻 How to extract YouTube channel data programmatically
This Actor runs like any Apify Actor: start it from the Apify Console, on a schedule, via a webhook, or with the apify-client SDK against a single REST endpoint — no separate API key or signup beyond your Apify account.
Python example
from apify_client import ApifyClientclient = ApifyClient("<APIFY_API_TOKEN>")run = client.actor("<YOUR_APIFY_USERNAME>/youtube-channel-scraper-and-playlists").call(run_input={"startUrls": ["https://www.youtube.com/@MKBHD"],"scrapePlaylists": True,"playlistDetails": True,"maxPlaylists": 50,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["rowType"] == "playlist":print(item["title"], item["videoCount"], item["lastUpdatedText"])
Export to spreadsheets or CRM
From the Apify Console, export the dataset as CSV or XLSX directly, or pull it via the API and map fields into your destination columns: channelName → Channel, title → Video/Playlist title, viewCount → Views, publishedTimeText → Published, url → Link. Filtering the export to rowType = "channel" before import gives a clean one-row-per-channel roster for a CRM; keeping video/playlist rows gives a content inventory sheet.
⚖️ Is it legal to scrape YouTube channel data?
Yes — this Actor reads only pages a logged-out visitor can already see: a channel's own public profile, videos, playlists and community posts, which the channel owner has chosen to publish. Because a channel's profile fields (channelName, channelLocation, channelDescription) can identify the individual behind it, this falls under personal data regimes like GDPR and CCPA where the channel owner is an EU/UK or California resident: you need a lawful basis to store and reuse that data, particularly in bulk or for profiling. Data that is purely content metadata (view counts, durations, playlist structure) carries no such requirement, but treat any row that names or identifies a person with the same care. Consult legal counsel for commercial applications involving bulk storage of personal data.
❓ FAQ
How does the Actor detect a video, Short or channel that no longer exists?
If a channel has no Live or Shorts tab, YouTube silently serves the Home tab instead — this Actor detects that substitution by checking the selected tab's title and returns zero rows for that surface rather than mislabelling home videos as streams or Shorts. A channel that fails entirely (e.g. a bad URL) is retried once on a fresh proxy session, then logged as a failure with zero rows.
Can I get the channel's playlists and podcasts along with the main videos?
Yes. scrapePlaylists and scrapePodcasts are on by default alongside scrapeVideos, scrapeShorts, scrapeLiveStreams and scrapePosts — a single run returns all seven row types unless you turn some off. maxPlaylists (default 20, max 200) caps the Playlists and Podcasts tabs separately from the maxResults cap on the other four surfaces.
How accurate are the view and like counts?
Video, Short and livestream rows get exact viewCount/likesCount from each video's own InnerTube /player payload — a real integer, not an estimate. Where YouTube's grid or playlist page only ever publishes an abbreviated string ("2.2M views"), viewCount is left null rather than backfilled with a guessed number; viewCountText always keeps the original string. Subscriber counts (numberOfSubscribers) are always the abbreviated form YouTube publishes — no exact subscriber count exists publicly.
How many records can I get per run?
Up to maxResults (max 1000) each for videos, Shorts, live streams and posts, per channel. Playlists and Podcasts are capped separately by maxPlaylists (max 200), and playlist expansion is capped by maxVideosPerPlaylist (max 500) per playlist. YouTube itself serves 30 collections and 100 playlist videos per request; this Actor pages past both automatically up to your caps — one measured run collected 65 playlists plus 1 podcast (66 unique rows) from a channel's 3-page Playlists tab.
What is the most useful targeting control this Actor has?
The section toggles (scrapeVideos, scrapeShorts, scrapeLiveStreams, scrapePosts, scrapePlaylists, scrapePodcasts, scrapeChannelInfo). Since every video-type row costs one extra request for its exact counts, turning off surfaces you don't need is the fastest way to cut run time on a large batch of channels.
Does the videos-tab sort order always work?
sortOrderVideos maps to YouTube's own Latest/Popular/Oldest chips on the Videos tab only. If a channel doesn't offer the Popular or Oldest chip, the run falls back to newest-first and logs that it did — there is deliberately no equivalent sort input for Shorts, Live, Community or Playlists, because YouTube publishes no sort chips there at all.
Does the YouTube Channel Scraper work with Claude, ChatGPT, and AI agent frameworks?
It is callable as a standard HTTP endpoint via the Apify API, so any agent framework that can make an authenticated REST call — LangChain, custom tool-calling agents, or a direct API integration — can trigger a run and read back the dataset.
Can I use this Actor without a YouTube API key or developer account?
Yes. You only need an Apify account. No Google Cloud project, YouTube Data API key, OAuth flow, or YouTube login/cookie is required — every surface this Actor reads is public and logged-out.
Why do some playlists share the same title?
Creators reuse playlist names. Rows are deduplicated on playlistId, never on title, so two "Reviews" playlists both appear as separate rows.
How do I join playlist videos back to their playlist and channel?
playlistVideo.parentId equals playlist.playlistId, and playlist.parentId equals channel.channelId — chain those two joins to rebuild the channel → playlist → video hierarchy from a flat dataset.
Conclusion
The YouTube Channel Scraper turns any channel URL, @handle or UC… id into structured JSON covering its videos, Shorts, live streams, community posts, and — uniquely among the account's channel scrapers — its full Playlists and Podcasts structure down to the individual episode. It's built for content strategists, agencies, researchers and AI pipelines that need exact view/like counts and a stable, joinable schema without touching the YouTube Data API's quota or an OAuth flow. Start a run from the Apify Console with a single channel URL, or call it through apify-client to fold channel and playlist audits straight into your own pipeline.