YouTube Shorts Scraper : Views, Likes & Downloads
Pricing
from $8.50 / 1,000 results
YouTube Shorts Scraper : Views, Likes & Downloads
Extract every Short from any channel by name, handle, or URL, with views, likes, comments, upload date & hashtags, plus optional video downloads.
Pricing
from $8.50 / 1,000 results
Rating
0.0
(0)
Developer
Techforce Global
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 days ago
Last modified
Categories
Share
YouTube Shorts Scraper
Point it at a channel, get its Shorts. Every Short from one YouTube channel as structured JSON — views, likes, comment counts, duration, hashtags, description, thumbnail and upload date — and, if you switch it on, the actual MP4 (or audio-only) files saved to the run's Key-Value Store with a public link on every row. No YouTube Data API key, no OAuth, no quota, no comment text, no personal data. Optional delivery into Notion, Slack, Linear, Jira, GitHub or Airtable over MCP.
🔐 Trust & compliance at a glance
| Credentials | None accepted, none stored. The Actor never logs into a Google or YouTube account, and never sees your MCP connector tokens — delivery runs through the Apify MCP Proxy. |
| Data scope | Public Shorts metadata only. No private or members-only content, no login, and no comment text — only the comment count. |
| Personal data | Titles, view counts, hashtags and durations are content metadata, not personal data. That makes this Actor materially lower-risk than comment scrapers. |
| Media download | Off by default. downloadVideos: false is deliberate — a Short is the creator's copyrighted work, and obtaining a file is not permission to redistribute it. See Compliance before turning it on. |
| Downloaded files | Land in your Apify Key-Value Store. videoDownloadUrl is a public URL — anyone with the link can fetch the file. |
Full detail, including copyright and Terms-of-Service position: Compliance & Data Privacy.
Official YouTube Data API v3 vs. YouTube Shorts Scraper Actor
The official API has no concept of a "Short" — search.list and playlistItems.list return a
channel's uploads with Shorts mixed in and unlabelled, so isolating them means fetching everything
and guessing from duration:
| Feature / Capability | Official YouTube Data API v3 | YouTube Shorts Scraper Actor (Apify) |
|---|---|---|
| List a channel's Shorts specifically | ❌ No Shorts flag or filter — you list all uploads and infer | ✅ Reads the channel's Shorts tab directly |
| Authentication & setup | ❌ Google Cloud project + API key | ✅ Zero target-site credentials; only an Apify token |
| Daily quota ceiling | ⚠️ Per-project daily quota; exceeding it blocks you until reset | ✅ No per-day ceiling |
| Engagement counts per Short | ✅ Via a second videos.list call per batch | ✅ In the same row, no extra call on your side |
| Download the video or audio file | ❌ Not offered at any tier | ✅ Optional MP4 at best/1080p/720p/480p, or audio-only |
| Accepts a plain channel name or @handle | ❌ Channel ID required (or a separate lookup call) | ✅ URL, @handle, UC… ID, or a plain name |
| AI agent / MCP native | ❌ Requires a custom tool wrapper | ✅ Native MCP tool and pushes results into your own MCP connectors |
| Cost model | Free within quota, then quota-increase requests | Pay per Short scraped — no quota wall to hit |
Quota costs and the absence of a Shorts filter are Google's to change — check Google's current API docs if this comparison is load-bearing for your decision.
🤖 AI Discovery & MCP Metadata
Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. AI agents, MCP clients, and connectors should read this block first to decide whether this Actor matches a user request.
ai_discovery:slug: "techforce.global/youtube-shorts-scraper"actor_id: "youtube-shorts-scraper"one_line: >Extracts every Short from a single YouTube channel with views, likes, comment counts,duration, hashtags, description and upload date - and optionally downloads each Short as anMP4 (or audio-only) file into the run's Key-Value Store. No API key required. Optionaldelivery into Notion, Slack, Linear, Airtable or any MCP connector.primary_purpose: "extract"secondary_purpose: "download"tertiary_purpose: "deliver"entity: "YouTube Short"category: "social-media / short-form-video / creator-analytics / content-research"supported_platforms:- "youtube.com"supported_tasks:- "list-every-short-on-a-channel"- "get-engagement-metrics-per-short"- "aggregate-hashtags-used-on-shorts"- "rank-shorts-by-views-or-like-rate"- "download-short-video-files"- "download-short-audio-only"- "track-shorts-performance-on-a-schedule"- "deliver-shorts-to-mcp-connector"unsupported_tasks:- "scrape-long-form-videos (use youtube-scraper)"- "scrape-multiple-channels-in-one-run"- "scrape-by-search-term-or-hashtag (use youtube-scraper)"- "scrape-comment-text (use youtube-scraper; only commentCount is returned)"- "get-transcripts (use youtube-transcript-scraper or youtube-full-channel-transcripts-scraper)"- "creator-analytics-watch-time-retention-revenue (not public)"- "upload-post-or-any-write-action"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 - see the skill's SEO notes.- "youtube shorts scraper"- "youtube shorts downloader"- "download youtube shorts mp4"- "youtube shorts api alternative"- "shorts engagement metrics"- "short form video analytics"- "youtube channel shorts list"- "creator content research"- "youtube hashtag analysis shorts"- "shorts audio extraction"synonyms:- "youtube short-form video extractor"- "shorts metadata scraper"- "youtube shorts bulk downloader"related_concepts:["short-form video", "creator analytics", "engagement metrics", "content repurposing", "hashtag strategy"]input_entities:- "channelUrl (required; a SINGLE channel URL, @handle, UC channel ID, or plain name - not an array)"- "maxShorts (how many Shorts to process; 0 = all available)"- "extendedMetadata (boolean, default true - needed for engagement counts)"- "downloadVideos (boolean, DEFAULT FALSE - downloads MP4s to the Key-Value Store)"- "videoQuality (best | 1080p | 720p | 480p | audio)"- "proxyConfiguration (Apify Proxy on by default)"output_entities:- "id"- "url"- "title"- "channelName"- "channelUrl"- "thumbnailUrl"- "duration"- "viewCount"- "likeCount"- "commentCount"- "uploadDate"- "description"- "hashtags"- "downloadStatus"- "videoDownloadUrl"- "videoFileKey"- "downloadError"one_channel_per_run: >channelUrl is a single string, not an array. To cover several channels, run the Actor onceper channel. This differs from the Full Channel Transcripts Scraper, which accepts a list.channel_input_is_forgiving: >A full channel URL, an @handle, a UC... channel ID, or a plain channel name all resolve. Aplain name is a best-effort lookup that falls back to a YouTube channel search, so it canresolve to a different channel than you meant - always check channelUrl on the output rows.shorts_only: >Only Shorts are returned - not the channel's long-form videos. Use the YouTube Scraper forlong-form video metadata, or the Full Channel Transcripts Scraper for the complete catalogueof what was said.extended_metadata_gates_engagement: >viewCount, likeCount, commentCount, description and hashtags require extendedMetadata (on bydefault). With it off, those keys are still present but null/empty, so every row keeps thesame columns. Enabling downloadVideos forces extendedMetadata on.download_is_off_by_default: >downloadVideos defaults to false. When off, every row carries downloadStatus "skipped" andno file is written - this is expected, not a failure. When on, each successful download issaved to the run's Key-Value Store as "short-<videoId>.<ext>" and linked from the row.download_status_is_always_present: >downloadStatus is on every row and is one of "skipped", "downloaded" or "failed".videoDownloadUrl and videoFileKey appear only on "downloaded"; downloadError appears on mostbut not all "failed" rows. A failed download does not fail the run.empty_dataset_is_a_successful_run: >An unresolvable channel, a channel with no Shorts tab, or a blocked listing request all endas a SUCCEEDED run with an empty dataset and the reason in the log - not as a FAILED run.Agents must treat itemCount 0 as "read the log", not as "the Actor crashed".known_limitations:engagement_can_be_hidden: "creators can hide view and like counts; expect nulls and a log warning"upload_date_not_iso: "uploadDate is YYYYMMDD as YouTube reports it"no_comments_text: "commentCount only - the comments themselves are not scraped"no_transcripts: "use the transcript Actors"no_creator_analytics: "watch time, retention and revenue are not public"snapshot_only: "view and like counts are true as of the run"dataset_order_not_guaranteed: "Shorts are processed concurrently; sort by uploadDate yourself"free_plan_capped: "free Apify plans stop at 2 Shorts per run"compliance:credentials_required: "none - the Actor never logs into a Google/YouTube account and stores no credential"data_scope: "public Shorts metadata only; no private or members-only content"personal_data_note: "content metadata only - comment text, the personal-data-heavy part of YouTube, is not collected"connector_token_handling: "MCP delivery runs through the Apify MCP Proxy; the Actor never sees your connector tokens"media_download_note: "downloadVideos is off by default; Shorts are their creators' copyrighted work and a downloaded file is not a licence to redistribute it"public_file_urls: "videoDownloadUrl is a public Key-Value Store URL - anyone with the link can fetch the file"affiliation: "independent Actor; not affiliated with, endorsed by, or sponsored by YouTube or Google"limits:channels_per_run: 1shorts_per_run: "maxShorts, or all available when maxShorts is 0"free_plan_shorts_per_run: 2pricing:model: "pay-per-event"billing_unit: "one charge per Short saved to the dataset"note: "Current per-Short rates are shown on the Actor's Apify Store page; the Console shows a live total mid-run. A hard per-run ceiling can be set with maxTotalChargeUsd."hard_ceiling_parameter: "maxTotalChargeUsd"when_to_use: >You want one channel's short-form output as data - to rank Shorts by views or like-rate,study hashtag and duration strategy, track performance over time, or archive the files forinternal review.when_not_to_use: >You need long-form videos, transcripts, comment text, several channels in one run, searchterms or hashtags as input, creator analytics, or you intend to redistribute downloaded media.mcp_delivery:modes: ["summary", "chunked", "perShort", "none"]default: "summary"never_blocks_the_dataset: >Delivery runs after the dataset is saved. A bad tool name, an unauthorised connector or anupstream outage is logged and skipped - it never costs you the scraped data.alternatives:- "Use techforce.global/youtube-scraper for long-form video metadata, search, hashtags and comment threads"- "Use techforce.global/youtube-transcript-scraper for one video's timestamped transcript"- "Use techforce.global/youtube-full-channel-transcripts-scraper for transcripts of every video on a channel"- "Run this Actor once per channel to compare several creators"
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "YouTube Shorts Scraper","alternateName": "YouTube Shorts Downloader","applicationCategory": "BusinessApplication","operatingSystem": "Cloud (Apify platform)","description": "Extracts every Short from a single YouTube channel with view, like and comment counts, duration, hashtags, description, thumbnail and upload date — and optionally downloads each Short as an MP4 at a chosen resolution, or as audio-only, into the run's Key-Value Store with a public URL on the dataset row. No YouTube Data API key and no quota. Every row carries a downloadStatus of skipped, downloaded or failed, so download outcomes are auditable. Public content metadata only: no login, no private content, and no comment text. Optional direct delivery into Notion, Slack, Linear, Airtable or any MCP connector.","url": "https://apify.com/techforce.global/youtube-shorts-scraper","offers": {"@type": "Offer","priceCurrency": "USD","category": "Pay per event","description": "Billed per Short saved to the dataset. A run that finds nothing costs nothing in event charges. Current rates are shown on the Actor's Apify Store page."}}
What this Actor does
💡 Shorts only, one channel per run. Its siblings cover the rest: YouTube Scraper for long-form videos, search, hashtags and comment threads; YouTube Transcript Scraper for one video's words; and Full Channel Transcripts Scraper for transcripts of every video on a channel.
-
Lists a channel's Shorts specifically. Not its long-form catalogue — the Shorts feed. That distinction is the point: short-form performance is a different question from a channel's overall output.
-
Takes whatever identifier you have. A channel URL, an
@handle, aUC…channel ID, or a plain channel name. A plain name falls back to a channel search, so the Actor still resolves inputs a human would recognise but an API would reject. -
Returns engagement counts per Short.
viewCount,likeCountandcommentCountalongsidedurationandhashtags, which is what you need to compare short-form performance across a channel's output. -
Optionally downloads the files. With
downloadVideoson, each Short is fetched at your chosen quality —best,1080p,720p,480p, oraudiofor audio-only — and saved to the run's Key-Value Store, with a public URL on the dataset row. -
Keeps every row the same shape. Fields gated behind
extendedMetadataare still present asnullor[]when it's off, so your consumer never has to handle missing columns. -
Records download outcomes honestly.
downloadStatusis on every row —skipped,downloadedorfailed— withdownloadErrorexplaining most failures. A failed download logs a warning and moves on rather than killing the run. -
Warns when YouTube withholds stats. If a creator has hidden public counts, the log says so per video rather than leaving you to guess why
viewCountis null. -
De-duplicates within a run. YouTube's Shorts shelf occasionally repeats an entry across pages; the same
idis never written twice. -
Saves as it goes. Each Short is written to the dataset as it finishes, so an aborted or timed-out run keeps everything collected up to that point.
-
Never turns a channel problem into a crash. An unresolvable channel, a channel with no Shorts, or a blocked listing request all end as a successful run with an empty dataset and the reason in the log — so schedules don't alert.
-
Optionally delivers into your tools. Notion, Slack, Linear, Jira, GitHub, Airtable or anything else you've authorised.
-
Handles the platform
ABORTINGevent so an aborted run exits promptly instead of burning time.
Which YouTube Actor do I want?
| You want… | Actor |
|---|---|
| A channel's Shorts, with engagement — and optionally the files | This Actor |
| Long-form videos, search terms, hashtags, or comment threads | YouTube Scraper |
| One video's transcript, timestamped | YouTube Transcript Scraper |
| Transcripts of every video on a channel | Full Channel Transcripts Scraper |
None of them need a YouTube Data API key or a quota. They're complementary, not alternatives.
When to use it — and when not to
| Scenario | Fit |
|---|---|
| Every Short from one channel | ✅ Strong — the core use case |
| Views, likes and comment counts per Short | ✅ Strong — needs extendedMetadata (on by default) |
| Hashtags used on each Short | ✅ Strong |
| Duration and upload date | ✅ Strong |
| Rank a channel's Shorts by views or like-rate | ✅ Strong — the Engagement view |
| Correlate Short length against reach | ✅ Strong |
| The MP4 files themselves | ✅ Yes — downloadVideos: true |
| Audio only from each Short | ✅ Yes — videoQuality: "audio" |
| A specific resolution | ✅ Yes — 1080p / 720p / 480p |
| Track a channel's Shorts on a schedule | ✅ Strong — fixed input on a schedule, join on id |
| Results in Notion / Slack / Airtable | ✅ Strong — native MCP delivery |
| Several channels in one run | ⚠️ One channelUrl — run once per channel and combine |
| A channel you can only name, not link | ⚠️ Best-effort search — verify the returned channelUrl |
| Long-form videos from the channel | ❌ Shorts only — use YouTube Scraper |
| Transcripts | ❌ Use the Transcript or Full Channel Transcripts Scraper |
| Comment text | ❌ Count only — use the YouTube Scraper |
| Search terms or hashtags as input | ❌ Channel input only |
| Creator analytics — watch time, retention, revenue | ❌ Not public |
| Uploading, posting or any write action | ❌ Read-only |
| To redistribute the downloaded videos | ❌ Technically possible, but see Compliance |
⚡ Execution Rules (Avoid Common Agent Failures)
-
channelUrlis a single string, not an array. One channel per run. To cover several, run the Actor once each — unlike the Full Channel Transcripts Scraper, which takes a list. -
channelUrlaccepts more than a URL. A full channel URL,@handle,UC…channel ID, or a plain name all work. A plain name is a best-effort search — the log saysResolved "<input>" via search to: <url>when that path is used. Always checkchannelUrlon the output rows before trusting the data is the creator you meant. -
Only Shorts are returned. If you expected the channel's long-form videos, this is the wrong Actor.
-
An empty dataset is a successful run. An unresolvable channel, no Shorts tab, or a blocked listing all end
SUCCEEDEDwith zero items and the reason in the log. Do not treatitemCount: 0as a crash — read the log line before retrying. -
downloadVideosisfalseby default, and that's usually right. With it off, every row showsdownloadStatus: "skipped"and no file is written. That is expected output, not a failure — the log says so explicitly at the start of the run. -
Turning on
downloadVideoschanges the run substantially. It fetches media per Short, so run time and storage both grow sharply, andextendedMetadatais forced on. Start with a smallmaxShorts, and raise the run timeout (default 1 hour) for large download runs. -
maxShorts: 0means all available Shorts, not "none". On a large channel that is a long, expensive run — pair it withmaxTotalChargeUsd. -
extendedMetadatagates the interesting fields.viewCount,likeCount,commentCount,descriptionandhashtagsall need it. With it off those keys are still present butnull/[]— so rows keep a stable shape, but you get no engagement data. -
nullengagement counts don't mean zero. Creators can hide public stats, and YouTube sometimes withholds them for a request. The log warns per video. Exclude nulls from averages rather than coercing them. -
downloadStatusis the field to branch on.skipped/downloaded/failed.videoDownloadUrlandvideoFileKeyexist only ondownloaded.downloadErrorcovers mostfailedrows but not all — one failure mode leaves it absent, so branch ondownloadStatus, never on the presence ofdownloadError. -
A failed download does not fail the run. It's logged, the row is saved with
downloadStatus: "failed", and the Actor continues. Check the Downloads view afterwards. -
uploadDateisYYYYMMDD, not ISO-8601. Parse accordingly. -
Dataset order is not chronological. Shorts are processed concurrently, so rows land in completion order. Sort by
uploadDate(orviewCount) yourself. -
Keep the proxy enabled. Default is
{"useApifyProxy": true}. YouTube blocks the platform's shared IPs with "Sign in to confirm you're not a bot". -
Free Apify plans stop at 2 Shorts per run. The run succeeds and says so in both the log and the run status message — a smoke test, not a data pull.
-
deliveryModedefaults tosummary, notnone. For dataset-only runs, either leavemcpConnectorempty or setdeliveryMode: "none"explicitly.
dependencies:- 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; the dataset is unaffected"- rule: "chunked mode exposes {part} and {partCount}"note: "put them in the page/message title or every part looks identical"- rule: "downloadVideos: true forces extendedMetadata on"note: "you cannot download files and skip the metadata fetch"authentication:actor_input: noneplatform: "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 any table on this page disagrees with the schema
files, the schema files win.
Scraping parameters
| Parameter | Type | Required | Default | Allowed values / format | Example |
|---|---|---|---|---|---|
channelUrl | string | ✅ Yes | none (Console prefill only) | One channel URL, @handle, UC… ID, or plain name. Not an array. | https://www.youtube.com/@MrBeast |
maxShorts | integer | ⬜ No | 25 | 0 = all available; otherwise the cap, newest first | 200 |
extendedMetadata | boolean | ⬜ No | true | Needed for views, likes, comments, description and hashtags. Forced on by downloadVideos. | true |
downloadVideos | boolean | ⬜ No | false | Downloads each Short into the run's Key-Value Store | false |
videoQuality | string (enum) | ⬜ No | best | best · 1080p · 720p · 480p · audio — only used when downloading | 720p |
proxyConfiguration | object | ⬜ No | {"useApifyProxy": true} | Apify proxy editor object. Leave enabled. | see below |
Accepted channel input shapes
| Shape | Example | Resolution |
|---|---|---|
| Channel URL | https://www.youtube.com/@MrBeast | Direct — a /videos, /about or other tab suffix is stripped for you |
| Shorts tab URL | https://www.youtube.com/@MrBeast/shorts | Direct |
| Handle | @MrBeast | Direct |
| Channel ID | UCX6OQ3DkcsbYNE6H8uQQuVA | Direct |
| Plain name | MrBeast | Tried as a handle first, then a channel search — verify the returned channelUrl |
An explicit URL or channel ID is never second-guessed: if it has no Shorts tab, the run ends with an empty dataset and an explanatory log line rather than silently scraping a different channel. Only a plain name triggers the search fallback.
Delivery (optional)
| Parameter | Type | Default | Description |
|---|---|---|---|
mcpConnector | string | "" | An MCP connector you have authorised on your Apify account — Notion, Slack, Linear, Jira, GitHub, Airtable, or any MCP-compatible connector. The Console offers a picker. Leave empty for dataset-only runs. |
deliveryMode | string (enum) | "summary" | summary (one call covering every Short) · chunked (split across calls/pages) · perShort (one call per Short) · none. |
mcpTool | string | "" | Tool to call — e.g. create_page, send_message, create_issue. Required when a connector is set. Run once with a connector selected and the log lists the connector's available tools. |
mcpArguments | object | {} | Arguments for the tool. String leaves support {placeholder} tokens. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Placeholders by mode:
| Mode | Placeholders |
|---|---|
summary | {channelName}, {channelUrl}, {shortCount}, {shortsText} (formatted list of every Short), {message} |
chunked | the above, plus {part} and {partCount} — and {shortsText} holds one part |
perShort | {channelName}, {channelUrl}, {id}, {title}, {url}, {viewCount}, {likeCount}, {commentCount}, {uploadDate}, {duration}, {description}, {hashtags}, {downloadStatus}, {videoDownloadUrl}, {thumbnailUrl}, {message} |
Unknown tokens render as an empty string rather than erroring. chunked groups Shorts so no single
call exceeds roughly 72,000 characters, which keeps services with per-request block or timeout caps
(Notion in particular) from rejecting the call.
perShortfires one connector call per Short. A 200-Short run is 200 calls — usechunkedorsummaryunless you want a record each.
📤 Output Contract & Data Structure
Storage: Apify Dataset — one item per Short, written as each Short finishes.
Duplicates: none within a run (deduplicated on id). Across runs, join on id.
Order: completion order, not chronological — sort client-side.
Every row carries the same columns. What varies is the value, and the Toggle / controlled by column names the input that decides it.
| Field | Type | Nullable | Toggle / controlled by | Meaning |
|---|---|---|---|---|
id | string | No | — | YouTube video ID. The stable key for joining runs. |
url | string | Yes | — | Canonical URL of the Short. |
title | string | Yes | — | Short title. |
channelName | string | Yes | channelUrl | Channel the Short belongs to — check this when you passed a plain name. |
channelUrl | string | Yes | channelUrl | URL of the resolved channel. |
thumbnailUrl | string | Yes | — | Thumbnail image URL. |
duration | number | Yes | — | Length in seconds. |
viewCount | integer | Yes | extendedMetadata | Views at capture time. Null when hidden or extendedMetadata is off. |
likeCount | integer | Yes | extendedMetadata | Likes at capture time. Null when hidden or extendedMetadata is off. |
commentCount | integer | Yes | extendedMetadata | Comment count. The comments themselves aren't scraped. |
uploadDate | string | Yes | extendedMetadata | YYYYMMDD as YouTube reports it — not ISO-8601. |
description | string | Yes | extendedMetadata | Description text. |
hashtags | array | No | extendedMetadata | Tags on the Short. [] when none, or when extendedMetadata is off. |
downloadStatus | string | No | downloadVideos | Always present: skipped, downloaded or failed. |
videoDownloadUrl | string | Yes | downloadVideos | Public Key-Value Store URL. Only when downloaded. |
videoFileKey | string | Yes | downloadVideos | Store key, short-<id>.<ext>. Only when downloaded. |
downloadError | string | Yes | downloadVideos | Why it failed. Present on most — not all — failed rows. |
Missing values are genuine
null(or[]forhashtags), not"N/A". The three download fields are the only ones that can be absent entirely.
Downloaded files
When downloadVideos is on, each successful download is written to the run's Key-Value Store:
| Key | short-<videoId>.<ext> — also on the row as videoFileKey |
| Content type | video/mp4, or audio/mp4 when videoQuality: "audio" |
| Public URL | on the row as videoDownloadUrl |
| Console tab | Downloaded Shorts → Shorts files |
Example output object — download enabled
{"id": "aBcDeFgHiJk","url": "https://www.youtube.com/shorts/aBcDeFgHiJk","title": "3 keyboard shortcuts that saved me hours","channelName": "Practical Systems","channelUrl": "https://www.youtube.com/@practicalsystems","thumbnailUrl": "https://i.ytimg.com/vi/aBcDeFgHiJk/oardefault.jpg","duration": 47,"viewCount": 812403,"likeCount": 41120,"commentCount": 903,"uploadDate": "20260712","description": "The three I actually use every day. #productivity #shorts","hashtags": ["productivity", "shorts"],"downloadStatus": "downloaded","videoDownloadUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/short-aBcDeFgHiJk.mp4","videoFileKey": "short-aBcDeFgHiJk.mp4"}
Example output object — download off (the default)
{"id": "lMnOpQrStUv","url": "https://www.youtube.com/shorts/lMnOpQrStUv","title": "Why your queue keeps backing up","channelName": "Practical Systems","channelUrl": "https://www.youtube.com/@practicalsystems","thumbnailUrl": "https://i.ytimg.com/vi/lMnOpQrStUv/oardefault.jpg","duration": 58,"viewCount": 194882,"likeCount": null,"commentCount": 212,"uploadDate": "20260803","description": "Backpressure, explained badly. #systems","hashtags": ["systems"],"downloadStatus": "skipped"}
Values illustrate shape and formatting — the channel and Shorts are invented. Note
likeCount: nullin the second row: that creator hid likes, and the log warns when it happens.
What a successful run looks like
- Status:
SUCCEEDED - Dataset: one item per Short
- Key-Value Store: one file per downloaded Short, only when
downloadVideosis on - Log: an explicit note when
downloadVideosis off, a per-video warning when stats are hidden, and a warning per failed download - Zero results is also a success. A channel with no Shorts, a name that resolved to nothing, a
bot-block, or an unresolvable URL all return an empty dataset with
SUCCEEDED. Check the log. downloadStatus: "skipped"on every row is normal when downloading is off.
Dataset views
| View | Shows |
|---|---|
| Shorts | Title, channel, engagement, duration and download state |
| Engagement | Views, likes, comments, duration and hashtags for performance comparison |
| Downloads | Download outcome, store key and error per Short |
| Thumbnails | Visual grid by thumbnail |
▶️ Invocation & Integration
Lifecycle: Discover → Understand → Configure → Execute → Retrieve → Validate. Authenticate every
call with Authorization: Bearer <APIFY_TOKEN>.
Apify Console
Open the Actor page → Input tab →
paste a channel URL or @handle → leave the proxy on → (optional) choose an MCP connector,
tool and template → Start → read the Dataset tab (JSON, CSV, Excel, XML).
Model Context Protocol (MCP)
Add this Actor directly as an AI tool in Claude Desktop, Cursor, VS Code, or LangChain agents:
claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/youtube-shorts-scraper"
Then ask your agent for "this channel's best-performing Shorts".
Python (apify-client)
import osfrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))run = client.actor("techforce.global/youtube-shorts-scraper").call(run_input={"channelUrl": "https://www.youtube.com/@handle","maxShorts": 100,"extendedMetadata": True,"downloadVideos": False, # metadata only — the sensible default"deliveryMode": "none","proxyConfiguration": {"useApifyProxy": True},})shorts = list(client.dataset(run["defaultDatasetId"]).iterate_items())# An empty dataset is a SUCCEEDED run — check it before assuming failure.if not shorts:raise SystemExit("No Shorts returned — check the run log for the reason.")# Confirm the channel that was actually resolved (matters when you passed a plain name).print("Resolved channel:", shorts[0].get("channelName"), shorts[0].get("channelUrl"))# Engagement counts can be null (creator hid them) — exclude, don't coerce to 0.scored = [s for s in shorts if s.get("viewCount") is not None]print(f"{len(scored)}/{len(shorts)} Shorts have public view counts")for s in sorted(scored, key=lambda s: s["viewCount"], reverse=True)[:10]:likes = s.get("likeCount")rate = f"{likes / s['viewCount']:.2%}" if likes else "n/a"print(f'{s["viewCount"]:>10,} like-rate {rate:>7} {s["title"][:50]}')
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-shorts-scraper').call({channelUrl: 'https://www.youtube.com/@handle',maxShorts: 50,downloadVideos: true,videoQuality: '720p',deliveryMode: 'none',proxyConfiguration: { useApifyProxy: true },});const { items } = await client.dataset(run.defaultDatasetId).listItems();// Always check download outcomes — failures don't fail the run.const by = items.reduce((a, i) => ({ ...a, [i.downloadStatus]: (a[i.downloadStatus] ?? 0) + 1 }), {});console.log(by); // e.g. { downloaded: 47, failed: 3 }// downloadError is absent on one failure mode — branch on downloadStatus, not on the error.for (const i of items.filter((i) => i.downloadStatus === 'failed')) {console.warn(`failed: ${i.title} — ${i.downloadError ?? 'no file produced'}`);}
cURL — synchronous (waits, returns the dataset)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"channelUrl": "https://www.youtube.com/@handle","maxShorts": 25,"extendedMetadata": true,"deliveryMode": "none","proxyConfiguration": { "useApifyProxy": true }}' \'https://api.apify.com/v2/acts/techforce.global~youtube-shorts-scraper/run-sync-get-dataset-items'
cURL — asynchronous with a spend cap (recommended when downloading)
# 1. Start execution with a hard spend limitcurl -X POST -H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{ "channelUrl": "https://www.youtube.com/@handle", "maxShorts": 500, "downloadVideos": true }' \'https://api.apify.com/v2/acts/techforce.global~youtube-shorts-scraper/runs?maxTotalChargeUsd=5'# 2. Pollcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/actor-runs/<runId>'# 3. Retrieve when SUCCEEDEDcurl -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 downloading — media fetches make
run length much harder to predict.
Performance analysis — the cheap configuration
{"channelUrl": "https://www.youtube.com/@handle","maxShorts": 200,"extendedMetadata": true,"downloadVideos": false,"deliveryMode": "none","proxyConfiguration": { "useApifyProxy": true }}
Everything you need to rank a channel's Shorts by views, like-rate or duration, with no media fetching. Start here.
Audio only
{"channelUrl": "@handle","maxShorts": 25,"downloadVideos": true,"videoQuality": "audio","deliveryMode": "none","proxyConfiguration": { "useApifyProxy": true }}
videoQuality: "audio" writes audio/mp4 files — much smaller than video, and the right choice
if you're feeding a transcription or audio-analysis step.
Every Short on the channel
{"channelUrl": "https://www.youtube.com/@handle","maxShorts": 0,"extendedMetadata": true,"downloadVideos": false,"deliveryMode": "none","proxyConfiguration": { "useApifyProxy": true }}
maxShorts: 0 is uncapped. On a prolific channel that is a long run and a large event bill — pair
it with maxTotalChargeUsd, and raise the run timeout above the 1-hour default if you also
download.
Several channels
channelUrl takes one channel, so run the Actor once per channel and concatenate the datasets.
Every row carries channelName and channelUrl, so a combined dataset stays separable.
With MCP delivery — a digest into Slack
{"channelUrl": "https://www.youtube.com/@handle","maxShorts": 25,"proxyConfiguration": { "useApifyProxy": true },"mcpConnector": "<your-authorized-slack-connector>","mcpTool": "send_message","deliveryMode": "summary","mcpMessageTemplate": "Shorts from {channelName} ({shortCount} total):\n\n{shortsText}","mcpArguments": {"channel": "#shorts-watch","text": "{message}"}}
Scheduling for performance tracking
Each run is a snapshot. Put a fixed input on a
schedule and join runs on id, comparing
viewCount over time — that gives you a growth curve per Short, which YouTube doesn't publish.
⚠️ Autonomous Agent Error Handling & Resolution Matrix
Read this first: almost nothing about the channel fails the run. An unresolvable channel, a
missing Shorts tab, a blocked listing, and a channel with no Shorts all end SUCCEEDED with an
empty dataset and the reason in the log. An agent that treats itemCount: 0 as a crash will retry
things that will never succeed — read the log line instead.
The CONDITION labels below are documentation handles for observable states. This Actor does not
write an error code into the dataset; detect conditions from the run status, the log, the dataset,
or the Key-Value Store as shown.
| Condition | What you observe | Underlying cause | Deterministic agent action |
|---|---|---|---|
MISSING_CHANNEL_URL | Run rejected at input validation | channelUrl absent — it is required in the schema | Modify Input: supply one channel identifier. |
EMPTY_CHANNEL_URL | SUCCEEDED, empty dataset; log: No channel name, handle, or URL provided… | channelUrl present but blank | Modify Input: pass a non-empty value. |
ARRAY_PASSED_AS_CHANNEL_URL | Validation error on type | channelUrl is a string, not an array | Modify Input: one identifier as a string; run once per channel. |
CHANNEL_NOT_RESOLVED | SUCCEEDED, empty dataset; log: Could not find a Shorts tab at … or Could not resolve "…" to a YouTube channel with a Shorts tab. | The channel doesn't exist, is private, or has no Shorts tab | Do not blind-retry. Pass the full channel URL or @handle; confirm the Shorts tab opens in a browser. |
WRONG_CHANNEL_RESOLVED | SUCCEEDED with data, but channelUrl isn't the creator you meant; log: Resolved "…" via search to: … | A plain name was matched by channel search | Validate & re-run: pass the exact URL, @handle, or UC… ID. |
LISTING_FAILED | SUCCEEDED, empty dataset; log: Failed to list Shorts for channel … | Listing request errored — commonly a bot-block | Retry once with {"useApifyProxy": true}; then treat as unavailable. |
NO_SHORTS_FOUND | SUCCEEDED, empty dataset; log: No Shorts found for channel … | Channel genuinely publishes no Shorts | Do not retry. Route long-form needs to the YouTube Scraper. |
ALL_SKIPPED | Every row downloadStatus: "skipped" | downloadVideos is false (the default) | Expected. Set it true if you wanted files. |
DOWNLOAD_FAILED_ROWS | Some rows failed, usually with downloadError; log: Failed to download … or Download produced no file for … | That Short couldn't be fetched at the requested quality | Expected, partial. Retry those IDs with videoQuality: "best", or accept the gap. Run is still valid. |
NULL_ENGAGEMENT | viewCount / likeCount null; log: View/like counts unavailable for … | Creator hid stats, or extendedMetadata off | Check extendedMetadata. If on, the stats are genuinely hidden. Null ≠ zero. |
METADATA_FETCH_FAILED | A row has nulls across engagement fields; log: Could not fetch extended metadata for … | That single video's detail request failed | Partial, not fatal. Re-run for those IDs if the fields matter. |
NO_DESCRIPTION_OR_HASHTAGS | description null, hashtags [] across all rows | extendedMetadata is off | Modify Input: enable it. |
RUN_LONGER_THAN_EXPECTED | Run still going well past estimate | downloadVideos: true with a large or 0 maxShorts | Abort and lower maxShorts, or wait with a spend cap in place. Rows already saved are kept. |
RUN_TIMED_OUT | TIMED-OUT with a partial dataset | Work exceeded the run timeout (1 hour by default) | Expected, partial. Raise the timeout or lower maxShorts; saved rows are valid. |
UPLOAD_DATE_NOT_PARSEABLE | uploadDate won't parse as ISO | It's YYYYMMDD by design | Fix expectation: parse that format. |
FREE_PLAN_CAPPED | 2 Shorts; log and run status message say so | Free Apify plan | Upgrade the plan. |
CHARGE_FAILED | Log: Charge failed for 'short-scraped'… | Billing event could not be recorded | None — data is unaffected. The row is still saved. |
CONNECTOR_RATE_LIMITED | Connector errors partway through | perShort on a large run | Modify Input: switch to chunked or summary. |
CONNECTOR_TOOL_MISSING | Log: A connector is selected but no tool name was provided | mcpTool empty | Modify Input: set mcpTool. Dataset already saved. |
CONNECTOR_TOOL_NOT_FOUND | Log: Tool 'x' is not available on this connector. Available tools: … | mcpTool name is wrong | Modify Input: copy an exact name from that log line. Dataset unaffected. |
CONNECTOR_UNREACHABLE | Log: Could not reach the connector: … | Connector not authorised, or the run isn't on the Apify platform | Authorise it in Apify, or set deliveryMode: "none". Data already saved. |
MCP_PROXY_MISSING | Log: APIFY_MCP_PROXY_URL is not set | Running locally with apify run | Reconfigure: apify push and run on the platform. |
AUTH_INVALID | HTTP 401 | Missing or malformed APIFY_TOKEN | Abort & prompt user: request a valid API token. |
RATE_LIMITED | HTTP 429 | Apify API rate / concurrency limits | Retry: exponential backoff (2s, 4s, 8s). |
SYNC_TIMEOUT | HTTP 408 on the sync endpoint | Run exceeded the synchronous ceiling | Reconfigure: switch to async POST /runs + polling. |
Provenance of this table: every log line, status and dataset condition above is taken from this Actor's own source and verified against it. The three HTTP rows (
401,408,429) 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… | Intent | Constructed Actor input |
|---|---|---|
| "Get this channel's Shorts" | Core extraction | {"channelUrl":"<url>","maxShorts":25} |
| "Scrape every Short MrBeast has posted" | Full catalogue | {"channelUrl":"@MrBeast","maxShorts":0} |
| "Which of their Shorts performed best?" | Ranking | {"channelUrl":"<url>","maxShorts":200} then sort on viewCount, excluding nulls |
| "What's their like-rate on Shorts?" | Engagement ratio | {"channelUrl":"<url>","maxShorts":200} then likeCount / viewCount on non-null rows |
| "What hashtags do they use?" | Tag strategy | {"channelUrl":"<url>","maxShorts":200} then aggregate hashtags |
| "Do short Shorts get more views?" | Duration analysis | {"channelUrl":"<url>","maxShorts":200} then correlate duration vs viewCount |
| "Download their Shorts" | Media | {"channelUrl":"<url>","downloadVideos":true,"videoQuality":"720p"} |
| "Just the audio from their Shorts" | Audio | {"channelUrl":"<url>","downloadVideos":true,"videoQuality":"audio"} |
| "Highest quality files" | Media | {"channelUrl":"<url>","downloadVideos":true,"videoQuality":"best"} |
| "Track their Shorts weekly" | Scheduled monitor | Fixed input on a schedule; join runs on id |
| "Just a quick sample" | Cheap probe | {"channelUrl":"<url>","maxShorts":3,"downloadVideos":false} |
| "Compare three creators" | Multi-channel | Three runs, one per channel; combine on channelName |
| "I only know the channel's name, not the link" | Fuzzy input | {"channelUrl":"<name>"} then verify channelUrl on the rows |
| "Put these in Notion" | Scrape + deliver | Above, plus mcpConnector, mcpTool, deliveryMode |
| "It returned nothing" | Empty result | Read the log: unresolved channel, no Shorts tab, or a block — enable Apify Proxy and pass a full URL |
| "Get their latest videos" | Long-form | ➡️ Route to YouTube Scraper |
| "What did they say in this Short?" | Transcript | ➡️ Route to YouTube Transcript Scraper |
| "Show me the comments on their Shorts" | Comment text | ➡️ Route to YouTube Scraper — this Actor returns commentCount only |
Should NOT route here: long-form videos (→ YouTube Scraper) · transcripts (→ Transcript or Full Channel Transcripts) · comment text (→ YouTube Scraper) · several channels in one run · search terms or hashtags as input · creator analytics (watch time, retention, revenue) · redistributing downloaded media · uploading or any write action · non-YouTube short-form platforms (TikTok, Instagram Reels).
🧭 Agent Execution & Routing Logic
Does the user want YOUTUBE SHORTS from a channel?├── NO → one of the siblings, or nothing here.│ long-form videos / search / hashtags / comments → YouTube Scraper│ one video's words → YouTube Transcript Scraper│ a channel's words → Full Channel Transcripts Scraper│ watch time / revenue → creator's own account only│└── YES│├── channelUrl — REQUIRED, a SINGLE string.│ URL / @handle / UC... ID → resolved directly│ plain name → best-effort search; VERIFY channelUrl on the output│ several channels? → one run each; combine on channelName afterwards│├── ALWAYS keep proxyConfiguration enabled (useApifyProxy: true)│├── Do they want the FILES, or the numbers?│ ├── numbers only → downloadVideos: FALSE (the default). Start here.│ └── files → downloadVideos: true│ ├── which quality? best | 1080p | 720p | 480p│ ├── audio only? videoQuality: "audio" (much smaller)│ ├── raise the run timeout above the 1-hour default for big runs│ └── ALWAYS set a spend cap — media fetches are unpredictable│├── Engagement data needed?│ └── keep extendedMetadata TRUE (default). Off = null counts.│├── How many? maxShorts│ ├── a probe → 3│ ├── analysis → 100-200│ └── everything → 0 (uncapped) + spend cap, especially when downloading│├── What is the actual question?│ ├── best performers → sort viewCount, EXCLUDING nulls│ ├── like-rate → likeCount / viewCount, both non-null only│ ├── hashtag strategy → aggregate hashtags│ ├── length vs reach → correlate duration with viewCount│ └── growth over time → SCHEDULE it; join on id│└── Should results land in another app?├── yes → mcpConnector + mcpTool + {message} in mcpArguments│ one digest → summary (default)│ long list → chunked (put {part}/{partCount} in the title)│ per Short → perShort (one call each — watch the count)└── no → deliveryMode "none", or leave mcpConnector empty[Execute Actor]├──► SUCCEEDED, itemCount > 0 → verify channelUrl, then consume rows├──► SUCCEEDED, itemCount = 0 → READ THE LOG: unresolved channel / no Shorts / blocked└──► TIMED-OUT → partial dataset is valid; lower maxShorts or raise the timeout
💰 Cost Control & Pricing Transparency
Billing model: pay-per-event. This Actor bills per Short saved to the dataset. You pay for rows that reach your dataset, so a run that finds nothing costs nothing in event charges. Current per-Short rates are on the Actor's Apify Store page; the Console shows the live total while a run is in progress.
| Choice | Effect on cost |
|---|---|
maxShorts | The direct lever on event charges. 0 means uncapped — budget accordingly. |
downloadVideos | The biggest effect on run time and storage. Media fetches per Short, plus Key-Value Store usage. |
videoQuality | audio and 480p are far smaller than best. Pick the lowest that answers your question. |
extendedMetadata | A metadata fetch per Short. Leave on — the engagement data is usually the point. |
deliveryMode | perShort issues one connector call per Short; summary/chunked issue a handful. |
| Being blocked | Costs run time and returns nothing. A disabled proxy is the most expensive setting on this page. |
Worked examples — relative run shape
Multiply by the current per-Short rate on the Store page to get a currency figure.
| Scenario | Shorts billed | Relative run cost | Notes |
|---|---|---|---|
Smoke test — maxShorts: 3, no download | 3 | Baseline | Confirms the channel resolved. |
| Performance analysis — 200 Shorts, no download | 200 | ≈ 67 × baseline in events, modest compute | The recommended starting point for research. |
Same 200 Shorts with extendedMetadata: false | 200 | Same events, less compute | You lose every engagement field — rarely worth it. |
50 Shorts downloaded at 720p | 50 | Events ≈ 17 × baseline, compute and storage far higher | Media fetch dominates. Set a spend cap. |
50 Shorts as audio | 50 | Same events, much lower storage | Right choice for transcription pipelines. |
Whole channel, maxShorts: 0, no download | all of them | Scales linearly with the channel | Unbounded by definition — cap the spend. |
| Unresolvable channel or no Shorts | 0 | Near zero | Ends early with an empty dataset. |
| Three creators compared | 3 runs | Sum of the three | One run per channel; combine on channelName. |
Keeping runs predictable:
- First run:
maxShorts: 3,downloadVideos: false. Confirm you resolved the right channel and are getting engagement counts. - Do the analysis before downloading. Rank on
viewCountfirst, then download only the Shorts you actually want. - Pick the smallest useful
videoQuality.audioif you only need sound;480pif you need to see it but not in detail. - Always set
maxTotalChargeUsdwhen downloading. Media fetch time varies far more than metadata, and it is the only hard stop. - Cache by
id. Re-running a schedule only to re-read unchanged Shorts is a pure cost. - Free Apify plans get 2 Shorts per run — a connectivity check, not a data pull.
🛠️ Troubleshooting
Most problems are channel resolution or proxy issues, not scraper bugs. Work through this table top to bottom — the first three rows account for the large majority of support tickets.
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | SUCCEEDED but the dataset is empty | The channel didn't resolve, has no Shorts tab, or the listing was blocked | Read the log: it names which. Pass the full channel URL or @handle, and keep Apify Proxy on. |
| 2 | Data came back, but it's the wrong creator | You passed a plain name and the search fallback matched someone else — log: Resolved "…" via search to: … | Pass the exact channel URL, @handle, or UC… ID. Always assert channelUrl on the rows. |
| 3 | Thin or empty results that used to work | Bot-block — YouTube challenges the platform's shared IPs | Confirm proxyConfiguration is {"useApifyProxy": true} and retry. |
| 4 | Every row says downloadStatus: "skipped" | downloadVideos is false — the default | Expected. Set it true if you wanted files; the log states this at the start of every run. |
| 5 | Some rows say failed | That Short couldn't be fetched at the requested quality | Partial, not fatal. Retry those IDs with videoQuality: "best". Check the Downloads view. |
| 6 | A failed row has no downloadError | One failure mode ends with no file and no exception — the log says Download produced no file for … | Branch on downloadStatus, never on the presence of downloadError. |
| 7 | viewCount / likeCount are null | The creator hid public stats, or extendedMetadata is off | Check extendedMetadata first. If it's on, the stats are genuinely hidden — exclude nulls, don't coerce to 0. |
| 8 | description is null and hashtags is [] on every row | extendedMetadata is off | Turn it on. It is on by default and forced on when downloading. |
| 9 | uploadDate won't parse | It is YYYYMMDD, not ISO-8601 | Parse that format, or convert on ingest. |
| 10 | Rows aren't in date order | Shorts are processed concurrently and saved as they finish | Sort by uploadDate or viewCount client-side. |
| 11 | Long-form videos are missing | By design — this Actor reads the Shorts tab only | Use the YouTube Scraper. |
| 12 | You wanted the comments, not the count | Only commentCount is collected | Use the YouTube Scraper for comment threads. |
| 13 | Only 2 Shorts returned | Free Apify plan cap | Upgrade the plan. The log and the run status message both say so. |
| 14 | Run hit the timeout | downloadVideos: true with a large or 0 maxShorts against the 1-hour default | Raise the run timeout or lower maxShorts. Rows saved before the timeout are valid. |
| 15 | Run is far slower than expected | Downloading media, or maxShorts: 0 on a prolific channel | Do the metadata pass first, then download a shortlist. |
| 16 | videoDownloadUrl returns 404 later | Key-Value Stores expire on the platform's retention schedule | Copy files to your own storage if you need them long term. |
| 17 | Slack / Notion / Linear received nothing | Delivery needs both mcpConnector and mcpTool; otherwise it is skipped with a warning | Set both. Every dataset row is still saved — your data is never lost. |
| 18 | Log: Tool 'x' is not available on this connector | Wrong tool name for that connector | Copy an exact name from the Available tools: list in the same log line. |
| 19 | Connector fired, but the message body is empty | mcpArguments did not reference {message} | Map {message} in mcpArguments and populate mcpMessageTemplate. |
| 20 | Every Notion page looks identical | {part}/{partCount} missing from the title template in chunked mode | Add them: "Shorts {channelName} (part {part}/{partCount})". |
| 21 | Hundreds of connector calls fired | deliveryMode: "perShort" sends one call per Short | Switch to chunked or summary. |
| 22 | Delivery fired when you only wanted a dataset | deliveryMode defaults to summary, not none | Set deliveryMode: "none" or leave mcpConnector empty. |
| 23 | Log: APIFY_MCP_PROXY_URL is not set | You ran locally with apify run; connectors only resolve on the platform | Deploy with apify push and run on the platform. |
| 24 | HTTP 401 / 403 | Missing, expired, or malformed APIFY_TOKEN | Regenerate in Apify Console → Settings → API & Integrations, send as Authorization: Bearer <token>. |
| 25 | HTTP 429 | Apify account concurrency / rate limits — not YouTube blocking | Retry with exponential backoff (2s → 4s → 8s). |
| 26 | HTTP 408 on run-sync-get-dataset-items | The synchronous endpoint has a hard time ceiling | Switch to async: POST /runs → poll /actor-runs/<runId> → fetch dataset. |
Diagnostic checklist before opening an issue
- Open the channel's Shorts tab in a browser (
youtube.com/@handle/shorts). If it's empty there, no scraper can produce Shorts. - Pass the exact URL or
@handle, not a plain name — this removes the search fallback from the equation. - Confirm Apify Proxy is enabled. It is the single most common cause of empty or thin results.
- Baseline run:
channelUrlonly,maxShorts: 3,downloadVideos: false,deliveryMode: "none". - Check
channelName/channelUrlon the returned rows against the creator you meant. - Read the run log — it names the resolved Shorts URL, how many Shorts were found, and every hidden-stat or download warning.
If the issue survives all six steps, open an Issues ticket on the Actor page (or email support) with the run ID, the channel input, and what you expected. Run IDs let us replay the exact execution.
❓ FAQ
Setup & access
Do I need a YouTube or Google account, API key, or OAuth app?
No. The Actor reads publicly available Shorts 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.
Why not just use the YouTube Data API for Shorts?
The Data API has no Shorts flag — you would list a channel's uploads and guess which ones are Shorts from duration. It also has a per-project daily quota, and it cannot download media at all.
Do I need to configure proxies?
Apify Proxy is on by default and you should leave it on. YouTube serves the platform's shared IPs a "Sign in to confirm you're not a bot" wall, which shows up as an empty or thin dataset.
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-shorts-scraper"
Input & channels
What can I put in channelUrl?
A full channel URL, an @handle, a UC… channel ID, or a plain channel name. A URL with a /videos or /about tab suffix is normalised for you. A plain name is a best-effort lookup that falls back to a channel search, so check the channelUrl on the returned rows.
Can I scrape several channels in one run?
No — one channel per run. Run the Actor once per channel and concatenate the datasets; every row carries channelName and channelUrl, so a combined dataset stays separable.
How do I get every Short a channel has ever posted?
Set maxShorts: 0, which is uncapped. On a prolific channel that is a long run and a large event bill, so pair it with maxTotalChargeUsd.
Does it return the channel's long-form videos too?
No. It reads the Shorts tab only. For long-form video metadata, search, hashtags or comments, use the YouTube Scraper.
Output & data
Why are viewCount and likeCount null?
Either the creator hid public stats, or extendedMetadata is off. The log warns per video when YouTube withholds counts. Null does not mean zero — exclude those rows from averages rather than coercing them.
Why isn't uploadDate in ISO-8601?
It is YYYYMMDD, exactly as YouTube reports it. Parse that format rather than assuming ISO.
Can I get the comments themselves?
No — only commentCount. Comment threads are the personal-data-heavy part of YouTube and are deliberately out of scope here. Use the YouTube Scraper if you need them.
Can I get transcripts of the Shorts?
Not from this Actor. Use the YouTube Transcript Scraper for one video, or the Full Channel Transcripts Scraper for a whole channel.
Why did my run succeed but return nothing?
Because the channel, not the Actor, was the problem: it may not have resolved, may have no Shorts tab, or the listing request may have been blocked. Rather than crashing, the Actor ends the run successfully with an empty dataset and the reason in the log — so scheduled runs don't produce an alert storm. Read the log before retrying.
Downloads
How do I download the actual video files?
Set downloadVideos: true. Each successful download is saved to the run's Key-Value Store as short-<videoId>.<ext>, and the row carries videoFileKey plus a public videoDownloadUrl.
Can I get audio only?
Yes — videoQuality: "audio" writes audio/mp4 files, which are far smaller than video and the right input for a transcription or audio-analysis step.
Some rows say failed — did the run break?
No. A failed download is logged, the row is saved with downloadStatus: "failed", and the run continues. Most failures also carry a downloadError; one mode (no file produced) does not, so branch on downloadStatus rather than on the error field.
Who can access videoDownloadUrl?
Anyone with the link — it is a public Key-Value Store URL. Don't paste those URLs into public channels or shared documents, and clear the store when you're done with a run.
Is downloading Shorts allowed?
A Short is the creator's copyrighted work, and YouTube's Terms of Service prohibit downloading except through features YouTube itself provides. Storing a copy for internal review is a very different act from republishing it. See Compliance & Data Privacy — the download toggle raises your risk rather than sitting alongside it.
Delivery
What is the MCP connector for?
It pushes the scraped Shorts straight into a tool you have already authorized on your Apify account — Notion, Slack, Linear, Jira, GitHub, Airtable, or any MCP-compatible connector — with no glue code. Credentials stay private: delivery runs through the Apify MCP Proxy, so the Actor never sees your connector tokens.
Which delivery mode should I use?
summary for a single digest, chunked for many Shorts into services with request caps (Notion), perShort for one record per Short, none for dataset-only runs. Delivery always runs after the dataset is saved, so a connector problem never costs you data.
Does delivery work when running locally?
No. Connectors resolve through the Apify MCP Proxy, which only exists on the platform. Locally you'll see APIFY_MCP_PROXY_URL is not set and the dataset is saved as normal.
Limits & pricing
What does it cost?
Pay-per-event: you are billed per Short saved to the dataset. A run that finds nothing costs nothing in event charges. Current per-Short rates are on the Actor's Apify Store page, and the Console shows a live total while a run is in progress.
What do I get on the free plan?
2 Shorts per run, regardless of maxShorts. The run succeeds and says so in both the log and the run status message — treat it as a connectivity check, not a data pull.
How do I guarantee an agent never overspends?
Pass maxTotalChargeUsd on the run endpoint — it is a hard per-run ceiling — and cache results by id so repeat reads cost nothing. Always set it when downloadVideos is on.
🔍 Companion machine-readable files
| File | What it defines |
|---|---|
| .actor/input_schema.json | Every input, its type, default, and the quality and delivery enums |
| .actor/dataset_schema.json | All 17 output fields with nullability, and the four Console views |
| .actor/key_value_store_schema.json | The downloaded-files collection and its content types |
| .actor/output_schema.json | Where the run's output is exposed |
| .actor/actor.json | Actor identity, run defaults (1 GB memory, 1-hour timeout) 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.
🔗 Related Actors by Techforce Global
Other YouTube Actors
This Actor is the short-form specialist: one channel in, its Shorts out, optionally with the files. Use these siblings when the job is different.
| Actor | Best for | Why pick it over this one |
|---|---|---|
| YouTube Scraper: Videos, Channels & Comments | Long-form video, channel and comment metadata from search queries, channel URLs, video URLs or hashtags | You need long-form videos, discovery by search or hashtag, or actual comment threads — none of which this Actor returns. |
| YouTube Transcript Scraper | One video's timestamped transcript, in 13 languages | You want the words of a specific video (Shorts included) rather than a channel's engagement numbers. |
| YouTube Full Channel Transcripts Scraper | Transcripts for every video on a channel, in one run | You're building a searchable archive of what a creator said, not how their Shorts performed. |
Pick by intent: a channel's Shorts and their files → this Actor · long-form, search and comments → YouTube Scraper · one video's words → Transcript Scraper · a channel's words → Full Channel Transcripts.
Enrichment & downstream pipeline
| Actor | Use it for |
|---|---|
| Advanced Website Crawling Actor | Crawl the creator's own site or link-in-bio for clean HTML/Markdown/text context alongside the Shorts data |
| Pinterest Scraper | Compare the same creative strategy on Pins from searches, boards and profiles |
| Facebook Public Comment Tracker | Track audience reaction where comment text is actually in scope |
Suggested pipeline patterns
📊 Short-form performance audit
This Actor with maxShorts: 200, no download → rank by viewCount and like-rate → correlate
duration and hashtags against reach → publish the digest into Notion via MCP delivery.
📈 Growth tracking
Fixed input on a schedule → join runs on id →
plot viewCount over time for a per-Short growth curve YouTube doesn't publish.
🎧 Transcription pipeline
This Actor with videoQuality: "audio" → the audio/mp4 files in the Key-Value Store → your ASR
service → topic analysis against the engagement numbers from the same rows.
🔍 Competitive comparison
One run per creator → concatenate on channelName → compare hashtag strategy, Short length and
like-rate side by side.
🤖 Agent research tool Register the Actor as an MCP tool so an agent can pull a creator's Shorts on demand and answer "what's working for them right now?" with real numbers.
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 or members-only content, no login, no stored credentials.
-
No personal data in the output. Titles, view counts, hashtags and durations are content metadata. Comment threads — the part of YouTube that is personal data — are not collected here; only the comment count. That makes this Actor materially lower-risk than the comment-scraping sibling, and keeps a straightforward GDPR/CCPA position for most uses.
-
Downloading changes the picture, and you should treat it as a separate decision. A Short is the creator's copyrighted work.
downloadVideos: falseis the default for a reason. Storing a copy for internal review or analysis is a very different act from re-uploading it, remixing it, or publishing it on another platform — the last of which is straightforward copyright infringement and, on most platforms, also a terms violation. Getting a file easily does not make redistributing it lawful. -
Re-uploading Shorts is a well-known enforcement target. Compilation and reposting channels get struck and removed. If your plan involves publishing someone else's Short anywhere, you need the creator's permission, not this Actor.
-
Training a model on downloaded media is legally distinct from analysing it. Take advice rather than relying on "it was public".
-
YouTube's Terms of Service prohibit downloading except through features YouTube itself provides, and prohibit automated access generally. This Actor does not remove that risk — it is a risk you accept by running it, and the download toggle raises it rather than sitting alongside it.
-
Downloaded files land in your Apify Key-Value Store, and
videoDownloadUrlis a public URL. Anyone with that link can fetch the file. Don't paste those URLs into public channels or shared documents, and clear the store when you're done with a run. -
Engagement figures are a snapshot, and hidden stats are common. A null
viewCountorlikeCountmeans hidden or withheld, not zero. Publishing derived metrics — like-rates, rankings — without saying when you captured them, or while silently treating nulls as zeros, produces numbers that look authoritative and aren't. -
Attribution over mirroring.
urlandchannelUrlare on every row. Link back and credit the creator rather than presenting their Shorts or their numbers as your own.
🆘 Support & Custom Pipeline Engineering
Need automated multi-city scheduled feeds, webhook enrichment, or enterprise data warehouse syncs?
- Email: bhavin.shah@techforceglobal.com
- Custom Enterprise Integrations: Book a 15-Minute Technical Consultation
- Maintained by: Techforce Global — Specialists in High-Performance Web Scrapers and Agentic Workflows.
🏷️ Structured data for search & AI discovery
Two blocks: SoftwareApplication describing the Actor, and FAQPage wrapping the FAQ above so those answers are eligible for rich results. The FAQPage entries are generated from the visible FAQ and match it question-for-question.
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "YouTube Shorts Scraper","alternateName": "YouTube Shorts Downloader","applicationCategory": "BusinessApplication","applicationSubCategory": "Web Scraping / Short-Form Video Analytics","operatingSystem": "Cloud (Apify platform)","description": "Extracts every Short from a single YouTube channel with view, like and comment counts, duration, hashtags, description, thumbnail and upload date — and optionally downloads each Short as an MP4 at a chosen resolution, or as audio-only, into the run's Key-Value Store with a public URL on the dataset row. No YouTube Data API key and no quota. Every row carries a downloadStatus of skipped, downloaded or failed, so download outcomes are auditable. Public content metadata only: no login, no private content, and no comment text. Optional direct delivery into Notion, Slack, Linear, Airtable or any MCP connector.","url": "https://apify.com/techforce.global/youtube-shorts-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 event","description": "Billed per Short saved to the dataset. A run that finds nothing costs nothing in event charges. Current rates are shown on the Actor's Apify Store page.","url": "https://apify.com/techforce.global/youtube-shorts-scraper"},"featureList": ["Shorts specifically, not a channel's long-form catalogue","Accepts a channel URL, @handle, UC channel ID, or a plain channel name","View, like and comment counts per Short","Hashtags, description, duration and upload date","maxShorts: 0 collects every available Short","Optional MP4 download at best, 1080p, 720p or 480p","Audio-only download option for transcription pipelines","Downloaded files saved to the run's Key-Value Store with a public URL","downloadStatus on every row so failures are auditable, not silent","Stable row shape - gated fields stay present as null rather than disappearing","Per-video log warning when a creator has hidden public stats","Rows saved as they are scraped, so aborted runs keep their partial data","Channel problems end as a successful run with an empty dataset, not a crash","Optional MCP delivery to Notion, Slack, Linear, Airtable and more","Four Console dataset views including engagement and download outcomes"],"isAccessibleForFree": false,"softwareRequirements": "Apify account and API token","keywords": "YouTube Shorts scraper, YouTube Shorts downloader, short-form video, Shorts analytics, video download, creator research, engagement metrics, hashtag analysis, MP4 download, audio extraction, MCP connector"}
{"@context": "https://schema.org","@type": "FAQPage","mainEntity": [{"@type": "Question","name": "Do I need a YouTube or Google account, API key, or OAuth app?","acceptedAnswer": {"@type": "Answer","text": "No. The Actor reads publicly available Shorts 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": "Why not just use the YouTube Data API for Shorts?","acceptedAnswer": {"@type": "Answer","text": "The Data API has no Shorts flag — you would list a channel's uploads and guess which ones are Shorts from duration. It also has a per-project daily quota, and it cannot download media at all."}},{"@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 serves the platform's shared IPs a \"Sign in to confirm you're not a bot\" wall, which shows up as an empty or thin dataset."}},{"@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-shorts-scraper\""}},{"@type": "Question","name": "What can I put in channelUrl?","acceptedAnswer": {"@type": "Answer","text": "A full channel URL, an @handle, a UC channel ID, or a plain channel name. A URL with a /videos or /about tab suffix is normalised for you. A plain name is a best-effort lookup that falls back to a channel search, so check the channelUrl on the returned rows."}},{"@type": "Question","name": "Can I scrape several channels in one run?","acceptedAnswer": {"@type": "Answer","text": "No — one channel per run. Run the Actor once per channel and concatenate the datasets; every row carries channelName and channelUrl, so a combined dataset stays separable."}},{"@type": "Question","name": "How do I get every Short a channel has ever posted?","acceptedAnswer": {"@type": "Answer","text": "Set maxShorts: 0, which is uncapped. On a prolific channel that is a long run and a large event bill, so pair it with maxTotalChargeUsd."}},{"@type": "Question","name": "Does it return the channel's long-form videos too?","acceptedAnswer": {"@type": "Answer","text": "No. It reads the Shorts tab only. For long-form video metadata, search, hashtags or comments, use the YouTube Scraper."}},{"@type": "Question","name": "Why are viewCount and likeCount null?","acceptedAnswer": {"@type": "Answer","text": "Either the creator hid public stats, or extendedMetadata is off. The log warns per video when YouTube withholds counts. Null does not mean zero — exclude those rows from averages rather than coercing them."}},{"@type": "Question","name": "Why isn't uploadDate in ISO-8601?","acceptedAnswer": {"@type": "Answer","text": "It is YYYYMMDD, exactly as YouTube reports it. Parse that format rather than assuming ISO."}},{"@type": "Question","name": "Can I get the comments themselves?","acceptedAnswer": {"@type": "Answer","text": "No — only commentCount. Comment threads are the personal-data-heavy part of YouTube and are deliberately out of scope here. Use the YouTube Scraper if you need them."}},{"@type": "Question","name": "Can I get transcripts of the Shorts?","acceptedAnswer": {"@type": "Answer","text": "Not from this Actor. Use the YouTube Transcript Scraper for one video, or the Full Channel Transcripts Scraper for a whole channel."}},{"@type": "Question","name": "Why did my run succeed but return nothing?","acceptedAnswer": {"@type": "Answer","text": "Because the channel, not the Actor, was the problem: it may not have resolved, may have no Shorts tab, or the listing request may have been blocked. Rather than crashing, the Actor ends the run successfully with an empty dataset and the reason in the log — so scheduled runs don't produce an alert storm. Read the log before retrying."}},{"@type": "Question","name": "How do I download the actual video files?","acceptedAnswer": {"@type": "Answer","text": "Set downloadVideos: true. Each successful download is saved to the run's Key-Value Store as short-<videoId>.<ext>, and the row carries videoFileKey plus a public videoDownloadUrl."}},{"@type": "Question","name": "Can I get audio only?","acceptedAnswer": {"@type": "Answer","text": "Yes — videoQuality: \"audio\" writes audio/mp4 files, which are far smaller than video and the right input for a transcription or audio-analysis step."}},{"@type": "Question","name": "Some rows say failed — did the run break?","acceptedAnswer": {"@type": "Answer","text": "No. A failed download is logged, the row is saved with downloadStatus: \"failed\", and the run continues. Most failures also carry a downloadError; one mode (no file produced) does not, so branch on downloadStatus rather than on the error field."}},{"@type": "Question","name": "Who can access videoDownloadUrl?","acceptedAnswer": {"@type": "Answer","text": "Anyone with the link — it is a public Key-Value Store URL. Don't paste those URLs into public channels or shared documents, and clear the store when you're done with a run."}},{"@type": "Question","name": "Is downloading Shorts allowed?","acceptedAnswer": {"@type": "Answer","text": "A Short is the creator's copyrighted work, and YouTube's Terms of Service prohibit downloading except through features YouTube itself provides. Storing a copy for internal review is a very different act from republishing it. The download toggle raises your risk rather than sitting alongside it — read the Compliance & Data Privacy section before enabling it."}},{"@type": "Question","name": "What is the MCP connector for?","acceptedAnswer": {"@type": "Answer","text": "It pushes the scraped Shorts straight into a tool you have already authorized on your Apify account — Notion, Slack, Linear, Jira, GitHub, Airtable, or any MCP-compatible connector — with no glue code. Credentials stay private: delivery runs through the Apify MCP Proxy, so the Actor never sees your connector tokens."}},{"@type": "Question","name": "Which delivery mode should I use?","acceptedAnswer": {"@type": "Answer","text": "summary for a single digest, chunked for many Shorts into services with request caps (Notion), perShort for one record per Short, none for dataset-only runs. Delivery always runs after the dataset is saved, so a connector problem never costs you data."}},{"@type": "Question","name": "Does delivery work when running locally?","acceptedAnswer": {"@type": "Answer","text": "No. Connectors resolve through the Apify MCP Proxy, which only exists on the platform. Locally you'll see APIFY_MCP_PROXY_URL is not set and the dataset is saved as normal."}},{"@type": "Question","name": "What does it cost?","acceptedAnswer": {"@type": "Answer","text": "Pay-per-event: you are billed per Short saved to the dataset. A run that finds nothing costs nothing in event charges. Current per-Short rates are on the Actor's Apify Store page, and the Console shows a live total while a run is in progress."}},{"@type": "Question","name": "What do I get on the free plan?","acceptedAnswer": {"@type": "Answer","text": "2 Shorts per run, regardless of maxShorts. The run succeeds and says so in both the log and the run status message — treat it as a connectivity check, not a data pull."}},{"@type": "Question","name": "How do I guarantee an agent never overspends?","acceptedAnswer": {"@type": "Answer","text": "Pass maxTotalChargeUsd on the run endpoint — it is a hard per-run ceiling — and cache results by id so repeat reads cost nothing. Always set it when downloadVideos is on."}}]}