YouTube Channel Scraper — Videos, Playlists and Community Posts avatar

YouTube Channel Scraper — Videos, Playlists and Community Posts

Pricing

from $2.99 / 1,000 results

Go to Apify Store
YouTube Channel Scraper — Videos, Playlists and Community Posts

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

SimpleAPI

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

0

Monthly active users

11 days ago

Last modified

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 bare UC… ID; no keyword channel search
  • Seven distinct row types in one dataset — channel, video, short, livestream, post, playlist, playlistVideo — linked by parentId/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 /player endpoint 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

FieldExample ValueUse 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
order1Position 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
viewCount4213087Exact 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
isChildtruefalse 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

FieldExample ValueUse 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 / channelTotalVideosText1735 / "1.7K videos"Exact count when digits are published, else null
channelTotalViews15987200341Lifetime channel views, exact digits
isChannelVerifiedtrueVerification badge
availableTabs["Home","Videos","Shorts","Live","Playlists","Community","About"]Which tabs YouTube actually rendered for this channel
playlistsTabAvailable / podcastsTabAvailabletrue / falseWhether those tabs exist on this channel
playlistCount / podcastCount66 / 1Roll-up counts from this run
playlistVideosCollected220Total playlistVideo child rows emitted this run

Video, Short and livestream rows (rowType = "video" / "short" / "livestream") add

FieldExample ValueUse 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
likesCount184213Exact 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)falseWhether the stream is currently live
isUpcoming (livestream only)falseWhether the stream is scheduled
concurrentViewersText (livestream only)"12K watching"Live viewer count as displayed

Community post rows (rowType = "post") add

FieldExample ValueUse 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.parentIdplaylist.playlistIdplaylist.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.

FeatureYouTube Data API v3YouTube Channel Scraper
SetupGoogle Cloud project + API keyApify account only
Daily volume cap10,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 postsNo public endpointReturned as rowType = "post"
Playlist last-updated / visibilityAvailable via playlists.list with extra parsingReturned directly as lastUpdatedText / visibility
Playlist video listingplaylistItems.list, 50 items/page, paginated by youplaylistVideo rows, paginated automatically up to maxVideosPerPlaylist
Output shapeRaw API response per resource typeOne 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.

ParameterRequiredTypeDescriptionExample Value
startUrlsYesarrayChannel 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"]
scrapePlaylistsNoboolean, default trueEmit one playlist row per playlist on the Playlists tab. Channels without one return no rows.true
scrapePodcastsNoboolean, default trueEmit podcast collections as playlist rows with collectionType = "podcast". Skipped when the tab is absent.true
maxPlaylistsNointeger, 1–200, default 20Cap applied to the Playlists tab and the Podcasts tab separately.50
playlistDetailsNoboolean, default trueOpen each playlist for lastUpdatedText, visibility, exact totalViews, videoCount, ownerName. Costs one extra request per playlist.true
expandPlaylistVideosNoboolean, default falseEmit one playlistVideo row per video inside each playlist.false
maxVideosPerPlaylistNointeger, 1–500, default 50Cap on videos listed per playlist, independent of maxResults.100
scrapeChannelInfoNoboolean, default trueEmit the channel/About row.true
scrapeVideosNoboolean, default trueEmit rows from the Videos tab.true
scrapeShortsNoboolean, default trueEmit rows from the Shorts tab.true
scrapeLiveStreamsNoboolean, default trueEmit rows from the Live tab.true
scrapePostsNoboolean, default trueEmit rows from the Community tab.true
maxResultsNointeger, 1–1000, default 10Cap applied separately to videos, Shorts, live streams and posts, per channel.25
sortOrderVideosNostring enum, default "date""date" (newest first), "viewCount" (most viewed first), "oldest" (first uploads first). Only affects the Videos tab."viewCount"
proxyConfigurationNoobject, 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:

  1. Section togglesscrapeChannelInfo, scrapeVideos, scrapeShorts, scrapeLiveStreams, scrapePosts, scrapePlaylists, scrapePodcasts each 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.
  2. Volume capsmaxResults (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 deep expandPlaylistVideos goes 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.
  3. Detail depthplaylistDetails decides whether playlist rows carry only tab-level fields (id, title, item-count badge) or the fuller set from opening each playlist (lastUpdatedText, visibility, exact totalViews). expandPlaylistVideos decides whether you get playlist rows only, or the full playlistVideo child 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?

ScraperWhat it extracts
Youtube Most Replayed Scraper With Channel DetailsMost-replayed heatmap peaks per video plus the publishing channel's id, handle, avatar and subscriber text
YouTube Video Scraper by HashtagVideos and Shorts discovered by hashtag, keyword, or @handle search

Comparable creator/profile scrapers on other platforms:

ScraperWhat it extracts
TikTok Data Scraper — Video, Hashtag and Sound Performance AnalyticsTikTok profiles, videos, hashtag/music/playlist feeds and comments, with view/like/comment deltas across runs
Pinterest Profile Scraper with Email and Website ContactsPublic 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 ApifyClient
client = 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.

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.