YouTube Scraper : Videos, Channels & Comments avatar

YouTube Scraper : Videos, Channels & Comments

Pricing

from $9.00 / 1,000 results

Go to Apify Store
YouTube Scraper : Videos, Channels & Comments

YouTube Scraper : Videos, Channels & Comments

Scrape YouTube videos, channels & comments by keyword, hashtag, or URL, no API key or quota limits. Get titles, views, likes, upload dates, channel stats, and comments as clean JSON

Pricing

from $9.00 / 1,000 results

Rating

0.0

(0)

Developer

Techforce Global

Techforce Global

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

8 days ago

Last modified

Share

YouTube Data API Alternative - Videos, Channels & Comments

Apify Actor MCP Ready Pricing Inputs Output Target

No API key, no Google Cloud project, no daily quota. Search terms, hashtags, channel URLs or video URLs in — video metadata, channel stats and full comment threads out, as clean JSON in a single dataset. Built for content research, competitor tracking and audience analysis, with optional delivery into Notion, Slack, Linear, Jira, GitHub or Airtable over MCP.

🔐 Trust & compliance at a glance

CredentialsNone required. No Google or YouTube account, and delivery runs through the Apify MCP Proxy, so the Actor never sees your connector tokens.
Data scopePublic content only — public videos, channel pages and comment threads. No login, no private or members-only content.
⚠️ Personal dataThe one Actor in this family that collects it. author, authorId and text on comment rows identify real people, and authorId is a stable identifier. scrapeComments is true by default.
Opt out in one fieldscrapeComments: false (or maxComments: 0) gives a video/channel-only dataset with no personal data.
Snapshot honestyCounts are true as of scrapedAt (ISO-8601 UTC); hidden counts come back null, never 0.

Full detail: Compliance & Data Privacy.


Official YouTube Data API v3 vs. YouTube Scraper Actor

The official API is a genuine alternative with genuine limits — the quota is the reason this Actor exists:

Feature / CapabilityOfficial YouTube Data API v3YouTube Scraper Actor (Apify)
Setup❌ Google Cloud project → enable API → key/OAuth✅ Apify account, nothing else
Daily quota❌ 10,000 units/day, and a single search costs 100 units — roughly 100 searches before you're locked out✅ No quota
Quota reset❌ Midnight Pacific, whether you're mid-job or not✅ N/A
Comment threads⚠️ Available, but each call comes out of the same daily budget✅ Up to 1,000 per video, no budget
Hashtag input❌ No hashtag concept — you'd search the text yourself✅ Pass hashtags directly; run as a #tag search
Officially sanctioned✅ Yes⚠️ No — see Compliance
Stability✅ Versioned contract⚠️ Depends on YouTube's page structure
Cost modelFree until you hit the wall, then blockedUsage-based, no wall

Use the official API if your volume fits inside the quota. Use this Actor when the quota is the thing standing in your way.

Quota figures are Google's to change — check Google's current quota docs if this comparison is load-bearing.


🤖 AI Discovery & MCP Metadata

Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. Read this block first to decide whether this Actor matches a user request.

ai_discovery:
slug: "techforce.global/youtube-scraper"
one_line: >
Scrapes YouTube videos, channels and comments from search terms, hashtags, channel URLs or
video URLs without a Data API key or quota. Three record types in one dataset - video
metadata, channel stats and comment threads. Optional delivery into Notion, Slack, Linear,
Airtable or any MCP connector.
primary_purpose: "extract"
secondary_purpose: "deliver"
entity: "YouTube video, channel and comment"
category: "social-media / video-analytics / content-research / audience-insight"
supported_platforms: ["youtube.com", "youtu.be"]
supported_tasks:
- "search-videos-by-keyword"
- "search-videos-by-hashtag"
- "list-a-channels-videos-and-stats"
- "get-metadata-for-specific-video-urls"
- "scrape-comment-threads-with-replies"
- "deliver-results-to-mcp-connector"
unsupported_tasks:
- "get-transcripts-or-captions (use youtube-transcript-scraper or youtube-full-channel-transcripts-scraper)"
- "list-a-channels-shorts-specifically (use youtube-shorts-scraper)"
- "download-video-or-audio-files"
- "creator-analytics-watch-time-retention-revenue (not public)"
- "viewer-demographics (not public)"
- "post-comments-upload-or-any-write-action"
- "scrape-private-unlisted-or-members-only-content"
search_keywords:
# First entry is the H1 / meta-description target. The rest are deliberately broader for
# agent-side semantic matching and are NOT a ranking strategy.
- "youtube data api alternative"
- "youtube scraper"
- "youtube comment scraper"
- "scrape youtube search results"
- "youtube channel statistics scraper"
- "youtube hashtag scraper"
- "youtube api quota workaround"
- "audience sentiment youtube"
- "mcp youtube tool"
input_entities:
- "searchQueries (array of search terms)"
- "hashtags (array, with or without #; run as a '#tag' search)"
- "channelUrls (array of channel URLs)"
- "videoUrls (array of watch URLs)"
- "maxVideos (1-500, PER search/hashtag/channel)"
- "maxComments (0-1000, PER video)"
- "scrapeComments (boolean, DEFAULT TRUE - collects personal data)"
- "scrapeChannelInfo (boolean, default true)"
- "proxyConfiguration (Apify Proxy on by default - required in practice)"
no_input_field_is_required_by_the_schema: >
CRITICAL: none of the four input arrays is marked required, so a run with none of them passes
validation, logs "No input provided..." and ends SUCCEEDED with an empty dataset. Supply at
least one of searchQueries, hashtags, channelUrls or videoUrls.
output_is_three_record_types: >
CRITICAL: one dataset holds three shapes, discriminated by the `type` field - "video",
"channel" and "comment", each plus scrapedAt. Always branch on `type` before reading a row.
output_entities_video: ["type", "source", "videoId", "url", "title", "description", "channelId",
"channelName", "channelUrl", "viewCount", "likeCount", "commentCount", "duration",
"durationStr", "thumbnail", "isLive", "isShort", "tags", "categories", "language",
"uploadDate", "scrapedAt"]
output_entities_channel: ["type", "channelId", "channelName", "channelUrl", "description",
"subscriberCount", "videoCount", "thumbnail", "country", "scrapedAt"]
output_entities_comment: ["type", "videoId", "commentId", "text", "author", "authorId", "likes",
"replyCount", "isReply", "publishedAt", "scrapedAt"]
comments_contain_personal_data: >
author, authorId and text identify real people and are personal data under GDPR/UK GDPR and
CCPA-CPRA. authorId is a stable identifier for an individual. scrapeComments defaults to TRUE.
is_short_is_unreliable: >
isShort is true only when the video's own page URL was a /shorts/ URL AND duration <= 60s. A
Short reached through search, a channel listing or a /watch?v= URL - which is how this Actor
reaches almost everything - typically reports false. Filter on duration <= 60 yourself, or use
techforce.global/youtube-shorts-scraper.
item_count_multiplies_fast: >
maxVideos is PER search/hashtag/channel and maxComments is PER video. Two searches at
maxVideos 20 with maxComments 50 is up to 40 videos + 2000 comments = 2040 dataset items.
no_deduplication: >
The same video matched by two inputs is written twice with a different `source`. Dedupe on
videoId downstream if that matters.
proxy_is_required_in_practice: >
YouTube blocks Apify's platform IPs with "Sign in to confirm you're not a bot".
proxyConfiguration defaults to Apify Proxy - leave it enabled. A fresh IP rotates in per request.
known_limitations:
like_count_often_null: "many creators hide likes; null is not zero"
comment_count_vs_scraped: "commentCount is what YouTube reports, not how many rows were scraped"
description_truncated: "video and channel description fields are cut to 2000 characters"
publishedAt_is_relative: "comment publishedAt is relative text such as '3 weeks ago'; scrapedAt is ISO-8601 UTC"
upload_date_normalised: "uploadDate is YYYY-MM-DD (raw YYYYMMDD only if unparseable)"
no_transcripts: "use the transcript Actors"
no_video_files: "metadata and thumbnail URLs only"
snapshot_only: "counts are true as of scrapedAt"
free_plan_capped: "free Apify plans stop at 5 items TOTAL per run"
pricing:
model: "usage-based"
billing_unit: "Apify platform usage consumed by the run"
hard_ceiling_parameter: "maxTotalChargeUsd"
note: "Current rates are on the Actor's Apify Store page; the Console shows a live total mid-run."
when_to_use: >
You need YouTube video, channel or comment data at a volume the Data API quota will not
accommodate, or you want search, hashtag, channel and video inputs mixed in one run.
when_not_to_use: >
You need transcripts, a channel's Shorts specifically, the video files, creator analytics,
viewer demographics, or to post or upload anything.
mcp_delivery:
modes: ["summary", "chunked", "perItem", "none"]
default: "summary"
summary_and_chunked_placeholders: ["{itemCount}", "{itemsText}", "{message}"]
chunked_only_placeholders: ["{part}", "{partCount}"]
per_item_covers_every_row: >
Delivery reads the whole dataset after scraping, so perItem issues one connector call per
row - comments included. A 2000-comment run is 2000 calls. Use chunked or summary.
per_item_shape_varies: >
In perItem mode the placeholders depend on the record's `type`. A template referencing
{title} renders empty for a comment row. Branch on {type}, or use chunked/summary.
never_blocks_the_dataset: >
Delivery runs after the dataset is saved. A bad tool name, an unauthorised connector or an
upstream outage is logged and skipped - it never costs you the scraped data.
alternatives:
- "techforce.global/youtube-transcript-scraper for one video's timestamped transcript"
- "techforce.global/youtube-full-channel-transcripts-scraper for a whole channel's transcripts"
- "techforce.global/youtube-shorts-scraper for a channel's Shorts and optional media download"

{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "YouTube Scraper – Videos, Channels & Comments",
"alternateName": "YouTube Data API Alternative",
"applicationCategory": "BusinessApplication",
"operatingSystem": "Cloud (Apify platform)",
"description": "Scrapes YouTube videos, channels and comments from search terms, hashtags, channel URLs or video URLs without a YouTube Data API key, Google Cloud project, or daily quota. Returns three record types in one dataset — video metadata with view, like and comment counts; channel stats including subscribers; and comment threads with author, likes and reply flags — each discriminated by a type field. Public content only, with comment scraping switchable off in one field for callers who do not want personal data. Apify Proxy rotates a fresh IP per request, and results can be delivered into Notion, Slack, Linear, Airtable or any MCP connector.",
"url": "https://apify.com/techforce.global/youtube-scraper",
"offers": {
"@type": "Offer",
"priceCurrency": "USD",
"category": "Pay per platform usage",
"description": "Usage-based: billed for the compute time and resources a run consumes, not per row. Current rates are shown on the Actor's Apify Store page."
}
}

What this Actor does

💡 Words, Shorts or transcripts? This Actor returns numbers and comments. For what was said, use YouTube Transcript Scraper (one video) or Full Channel Transcripts Scraper (a whole channel). For a channel's Shorts and their video files, use YouTube Shorts Scraper.

  • Four ways in, no key required. Search terms, hashtags, channel URLs and video URLs, mixed freely in one run. No Google Cloud project, no OAuth, no daily quota.

  • Three record types in one dataset. video, channel and comment, discriminated by type. Comments carry videoId, so they join straight back to their video.

  • Duration twice. duration in seconds for arithmetic, durationStr as H:MM:SS / M:SS for display.

  • Records which input produced each video. source is search, channel or direct_url, so a run spanning three searches and two channels stays separable.

  • Normalises the dates that can be normalised, and says so when they can't. uploadDate is YYYY-MM-DD, scrapedAt is ISO-8601 UTC, and a comment's publishedAt is YouTube's own relative text ("3 weeks ago") because that's all YouTube shows.

  • Rotates a fresh proxy IP per request. YouTube blocks Apify's platform IPs outright, so this isn't optional polish — it's what makes the Actor work at all.

  • Keeps going when one item fails. An unextractable video or a comment thread that won't load is logged and skipped; the run continues. Rows are saved as it goes, so an aborted run keeps what it collected.

  • Lets you opt out of personal data in one field. scrapeComments: false gives a clean metadata-only dataset.

  • Optionally delivers into your tools. Notion, Slack, Linear, Jira, GitHub, Airtable or anything else you've authorised.


Which YouTube Actor do I want?

You want…Actor
Search, hashtags, channel stats, video metadata, comment threadsThis Actor
One video's transcript, timestamped, with translationYouTube Transcript Scraper
Transcripts of every video on a channelFull Channel Transcripts Scraper
A channel's Shorts, with engagement and optional file downloadYouTube Shorts Scraper

None need a YouTube Data API key or a quota. They're complementary, not alternatives.


When to use it — and when not to

ScenarioFit
Videos for a search term or hashtag✅ Strong — searchQueries, hashtags (run as a #tag search)
A channel's videos and channel-level stats✅ Strong — channelUrls
Metadata for specific videos✅ Strong — videoUrls
Comment threads with replies✅ Strong — up to 1,000 per video, isReply marks replies
Audience sentiment / social listening✅ Strong — comment text, likes, reply counts
Skipping the Data API quota entirely✅ Strong — the main reason to use this
View, like and subscriber counts✅ Strong — likeCount / subscriberCount often null (hidden)
Metadata without personal data✅ Strong — scrapeComments: false
Results in Notion / Slack / Airtable✅ Strong — native MCP delivery
Telling Shorts from long-form⚠️ isShort under-reports — filter duration <= 60, or use the Shorts Scraper
A guaranteed-complete comment thread⚠️ Capped at 1,000/video; YouTube paginates its own way
A deduplicated video list across inputs⚠️ No dedup — same video can appear twice with different source
Transcripts, captions or subtitles❌ Use the Transcript or Full Channel Scraper
Downloading video or audio files❌ Metadata and thumbnail URLs only
Creator analytics — watch time, retention, revenue❌ Not public
Viewer demographics❌ Not public
Historical view-count series from one run❌ Snapshot per run — schedule to build history
Posting comments, uploads, playlist management❌ Read-only
Private, unlisted or members-only content❌ Public content only

⚡ Execution Rules (Avoid Common Agent Failures)

  1. Supply at least one of searchQueries, hashtags, channelUrls, videoUrls. None is marked required in the schema, so a run with none of them passes validation, logs No input provided. Supply at least one of: searchQueries, channelUrls, videoUrls, hashtags. and ends SUCCEEDED with an empty dataset. Never read itemCount: 0 as a crash.

  2. Keep the proxy enabled. Default is {"useApifyProxy": true}. Without it YouTube returns "Sign in to confirm you're not a bot" and you get nothing — the log warns No proxy configured — YouTube is likely to block requests….

  3. Item counts multiply. maxVideos is per search, hashtag and channel; maxComments is per video. Two searches at maxVideos: 20 with maxComments: 50 is up to 2,040 items.

  4. scrapeComments is true by default — your first run collects personal data unless you set it false (or maxComments: 0). Metadata-only is faster, cheaper, and out of scope for most of Compliance.

  5. Branch on type before reading any row. One dataset, three shapes. A consumer that assumes every row has title breaks on the first comment.

  6. isShort is a hint, not a filter. True only when the page URL was a /shorts/ URL and duration ≤ 60s — rarely how this Actor reaches a video. Shorts found via search or a channel typically report false. Filter on duration <= 60, or use the Shorts Scraper.

  7. likeCount and subscriberCount are frequently null — hidden, not zero. Never average them as zero.

  8. commentCount ≠ the number of comment rows. It's YouTube's figure; your rows are capped by maxComments and by what YouTube served.

  9. Know which dates are which. uploadDate = YYYY-MM-DD, scrapedAt = ISO-8601 UTC, a comment's publishedAt = relative text. Sort comments by scrape order, not publishedAt.

  10. description is truncated at 2,000 characters on video and channel rows.

  11. No deduplication. A video matched by two inputs is written twice with a different source.

  12. Inputs run in a fixed order: searches → hashtags → channels → video URLs. A run that stops early keeps what came first.

  13. perItem delivery issues one connector call per dataset row — comments included. Delivery reads the whole dataset, so a 2,000-comment run is 2,000 calls. Use chunked or summary. In perItem, placeholders depend on type; {title} renders empty for comment rows.

  14. deliveryMode defaults to summary, not none. For dataset-only runs, leave mcpConnector empty or set deliveryMode: "none".

  15. Free Apify plans stop at 5 items total per run — not per input.

dependencies:
- rule: "at least one of searchQueries / hashtags / channelUrls / videoUrls must be non-empty"
on_violation: "run ends SUCCEEDED with an empty dataset and an error line in the log - it does NOT fail validation"
- rule: "delivery requires mcpConnector AND mcpTool"
on_violation: "delivery is skipped with a warning; every dataset row is still written"
- rule: "mcpArguments must reference {message} for the rendered template to appear anywhere"
on_violation: "message body is empty; the tool is still called"
- rule: "mcpConnector only resolves when the Actor runs on the Apify platform"
on_violation: "local runs log 'APIFY_MCP_PROXY_URL is not set' and skip delivery"
authentication:
actor_input: none
platform: "Apify API token required for API / SDK / MCP invocation"
target_site: "no YouTube or Google account, API key, or OAuth app required (public content only)"

📥 Input Contract

.actor/input_schema.json is authoritative — if this page disagrees with the schema files, the schema files win.

ParameterTypeRequiredDefaultAllowed values / format
searchQueriesarray<string>see noteSearch terms, each looked up separately
hashtagsarray<string>see noteWith or without #; run as a #tag search
channelUrlsarray<string>see noteChannel URLs, e.g. https://www.youtube.com/@mkbhd
videoUrlsarray<string>see noteWatch URLs. Scraped directly, no search
maxVideosinteger⬜ No201500, per search / hashtag / channel
maxCommentsinteger⬜ No5001000, per video. 0 disables comments
scrapeCommentsboolean⬜ NotrueCollects personal data when on. false = metadata only
scrapeChannelInfoboolean⬜ NotrueEmit a channel record for channel URLs
proxyConfigurationobject⬜ No{"useApifyProxy": true}Apify proxy editor object. Leave enabled.

At least one of the four input arrays must be non-empty. None is marked required in the schema, so this is enforced at runtime: a run without any of them succeeds with an empty dataset and an error line in the log.

What each input produces

InputRecords emitted per item
searchQueriesup to maxVideos × video (source: "search"), each optionally followed by its comment rows
hashtagsthe same, searched as #tag
channelUrlsone channel row (if scrapeChannelInfo), then up to maxVideos × video (source: "channel") + comments
videoUrlsone video row (source: "direct_url") plus its comment rows

Delivery (optional)

ParameterTypeDefaultDescription
mcpConnectorstring""A connector you've authorised on your Apify account — Notion, Slack, Linear, Jira, GitHub, Airtable, or any MCP-compatible connector. Leave empty for dataset-only runs.
deliveryModestring (enum)"summary"summary (one call) · chunked (split across calls) · perItem (one call per row) · none.
mcpToolstring""Tool to call — create_page, send_message, create_issue. Required when a connector is set; run once and the log lists the connector's tools.
mcpArgumentsobject{}Arguments for the tool. String leaves support {placeholder} tokens.
mcpMessageTemplatestring""Template rendered and exposed to mcpArguments as {message}.

Placeholders by mode:

ModePlaceholders
summary{itemCount} {itemsText} (formatted list of every result) {message}
chunkedthe same, plus {part} {partCount}{itemsText} holds one part
perItem{type} {scrapedAt} {message} {itemCount}, plus whichever fields that record type carries — video: {videoId} {title} {url} {viewCount} {likeCount} {duration} {tags} …; channel: {channelName} {subscriberCount} {videoCount} …; comment: {author} {text} {likes} {isReply}

Unknown tokens render empty rather than erroring — which is why {type} matters in perItem. chunked groups results so no single call exceeds roughly 72,000 characters, keeping Notion and similar services from rejecting the payload.


📤 Output Contract & Data Structure

One dataset, three record types. Branch on type first. Every row carries scrapedAt (ISO-8601 UTC).

type: "video"

FieldTypeNullableMeaning
typestringNoAlways "video".
sourcestringNoWhich input produced it: search, channel or direct_url.
videoIdstringYesYouTube's 11-character ID. The join key for comments.
urlstringYesCanonical watch URL.
titlestringYesVideo title.
descriptionstringYesTruncated to 2,000 characters.
channelId / channelName / channelUrlstringYesChannel identity.
viewCountintegerYesViews as of scrapedAt.
likeCountintegerYesFrequently null — creators hide it. Null ≠ zero.
commentCountintegerYesWhat YouTube reports — not the number of comment rows scraped.
durationintegerYesLength in seconds.
durationStrstringYesH:MM:SS, or M:SS under an hour.
thumbnailstringYesHighest-resolution thumbnail available.
isLivebooleanNotrue for live streams.
isShortbooleanNoUnreliable — see the caveat below.
tags / categoriesarrayNo[] when none.
languagestringYesLanguage code YouTube reports.
uploadDatestringYesYYYY-MM-DD (raw YYYYMMDD only if unparseable).
scrapedAtstringNoISO-8601 UTC capture time.

isShort caveat. True only when the video's own page URL was a /shorts/ URL and duration ≤ 60 seconds. Videos reached through search, a channel listing or a /watch?v= URL — nearly everything this Actor returns — normally report false even when they are Shorts. Filter on duration <= 60, or use the YouTube Shorts Scraper, which reads the Shorts tab directly.

type: "channel"

FieldTypeNullableMeaning
typestringNoAlways "channel".
channelId / channelName / channelUrlstringYesChannel identity.
descriptionstringYesTruncated to 2,000 characters.
subscriberCountintegerYesNull when the channel hides it.
videoCountintegerYesPublic video count as YouTube reports it.
thumbnailstringYesChannel avatar URL.
countrystringYesDeclared country. Often null.
scrapedAtstringNoISO-8601 UTC capture time.

type: "comment" — contains personal data

FieldTypeNullableMeaning
typestringNoAlways "comment".
videoIdstringNoJoin key back to the video row.
commentIdstringYesYouTube's comment ID.
textstringYesComment body. Personal data.
authorstringYesDisplay name. Personal data.
authorIdstringYesStable channel identifier for that person. Personal data.
likesintegerYesLikes on the comment.
replyCountintegerYesReplies YouTube reports.
isReplybooleanNotrue when the row is a reply.
publishedAtstringYesRelative text — e.g. "3 weeks ago". Not a date.
scrapedAtstringNoISO-8601 UTC capture time.

Example output — one of each type

[
{
"type": "video",
"source": "search",
"videoId": "aBcDeFgHiJk",
"url": "https://www.youtube.com/watch?v=aBcDeFgHiJk",
"title": "How We Rebuilt Our Ingestion Pipeline",
"description": "Everything we learned moving 40M events a day…",
"channelId": "UC000000000000000000000",
"channelName": "Practical Systems",
"channelUrl": "https://www.youtube.com/@practicalsystems",
"viewCount": 148203,
"likeCount": null,
"commentCount": 412,
"duration": 964,
"durationStr": "16:04",
"thumbnail": "https://i.ytimg.com/vi/aBcDeFgHiJk/maxresdefault.jpg",
"isLive": false,
"isShort": false,
"tags": ["data engineering", "kafka"],
"categories": ["Science & Technology"],
"language": "en",
"uploadDate": "2026-06-14",
"scrapedAt": "2026-08-27T09:12:44.183921+00:00"
},
{
"type": "channel",
"channelId": "UC000000000000000000000",
"channelName": "Practical Systems",
"channelUrl": "https://www.youtube.com/@practicalsystems",
"description": "Weekly deep dives into systems that actually run in production.",
"subscriberCount": 128000,
"videoCount": 214,
"thumbnail": "https://yt3.googleusercontent.com/…",
"country": null,
"scrapedAt": "2026-08-27T09:12:41.006512+00:00"
},
{
"type": "comment",
"videoId": "aBcDeFgHiJk",
"commentId": "UgxAbCdEf1234567890",
"text": "The backpressure section finally made this click for me.",
"author": "@some_viewer",
"authorId": "UC111111111111111111111",
"likes": 34,
"replyCount": 2,
"isReply": false,
"publishedAt": "3 weeks ago",
"scrapedAt": "2026-08-27T09:12:47.771044+00:00"
}
]

Values illustrate shape and formatting — the channel, video and commenter are invented. Note likeCount: null (that creator hid likes) and publishedAt as relative text.

What a successful run looks like

  • Status: SUCCEEDED
  • Dataset: interleaved video, channel and comment rows, in input order
  • Log: one line per input (Searching YouTube for: …, Scraping channel: …), then Scraping complete.
  • Zero results is also a success. No inputs supplied, a bot-block, or an unresolvable channel all return an empty dataset with SUCCEEDED. Check the log.
  • Per-item failures are logged and skipped[yt-dlp] Error extracting … or [comments] Failed for … — and the run continues.

Dataset views

ViewShows
Results overviewType, title, channel, views, likes, comments, duration, upload date
Video detailEngagement, duration, live/Short flags, language, tags, categories, source
CommentsVideo ID, author, text, likes, reply count and reply flag
ChannelsChannel name, subscribers, video count, country, description

▶️ Invocation & Integration

Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.

Apify Console

Open the Actor pageInput tab → fill in at least one of search queries, hashtags, channel URLs or video URLs → set maxVideos and maxComments → decide whether you want comments at all → leave the proxy on → Start.

Model Context Protocol (MCP)

claude mcp add --transport http apify \
"https://mcp.apify.com?tools=techforce.global/youtube-scraper"

Then ask your agent for "the top videos and comment sentiment for this search term".

Python (apify-client)

import os
from collections import Counter
from apify_client import ApifyClient
client = ApifyClient(os.getenv("APIFY_TOKEN"))
run = client.actor("techforce.global/youtube-scraper").call(run_input={
"searchQueries": ["apify web scraping"],
"maxVideos": 20,
"scrapeComments": False, # metadata only — no personal data
"maxComments": 0,
"deliveryMode": "none",
"proxyConfiguration": {"useApifyProxy": True},
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
# An empty dataset is a SUCCEEDED run — check it before assuming failure.
if not rows:
raise SystemExit("Nothing scraped — check the run log (no input? blocked?).")
print(Counter(r["type"] for r in rows)) # e.g. Counter({'video': 20})
# ALWAYS branch on type — one dataset, three shapes.
videos = [r for r in rows if r["type"] == "video"]
# likeCount is often null (creator hid it) — exclude, don't coerce to 0.
liked = [v for v in videos if v.get("likeCount") is not None]
for v in sorted(liked, key=lambda v: v["viewCount"] or 0, reverse=True)[:10]:
rate = v["likeCount"] / v["viewCount"] if v.get("viewCount") else 0
print(f'{v["viewCount"]:>10,} {rate:>6.2%} {v["title"][:60]}')
# isShort is unreliable through search — use duration instead.
print(f'{sum(1 for v in videos if (v.get("duration") or 999) <= 60)} likely Shorts by duration')

JavaScript / TypeScript (apify-client)

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('techforce.global/youtube-scraper').call({
channelUrls: ['https://www.youtube.com/@mkbhd'],
maxVideos: 10,
scrapeComments: true,
maxComments: 100,
deliveryMode: 'none',
proxyConfiguration: { useApifyProxy: true },
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
// Three record types in one dataset — split them first.
const videos = items.filter((i) => i.type === 'video');
const comments = items.filter((i) => i.type === 'comment');
const channels = items.filter((i) => i.type === 'channel');
// Comments join back to their video on videoId.
const byVideo = new Map(videos.map((v) => [v.videoId, { ...v, comments: [] as any[] }]));
for (const c of comments) byVideo.get(c.videoId)?.comments.push(c);
for (const v of byVideo.values()) {
// commentCount is YouTube's number; v.comments.length is what we actually got.
console.log(`${v.title}: ${v.comments.length}/${v.commentCount ?? '?'} comments scraped`);
}
console.log(channels[0]?.subscriberCount ?? 'subscriber count hidden');

cURL — synchronous (waits, returns the dataset)

curl -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
-d '{
"searchQueries": ["python tutorial 2024"],
"maxVideos": 10,
"scrapeComments": false,
"deliveryMode": "none",
"proxyConfiguration": { "useApifyProxy": true }
}' \
'https://api.apify.com/v2/acts/techforce.global~youtube-scraper/run-sync-get-dataset-items'
# 1. Start with a hard spend limit
curl -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
-d '{ "channelUrls": ["https://www.youtube.com/@handle"], "maxVideos": 200, "maxComments": 200,
"proxyConfiguration": { "useApifyProxy": true } }' \
'https://api.apify.com/v2/acts/techforce.global~youtube-scraper/runs?maxTotalChargeUsd=10'
# 2. Poll, then 3. fetch the dataset when SUCCEEDED
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' 'https://api.apify.com/v2/actor-runs/<runId>'
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json'

maxTotalChargeUsd is the hard ceiling. Always set it when maxVideos × maxComments runs into the thousands.

Metadata-only configuration — no personal data

{
"searchQueries": ["ai agents 2026"],
"hashtags": ["#llm"],
"maxVideos": 50,
"scrapeComments": false,
"maxComments": 0,
"deliveryMode": "none",
"proxyConfiguration": { "useApifyProxy": true }
}

Video rows only — no author, no authorId, no comment text. Faster, cheaper, and out of scope for most of the compliance burden. Start here.

With MCP delivery — a digest into Slack

{
"searchQueries": ["apify web scraping"],
"maxVideos": 15,
"scrapeComments": false,
"proxyConfiguration": { "useApifyProxy": true },
"mcpConnector": "<your-authorized-slack-connector>",
"mcpTool": "send_message",
"deliveryMode": "summary",
"mcpMessageTemplate": "Scraped {itemCount} YouTube results:\n\n{itemsText}",
"mcpArguments": { "channel": "#youtube-watch", "text": "{message}" }
}

For competitor tracking, put a fixed channelUrls input on a schedule with comments off, and join runs on videoId to watch viewCount curves over time.


⚠️ Autonomous Agent Error Handling & Resolution Matrix

Read this first: nothing about the content fails the run. Missing inputs, a blocked request, an unextractable video and a comment thread that won't load are all logged while the run continues to SUCCEEDED. An agent that checks run status alone will read an empty dataset as success-with-data — check the row count and the log.

The CONDITION labels are documentation handles for observable states; this Actor writes no error code into the dataset.

ConditionWhat you observeCauseDeterministic agent action
NO_INPUT_PROVIDEDSUCCEEDED, empty dataset; log: No input provided. Supply at least one of: …All four input arrays empty — not caught by validationModify Input: supply at least one array.
BOT_CHECK_BLOCKEDEmpty/thin dataset; log: [yt-dlp] Error extracting …, sign-in-to-confirm errorsProxy disabled or platform IP blockedModify Input: {"useApifyProxy": true} and retry.
NO_PROXY_CONFIGUREDLog: No proxy configured — YouTube is likely to block requests…proxyConfiguration omitted or unusableModify Input: enable Apify Proxy before retrying anything else.
CHANNEL_UNRESOLVEDThat channel contributes no rows; log: [yt-dlp] Error extracting <url>Not a resolvable channel, or blockedModify Input: use the @handle or /channel/ID form.
VIDEO_EXTRACT_FAILEDA requested video URL produced no rowPrivate, deleted, region-blocked, or challengedDo not retry blindly. Verify the URL opens signed-out.
COMMENTS_FAILEDVideo rows present, no comment rows; log: [comments] Failed for <videoId>Comments disabled on that video, or the fetch was blockedExpected, partial. Retry once.
TYPE_MISMATCH_IN_CONSUMERConsumer crashes reading title / textOne dataset, three shapesFix consumer: branch on type.
SHORT_NOT_FLAGGEDA Short has isShort: falseFlag needs a /shorts/ page URL and ≤60sFix expectation: filter duration <= 60, or use the Shorts Scraper.
NULL_LIKE_COUNTlikeCount / subscriberCount nullCreator or channel hides the numberExpected. Null ≠ zero.
COMMENT_COUNT_MISMATCHFewer comment rows than commentCountmaxComments cap, or YouTube served fewerExpected. Raise maxComments (max 1,000).
PUBLISHED_AT_NOT_PARSEABLEpublishedAt won't parse as a dateRelative text by designFix expectation: use scrapedAt.
DUPLICATE_VIDEOSSame videoId twice, different sourceMatched by two inputs; no dedupExpected. Dedupe on videoId.
ITEM_EXPLOSIONDataset far larger than expectedmaxVideos per input × maxComments per videoModify Input: lower both, or scrapeComments: false.
FREE_PLAN_CAPPED5 items; log: Free plan detected - capping results to 5 item(s).Free plan — cap is total, not per inputUpgrade the plan.
CONNECTOR_CALL_STORMHundreds/thousands of connector callsperItem sends one call per row, comments includedModify Input: switch to chunked or summary.
CONNECTOR_TOOL_NOT_FOUNDLog: Tool 'x' is not available on this connector. Available tools: …Wrong mcpTool name (or empty — logged separately)Modify Input: copy an exact name from that log line. Dataset unaffected.
CONNECTOR_UNREACHABLELog: Could not reach the connector: … or APIFY_MCP_PROXY_URL is not setConnector not authorised, or running locallyAuthorise it, or apify push and run on the platform. Data already saved.
AUTH_INVALIDHTTP 401Missing or malformed APIFY_TOKENAbort & prompt user for a valid token.
RATE_LIMITEDHTTP 429Apify API rate / concurrency limitsRetry: exponential backoff (2s, 4s, 8s).
SYNC_TIMEOUTHTTP 408 on the sync endpointRun exceeded the synchronous ceilingReconfigure: async POST /runs + polling.

Provenance: every log line, status and dataset condition above is taken from this Actor's own source and verified against it. The three HTTP rows describe the Apify API transport layer, not this Actor — confirm them against the Apify API reference before wiring them into automated retry logic.


🗣️ Natural Language → Actor Mapping

A user says…IntentConstructed Actor input
"Find YouTube videos about X"Search{"searchQueries":["X"],"maxVideos":20}
"Top videos for this hashtag"Hashtag discovery{"hashtags":["#X"],"maxVideos":20}
"What has this channel posted lately?"Channel listing{"channelUrls":["<url>"],"maxVideos":25}
"How many subscribers does this channel have?"Channel stats{"channelUrls":["<url>"],"maxVideos":1,"scrapeChannelInfo":true}
"Get the stats for these three videos"Direct metadata{"videoUrls":["<a>","<b>","<c>"],"scrapeComments":false}
"What are people saying in the comments?"Comment mining{"videoUrls":["<url>"],"scrapeComments":true,"maxComments":500}
"Sentiment on this topic across YouTube"Social listening{"searchQueries":["<topic>"],"maxVideos":10,"maxComments":200}
"Just the video data, no personal data"Privacy-safe pull{"searchQueries":["<topic>"],"scrapeComments":false,"maxComments":0}
"Compare two competitors' channels"Competitive analysis{"channelUrls":["<A>","<B>"],"maxVideos":25,"scrapeComments":false}
"Which of their videos performed best?"RankingChannel input, then sort on viewCount excluding nulls
"Track this channel weekly"Scheduled monitorFixed input on a schedule; join runs on videoId
"Which of these are Shorts?"Format filterScrape, then filter duration <= 60isShort under-reports
"Put these results in Notion"Scrape + deliverAbove, plus mcpConnector, mcpTool, deliveryMode: "chunked"
"It returned nothing"Empty resultCheck an input array was supplied, and that Apify Proxy is on
"What did they actually say in the video?"Transcript➡️ Transcript Scraper
"Transcripts for their whole channel"Corpus➡️ Full Channel Transcripts Scraper
"List all their Shorts / download them"Short-form➡️ Shorts Scraper

Should NOT route here: transcripts or captions (→ Transcript / Full Channel) · a channel's Shorts catalogue or video files (→ Shorts Scraper) · creator analytics — watch time, retention, revenue · viewer demographics · private, unlisted or members-only content · posting comments, uploading, or any write action · non-YouTube video platforms (Vimeo, TikTok, Twitch).


🧭 Agent Execution & Routing Logic

Does the user want YouTube NUMBERS or COMMENTS (not words)?
├── NO → one of the siblings.
│ one video's transcript → YouTube Transcript Scraper
│ a channel's transcripts → Full Channel Transcripts Scraper
│ a channel's Shorts/files → YouTube Shorts Scraper
│ watch time / revenue → creator's own account only
└── YES
├── Which input do they have? (mix freely; at least ONE is mandatory at
│ runtime even though the schema marks none required)
│ ├── a topic → searchQueries
│ ├── a hashtag → hashtags (run as a "#tag" search)
│ ├── a creator → channelUrls (adds a channel row)
│ └── specific videos → videoUrls (most predictable item count)
├── ALWAYS keep proxyConfiguration enabled (useApifyProxy: true)
├── Do they want COMMENTS?
│ ├── no → scrapeComments: false, maxComments: 0
│ │ → smaller, faster, and NO personal data
│ └── yes → maxComments is PER VIDEO (0-1000)
│ ├── personal data: author, authorId, text → see Compliance
│ └── multiply: videos × maxComments = row count
├── How many videos? maxVideos is PER search / hashtag / channel (1-500)
│ ├── a probe → 3 ├── analysis → 20-50 └── deep → + maxTotalChargeUsd
├── What is the actual question?
│ ├── best performers → sort viewCount, EXCLUDING nulls
│ ├── like-rate → likeCount / viewCount, both non-null only
│ ├── Shorts vs long → duration <= 60 (NOT isShort)
│ ├── audience mood → comment text + likes, join on videoId
│ └── growth over time→ SCHEDULE it; join on videoId
└── Should results land in another app?
├── yes → mcpConnector + mcpTool + {message} in mcpArguments
│ one digest → summary · long list → chunked
│ per record → perItem (ONE CALL PER ROW, comments included)
└── no → deliveryMode "none", or leave mcpConnector empty
[Execute Actor]
├──► SUCCEEDED with rows → split by `type`, then consume
├──► SUCCEEDED, 0 rows → READ THE LOG: no input / blocked / unresolvable
└──► exactly 5 rows → free plan cap, not a data pull

💰 Cost Control & Pricing Transparency

Billing model: usage-based. This Actor bills for platform usage — the compute time and resources a run consumes — not per row. Current rates are on the Actor's Apify Store page; the Console shows the live total mid-run.

ChoiceEffect on cost
maxVideosThe main driver, and it applies per search, hashtag and channel.
Number of inputsMultiplies maxVideos — three searches at 20 is 60 videos, not 20.
scrapeCommentsThe second-biggest lever: one extra fetch per video, and comment rows dominate dataset size.
maxCommentsUp to 1,000 rows per video. 20 videos × 500 = 10,000 rows.
Channel inputsEach video in a channel needs its own full metadata request.
scrapeChannelInfoNegligible — one row per channel from data already fetched.
Being blockedCosts run time and returns nothing. A disabled proxy is the most expensive setting on this page.

Worked examples — relative run shape

ScenarioVideosComment rowsTotal rowsNotes
Probe — 1 search, maxVideos: 3, no comments303Baseline. Confirms proxy and input work.
Topic scan — 1 search, 20 videos, no comments20020The cheap research shape.
Same, with maxComments: 5020≤ 1,000≤ 1,020Comments dominate immediately.
Two searches, 20 each, maxComments: 5040≤ 2,000≤ 2,040The execution-rules example.
Channel audit — 1 channel, 25 videos, no comments25026Plus one channel row.
Comment deep-dive — 2 video URLs, maxComments: 5002≤ 1,000≤ 1,002Most predictable comment-heavy shape.
Broad sweep — 5 searches × 100 videos × 200 comments500≤ 100,000≤ 100,500Set maxTotalChargeUsd before running this.

Keeping runs predictable:

  1. First run: one input, maxVideos: 3, scrapeComments: false.
  2. Decide the comment question up front — it's the difference between a 20-row dataset and a 1,020-row one, and between being in and out of scope for data-protection obligations.
  3. Prefer videoUrls when you can — a search's item count is a prediction; a list of video URLs is arithmetic.
  4. Always set maxTotalChargeUsd on runs whose row count reaches the thousands.
  5. Cache by videoId so scheduled re-runs don't re-pay for unchanged videos.
  6. Free Apify plans get 5 items per run — a connectivity check, not a data pull.

🛠️ Troubleshooting

Most problems are missing input, the proxy, or the three-record-type shape — not scraper bugs.

#Symptom you seeMost likely causeFix
1SUCCEEDED but the dataset is emptyNo input array supplied — the schema doesn't enforce itRead the log for No input provided…, then supply searchQueries, hashtags, channelUrls or videoUrls.
2Empty or thin results despite valid inputBot check — YouTube blocks Apify's platform IPsConfirm proxyConfiguration is {"useApifyProxy": true}; the log warns when no proxy is configured.
3Consumer crashes on a missing fieldOne dataset holds three shapesBranch on type before reading any field.
4A Short shows isShort: falseFlag needs a /shorts/ page URL and ≤60s; search/channel inputs don't provide thatFilter duration <= 60, or use the Shorts Scraper.
5likeCount / subscriberCount nullCreator or channel hides the numberExpected. Null ≠ zero — exclude from averages.
6Far fewer comment rows than commentCountmaxComments cap, or YouTube served fewerRaise maxComments (max 1,000). Completeness isn't guaranteed.
7No comment rows at allscrapeComments: false, maxComments: 0, or comments disabledCheck the input, then the log for [comments] Failed for <videoId>.
8publishedAt won't parse as a dateIt's YouTube's relative text ("3 weeks ago")Use scrapedAt (ISO-8601 UTC) for timing.
9The description looks cut offTruncated at 2,000 characters by designFetch the page yourself if you need the full text.
10The same video appears twiceMatched two inputs; source differsNo dedup by design — dedupe on videoId.
11Dataset far bigger than expectedmaxVideos is per input; maxComments is per videoDo the multiplication, lower the caps, or turn comments off.
12The run takes far longer than expectedComments add a fetch per video; channels add a full request per videoRun metadata-only first, then target specific videos.
13Exactly 5 rows came backFree plan cap — total, not per inputUpgrade the plan; the log says Free plan detected….
14A free-plan run returned slightly more than 5 rowsThe cap counts push batches, and a videoUrls entry pushes its video and comments togetherExpected on free plans; paid runs are uncapped.
15One channel produced nothing in a multi-channel runThat channel's extraction failed — [yt-dlp] Error extracting <url>Re-run it alone with the @handle form.
16A specific video URL produced no rowPrivate, deleted, region-blocked, or challengedOpen it signed-out in a browser to confirm.
17Hundreds of connector calls firedperItem sends one call per row, comments includedSwitch to chunked or summary.
18Connector messages have blank fieldsperItem template referenced a field that record type lacksBranch the template on {type}, or use summary.
19Slack / Notion received nothingDelivery needs both mcpConnector and mcpToolSet both. Every dataset row is still saved.
20Delivery fired when you only wanted a datasetdeliveryMode defaults to summary, not noneSet deliveryMode: "none" or leave mcpConnector empty.
21Log: APIFY_MCP_PROXY_URL is not setRan locally; connectors only resolve on the platformapify push and run on the platform.
22HTTP 401 / 403Missing, expired or malformed APIFY_TOKENRegenerate in Console → Settings → API & Integrations; send as Authorization: Bearer <token>.
23HTTP 429 / 408Apify rate limits / the sync endpoint's time ceilingBack off (2s → 4s → 8s), or switch to async POST /runs + polling.

Diagnostic checklist before opening an issue

  1. Confirm an input array is non-empty — the most common cause of an empty run.
  2. Confirm Apify Proxy is enabled and the log has no no-proxy warning.
  3. Baseline run: one searchQueries entry, maxVideos: 3, scrapeComments: false.
  4. Count rows by type before concluding anything is missing.
  5. Open the failing URL signed-out — private, deleted and region-blocked videos look identical to a scraper.
  6. Read the log — per-item failures appear as [yt-dlp] Error extracting … and [comments] Failed for ….

If the issue survives all six, open an Issues ticket (or email support) with the run ID, the input you used, and what you expected.


❓ FAQ

Do I need a YouTube Data API key or a Google Cloud project?

No. The Actor reads publicly available YouTube pages. You need an Apify account and API token; you need no Google credentials whatsoever.

Is this the official YouTube API?

No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by YouTube or Google.

What's the real advantage over the Data API?

The quota. The official API gives you 10,000 units a day and a single search costs 100 of them — roughly 100 searches before you're locked out until midnight Pacific. This Actor has no quota, no Google Cloud project and no OAuth flow. The trade-off: the API is the sanctioned route with a versioned contract, while this Actor depends on YouTube's page structure.

Do I need to configure proxies?

Apify Proxy is on by default and you should leave it on. YouTube blocks the platform's shared IPs with a "Sign in to confirm you're not a bot" wall, which shows up as an empty or thin dataset. A fresh IP is rotated in per request.

Can I plug it into Claude, Cursor, or a LangChain agent?

Yes — it is a native MCP tool: claude mcp add --transport http apify "https://mcp.apify.com?tools=techforce.global/youtube-scraper"

Which inputs can I combine in one run?

All four — searchQueries, hashtags, channelUrls and videoUrls — freely mixed. They're processed in that order, and every video row carries a source field so you can tell which input produced it.

How do hashtags work?

Each hashtag is run as a search for #tag (the # is added if you leave it off). That's the honest description: it is a search, not a separate hashtag-page crawl. The official API has no hashtag concept at all.

How many items will my run produce?

Multiply before you run. maxVideos is per search, hashtag and channel; maxComments is per video. Two searches at maxVideos: 20 with maxComments: 50 is up to 40 videos + 2,000 comments = 2,040 rows.

How do I get video metadata without collecting personal data?

Set scrapeComments: false (or maxComments: 0). You get video and channel rows only — no author names, no author IDs, no comment text.

Why do my rows have different shapes, and how do I join them?

One dataset holds three record types — video, channel and comment — discriminated by the type field. Always branch on type before reading fields; a consumer that assumes every row has title breaks on the first comment. Comment rows carry videoId, which joins them back to their video.

Why is likeCount null?

The creator hid the like count; subscriberCount behaves the same way on channels. Null is not zero — exclude those rows from averages rather than coercing them.

Are the dates ISO-8601?

uploadDate is normalised to YYYY-MM-DD and scrapedAt is full ISO-8601 UTC. A comment's publishedAt is relative text such as "3 weeks ago", because that is all YouTube publishes — use scrapedAt for anything time-sensitive.

Why is isShort false for a video I know is a Short?

Because the flag requires the video's own page URL to be a /shorts/ URL and the duration to be 60 seconds or less. Videos reached through search, a channel listing or a /watch?v= URL — nearly everything this Actor returns — normally report false. Filter on duration <= 60, or use the YouTube Shorts Scraper, which reads the Shorts tab directly.

Why did I get fewer comments than commentCount says?

commentCount is the number YouTube reports for the video. The rows you receive are capped by maxComments (max 1,000) and by what YouTube actually served.

Can the same video appear twice?

Yes. There is no deduplication: a video matched by two searches, or by a search and a channel, is written twice with a different source. Dedupe on videoId downstream.

Can I get transcripts, or download the video files?

Neither. For transcripts use the YouTube Transcript Scraper (one video) or the Full Channel Transcripts Scraper. For a channel's Shorts as MP4 or audio, use the YouTube Shorts Scraper. This Actor returns metadata and thumbnail URLs only.

Which delivery mode should I use, and does it work locally?

summary for a single digest, chunked for large result sets into services with request caps (Notion). Avoid perItem unless you really want one record each: delivery reads the whole dataset, so it issues one call per row — comments included, which can be thousands. Delivery only works on the Apify platform; locally you'll see APIFY_MCP_PROXY_URL is not set and the dataset is saved as normal.

Do comments contain personal data?

Yes. author, authorId and text identify real people, and authorId is a stable identifier for an individual. scrapeComments defaults to true, so your first run collects them unless you turn it off. Read Compliance & Data Privacy before you store or reuse that data.

What does it cost, and what do I get on the free plan?

Usage-based: you pay for the platform usage a run consumes, not per row. The drivers are maxVideos, the number of inputs, and whether comments are on. Free Apify plans return 5 items total per run — a connectivity check, not a data pull. Pass maxTotalChargeUsd on the run endpoint for a hard per-run ceiling.


🔍 Companion machine-readable files

FileWhat it defines
.actor/input_schema.jsonEvery input, its type, default, bounds and the delivery enum
.actor/dataset_schema.jsonEvery field across all three record types, and the four Console views
.actor/output_schema.jsonWhere the run's output is exposed
.actor/actor.jsonActor identity and storage wiring

If this page and the schema files disagree, the schema files win. They are what the platform validates against at run time; this page is documentation and can lag a release.


This Actor is the numbers-and-comments specialist: discovery, metadata and audience reaction.

ActorBest forWhy pick it over this one
YouTube Transcript ScraperOne video's timestamped transcript, 13 languages with auto-translationYou want what was said in a specific video, not how it performed.
YouTube Full Channel Transcripts ScraperTranscripts for every video on a channelYou're building a RAG corpus or searchable archive of a creator's words.
YouTube Shorts ScraperA channel's Shorts with engagement, optional MP4/audio downloadYou need the Shorts tab specifically — this Actor's isShort under-reports — or the media files.
Advanced Website Crawling ActorWhole-site text, Markdown or HTMLYou want a creator's or competitor's site in the same corpus as their video data.
Pinterest ScraperPins from searches, boards, profilesYou're comparing the same content strategy on another visual platform.

Pipeline patterns: topic landscape — several searchQueries, comments off, rank by viewCount, then send the winners to the Full Channel Transcripts Scraper for a content-gap analysis · audience sentimentvideoUrls with maxComments: 500, comment text into your LLM, digest to Slack via MCP · competitor trackingchannelUrls on a schedule, comments off, join runs on videoId to watch view curves and posting cadence.

Browse all Actors by Techforce Global at scraper.techforce.global.


🔐 Compliance & Data Privacy

Trademark & affiliation. This is an independent Actor. It is not affiliated with, endorsed by, or sponsored by YouTube or Google. YouTube™ is a trademark of Google LLC; all trademarks are the property of their respective owners.

  • Public content only. Everything returned is visible to any signed-out visitor. No private, unlisted or members-only content, no login, no stored credentials.

  • ⚠️ Comments are personal data — and they're on by default. author, authorId and text identify real people, and authorId is a stable identifier that lets you track one person across videos. Under GDPR / UK GDPR and CCPA/CPRA, collecting and storing them is processing personal data, and the obligations sit with you, not with the Actor.

  • The cheapest compliance measure is a single field. If your question is about videos rather than people, set scrapeComments: false. You keep every video and channel metric and remove the personal data entirely.

  • If you do collect comments: have a lawful basis, keep retention short, restrict access, and don't re-publish individuals' comments out of context. Comment text is unfiltered public speech — it can contain special-category content (health, politics, religion, sexuality) and abuse, and you cannot know which threads contain it until after retrieval.

  • Aggregate rather than store where you can. Sentiment scores, theme counts and engagement ratios rarely require keeping the raw author / authorId columns. Drop them at ingest.

  • Public does not mean consented. Someone commenting under a video did not agree to be profiled, ranked, or added to a dataset that follows them across channels. Treat authorId linkage as the sensitive operation it is.

  • Video content and metadata are the creators' work. Internal research, analysis and reporting sit on far safer ground than republishing scraped descriptions, thumbnails or metrics as your own content.

  • Counts are a snapshot. viewCount, likeCount and subscriberCount are true as of scrapedAt, and hidden values come back null, not 0. Publishing rankings or like-rates without saying when you captured them — or while silently treating nulls as zeros — produces numbers that look authoritative and aren't.

  • YouTube's Terms of Service prohibit automated access. This Actor does not remove that risk — it is a risk you accept by running it. The official Data API is the sanctioned route; its quota is the reason this Actor exists.

  • Attribution over mirroring. url and channelUrl are on every video row. Link back and credit the creator rather than presenting their content or their numbers as your own.


🆘 Support & Custom Pipeline Engineering

Need automated multi-city scheduled feeds, webhook enrichment, or enterprise data warehouse syncs?



🏷️ Structured data for search & AI discovery

SoftwareApplication describes the Actor; FAQPage wraps the FAQ above so those answers are eligible for rich results, matching it question-for-question.

{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "YouTube Scraper – Videos, Channels & Comments",
"alternateName": "YouTube Data API Alternative",
"applicationCategory": "BusinessApplication",
"applicationSubCategory": "Web Scraping / Video & Audience Analytics",
"operatingSystem": "Cloud (Apify platform)",
"description": "Scrapes YouTube videos, channels and comments from search terms, hashtags, channel URLs or video URLs without a YouTube Data API key, Google Cloud project, or daily quota. Returns three record types in one dataset — video metadata with view, like and comment counts; channel stats including subscribers; and comment threads with author, likes and reply flags — each discriminated by a type field. Public content only, with comment scraping switchable off in one field for callers who do not want personal data. Apify Proxy rotates a fresh IP per request, and results can be delivered into Notion, Slack, Linear, Airtable or any MCP connector.",
"url": "https://apify.com/techforce.global/youtube-scraper",
"author": { "@type": "Organization", "name": "Techforce Global", "url": "https://www.techforceglobal.com" },
"provider": { "@type": "Organization", "name": "Apify", "url": "https://apify.com" },
"offers": {
"@type": "Offer",
"priceCurrency": "USD",
"category": "Pay per platform usage",
"description": "Usage-based: billed for the compute time and resources a run consumes, not per row. Current rates are shown on the Actor's Apify Store page.",
"url": "https://apify.com/techforce.global/youtube-scraper"
},
"featureList": [
"Four input types in one run: search terms, hashtags, channel URLs and video URLs",
"No YouTube Data API key, Google Cloud project, OAuth or daily quota",
"Three record types in one dataset, discriminated by a type field",
"Video metadata with view, like and comment counts, duration and tags",
"Channel stats including subscriber and video counts",
"Comment threads with author, likes, reply count and reply flag, up to 1,000 per video",
"Comment scraping switchable off in one field for a personal-data-free dataset",
"source field records which input produced each video",
"uploadDate normalised to YYYY-MM-DD and scrapedAt in ISO-8601 UTC",
"Apify Proxy with a fresh IP rotated in per request",
"Per-item failures logged and skipped so one bad video never ends the run",
"Optional MCP delivery to Notion, Slack, Linear, Airtable and more",
"Four Console dataset views covering videos, comments and channels"
],
"isAccessibleForFree": false,
"softwareRequirements": "Apify account and API token",
"keywords": "YouTube Data API alternative, YouTube scraper, YouTube comment scraper, video metadata extraction, channel statistics, hashtag search, audience insight, social listening, content research, MCP connector"
}
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Do I need a YouTube Data API key or a Google Cloud project?",
"acceptedAnswer": { "@type": "Answer", "text": "No. The Actor reads publicly available YouTube pages. You need an Apify account and API token; you need no Google credentials whatsoever." }
},
{
"@type": "Question",
"name": "Is this the official YouTube API?",
"acceptedAnswer": { "@type": "Answer", "text": "No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by YouTube or Google." }
},
{
"@type": "Question",
"name": "What's the real advantage over the Data API?",
"acceptedAnswer": { "@type": "Answer", "text": "The quota. The official API gives you 10,000 units a day and a single search costs 100 of them — roughly 100 searches before you're locked out until midnight Pacific. This Actor has no quota, no Google Cloud project and no OAuth flow. The trade-off: the API is the sanctioned route with a versioned contract, while this Actor depends on YouTube's page structure." }
},
{
"@type": "Question",
"name": "Do I need to configure proxies?",
"acceptedAnswer": { "@type": "Answer", "text": "Apify Proxy is on by default and you should leave it on. YouTube blocks the platform's shared IPs with a \"Sign in to confirm you're not a bot\" wall, which shows up as an empty or thin dataset. A fresh IP is rotated in per request." }
},
{
"@type": "Question",
"name": "Can I plug it into Claude, Cursor, or a LangChain agent?",
"acceptedAnswer": { "@type": "Answer", "text": "Yes — it is a native MCP tool: claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/youtube-scraper\"" }
},
{
"@type": "Question",
"name": "Which inputs can I combine in one run?",
"acceptedAnswer": { "@type": "Answer", "text": "All four — searchQueries, hashtags, channelUrls and videoUrls — freely mixed. They're processed in that order, and every video row carries a source field so you can tell which input produced it." }
},
{
"@type": "Question",
"name": "How do hashtags work?",
"acceptedAnswer": { "@type": "Answer", "text": "Each hashtag is run as a search for #tag (the # is added if you leave it off). That's the honest description: it is a search, not a separate hashtag-page crawl. The official API has no hashtag concept at all." }
},
{
"@type": "Question",
"name": "How many items will my run produce?",
"acceptedAnswer": { "@type": "Answer", "text": "Multiply before you run. maxVideos is per search, hashtag and channel; maxComments is per video. Two searches at maxVideos: 20 with maxComments: 50 is up to 40 videos + 2,000 comments = 2,040 rows." }
},
{
"@type": "Question",
"name": "How do I get video metadata without collecting personal data?",
"acceptedAnswer": { "@type": "Answer", "text": "Set scrapeComments: false (or maxComments: 0). You get video and channel rows only — no author names, no author IDs, no comment text." }
},
{
"@type": "Question",
"name": "Why do my rows have different shapes, and how do I join them?",
"acceptedAnswer": { "@type": "Answer", "text": "One dataset holds three record types — video, channel and comment — discriminated by the type field. Always branch on type before reading fields; a consumer that assumes every row has title breaks on the first comment. Comment rows carry videoId, which joins them back to their video." }
},
{
"@type": "Question",
"name": "Why is likeCount null?",
"acceptedAnswer": { "@type": "Answer", "text": "The creator hid the like count; subscriberCount behaves the same way on channels. Null is not zero — exclude those rows from averages rather than coercing them." }
},
{
"@type": "Question",
"name": "Are the dates ISO-8601?",
"acceptedAnswer": { "@type": "Answer", "text": "uploadDate is normalised to YYYY-MM-DD and scrapedAt is full ISO-8601 UTC. A comment's publishedAt is relative text such as \"3 weeks ago\", because that is all YouTube publishes — use scrapedAt for anything time-sensitive." }
},
{
"@type": "Question",
"name": "Why is isShort false for a video I know is a Short?",
"acceptedAnswer": { "@type": "Answer", "text": "Because the flag requires the video's own page URL to be a /shorts/ URL and the duration to be 60 seconds or less. Videos reached through search, a channel listing or a /watch?v= URL — nearly everything this Actor returns — normally report false. Filter on duration <= 60, or use the YouTube Shorts Scraper, which reads the Shorts tab directly." }
},
{
"@type": "Question",
"name": "Why did I get fewer comments than commentCount says?",
"acceptedAnswer": { "@type": "Answer", "text": "commentCount is the number YouTube reports for the video. The rows you receive are capped by maxComments (max 1,000) and by what YouTube actually served." }
},
{
"@type": "Question",
"name": "Can the same video appear twice?",
"acceptedAnswer": { "@type": "Answer", "text": "Yes. There is no deduplication: a video matched by two searches, or by a search and a channel, is written twice with a different source. Dedupe on videoId downstream." }
},
{
"@type": "Question",
"name": "Can I get transcripts, or download the video files?",
"acceptedAnswer": { "@type": "Answer", "text": "Neither. For transcripts use the YouTube Transcript Scraper (one video) or the Full Channel Transcripts Scraper. For a channel's Shorts as MP4 or audio, use the YouTube Shorts Scraper. This Actor returns metadata and thumbnail URLs only." }
},
{
"@type": "Question",
"name": "Which delivery mode should I use, and does it work locally?",
"acceptedAnswer": { "@type": "Answer", "text": "summary for a single digest, chunked for large result sets into services with request caps (Notion). Avoid perItem unless you really want one record each: delivery reads the whole dataset, so it issues one call per row — comments included, which can be thousands. Delivery only works on the Apify platform; locally you'll see APIFY_MCP_PROXY_URL is not set and the dataset is saved as normal." }
},
{
"@type": "Question",
"name": "Do comments contain personal data?",
"acceptedAnswer": { "@type": "Answer", "text": "Yes. author, authorId and text identify real people, and authorId is a stable identifier for an individual. scrapeComments defaults to true, so your first run collects them unless you turn it off. Read the Compliance & Data Privacy section before you store or reuse that data." }
},
{
"@type": "Question",
"name": "What does it cost, and what do I get on the free plan?",
"acceptedAnswer": { "@type": "Answer", "text": "Usage-based: you pay for the platform usage a run consumes, not per row. The drivers are maxVideos, the number of inputs, and whether comments are on. Free Apify plans return 5 items total per run — a connectivity check, not a data pull. Pass maxTotalChargeUsd on the run endpoint for a hard per-run ceiling." }
}
]
}