YouTube Shorts Scraper : Views, Likes & Downloads avatar

YouTube Shorts Scraper : Views, Likes & Downloads

Pricing

from $8.50 / 1,000 results

Go to Apify Store
YouTube Shorts Scraper : Views, Likes & Downloads

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

Techforce Global

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Share

YouTube Shorts Scraper

Apify Actor MCP Ready Pricing Output Public data only Target

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

CredentialsNone 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 scopePublic Shorts metadata only. No private or members-only content, no login, and no comment text — only the comment count.
Personal dataTitles, view counts, hashtags and durations are content metadata, not personal data. That makes this Actor materially lower-risk than comment scrapers.
Media downloadOff 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 filesLand 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 / CapabilityOfficial YouTube Data API v3YouTube 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 modelFree within quota, then quota-increase requestsPay 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 an
MP4 (or audio-only) file into the run's Key-Value Store. No API key required. Optional
delivery 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 once
per 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. A
plain name is a best-effort lookup that falls back to a YouTube channel search, so it can
resolve 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 for
long-form video metadata, or the Full Channel Transcripts Scraper for the complete catalogue
of what was said.
extended_metadata_gates_engagement: >
viewCount, likeCount, commentCount, description and hashtags require extendedMetadata (on by
default). With it off, those keys are still present but null/empty, so every row keeps the
same columns. Enabling downloadVideos forces extendedMetadata on.
download_is_off_by_default: >
downloadVideos defaults to false. When off, every row carries downloadStatus "skipped" and
no file is written - this is expected, not a failure. When on, each successful download is
saved 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 most
but 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 end
as 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: 1
shorts_per_run: "maxShorts, or all available when maxShorts is 0"
free_plan_shorts_per_run: 2
pricing:
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 for
internal review.
when_not_to_use: >
You need long-form videos, transcripts, comment text, several channels in one run, search
terms 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 an
upstream 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, a UC… 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, likeCount and commentCount alongside duration and hashtags, which is what you need to compare short-form performance across a channel's output.

  • Optionally downloads the files. With downloadVideos on, each Short is fetched at your chosen quality — best, 1080p, 720p, 480p, or audio for 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 extendedMetadata are still present as null or [] when it's off, so your consumer never has to handle missing columns.

  • Records download outcomes honestly. downloadStatus is on every row — skipped, downloaded or failed — with downloadError explaining 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 viewCount is null.

  • De-duplicates within a run. YouTube's Shorts shelf occasionally repeats an entry across pages; the same id is 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 ABORTING event 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 filesThis Actor
Long-form videos, search terms, hashtags, or comment threadsYouTube Scraper
One video's transcript, timestampedYouTube Transcript Scraper
Transcripts of every video on a channelFull 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

ScenarioFit
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)

  1. channelUrl is 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.

  2. channelUrl accepts 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 says Resolved "<input>" via search to: <url> when that path is used. Always check channelUrl on the output rows before trusting the data is the creator you meant.

  3. Only Shorts are returned. If you expected the channel's long-form videos, this is the wrong Actor.

  4. An empty dataset is a successful run. An unresolvable channel, no Shorts tab, or a blocked listing all end SUCCEEDED with zero items and the reason in the log. Do not treat itemCount: 0 as a crash — read the log line before retrying.

  5. downloadVideos is false by default, and that's usually right. With it off, every row shows downloadStatus: "skipped" and no file is written. That is expected output, not a failure — the log says so explicitly at the start of the run.

  6. Turning on downloadVideos changes the run substantially. It fetches media per Short, so run time and storage both grow sharply, and extendedMetadata is forced on. Start with a small maxShorts, and raise the run timeout (default 1 hour) for large download runs.

  7. maxShorts: 0 means all available Shorts, not "none". On a large channel that is a long, expensive run — pair it with maxTotalChargeUsd.

  8. extendedMetadata gates the interesting fields. viewCount, likeCount, commentCount, description and hashtags all need it. With it off those keys are still present but null / [] — so rows keep a stable shape, but you get no engagement data.

  9. null engagement 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.

  10. downloadStatus is the field to branch on. skipped / downloaded / failed. videoDownloadUrl and videoFileKey exist only on downloaded. downloadError covers most failed rows but not all — one failure mode leaves it absent, so branch on downloadStatus, never on the presence of downloadError.

  11. 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.

  12. uploadDate is YYYYMMDD, not ISO-8601. Parse accordingly.

  13. Dataset order is not chronological. Shorts are processed concurrently, so rows land in completion order. Sort by uploadDate (or viewCount) yourself.

  14. 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".

  15. 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.

  16. deliveryMode defaults to summary, not none. For dataset-only runs, either leave mcpConnector empty or set deliveryMode: "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: 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 any table on this page disagrees with the schema files, the schema files win.

Scraping parameters

ParameterTypeRequiredDefaultAllowed values / formatExample
channelUrlstring✅ Yesnone (Console prefill only)One channel URL, @handle, UC… ID, or plain name. Not an array.https://www.youtube.com/@MrBeast
maxShortsinteger⬜ No250 = all available; otherwise the cap, newest first200
extendedMetadataboolean⬜ NotrueNeeded for views, likes, comments, description and hashtags. Forced on by downloadVideos.true
downloadVideosboolean⬜ NofalseDownloads each Short into the run's Key-Value Storefalse
videoQualitystring (enum)⬜ Nobestbest · 1080p · 720p · 480p · audio — only used when downloading720p
proxyConfigurationobject⬜ No{"useApifyProxy": true}Apify proxy editor object. Leave enabled.see below

Accepted channel input shapes

ShapeExampleResolution
Channel URLhttps://www.youtube.com/@MrBeastDirect — a /videos, /about or other tab suffix is stripped for you
Shorts tab URLhttps://www.youtube.com/@MrBeast/shortsDirect
Handle@MrBeastDirect
Channel IDUCX6OQ3DkcsbYNE6H8uQQuVADirect
Plain nameMrBeastTried 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)

ParameterTypeDefaultDescription
mcpConnectorstring""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.
deliveryModestring (enum)"summary"summary (one call covering every Short) · chunked (split across calls/pages) · perShort (one call per Short) · none.
mcpToolstring""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.
mcpArgumentsobject{}Arguments for the tool. String leaves support {placeholder} tokens.
mcpMessageTemplatestring""Template rendered and exposed to mcpArguments as {message}.

Placeholders by mode:

ModePlaceholders
summary{channelName}, {channelUrl}, {shortCount}, {shortsText} (formatted list of every Short), {message}
chunkedthe 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.

perShort fires one connector call per Short. A 200-Short run is 200 calls — use chunked or summary unless 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.

FieldTypeNullableToggle / controlled byMeaning
idstringNoYouTube video ID. The stable key for joining runs.
urlstringYesCanonical URL of the Short.
titlestringYesShort title.
channelNamestringYeschannelUrlChannel the Short belongs to — check this when you passed a plain name.
channelUrlstringYeschannelUrlURL of the resolved channel.
thumbnailUrlstringYesThumbnail image URL.
durationnumberYesLength in seconds.
viewCountintegerYesextendedMetadataViews at capture time. Null when hidden or extendedMetadata is off.
likeCountintegerYesextendedMetadataLikes at capture time. Null when hidden or extendedMetadata is off.
commentCountintegerYesextendedMetadataComment count. The comments themselves aren't scraped.
uploadDatestringYesextendedMetadataYYYYMMDD as YouTube reports it — not ISO-8601.
descriptionstringYesextendedMetadataDescription text.
hashtagsarrayNoextendedMetadataTags on the Short. [] when none, or when extendedMetadata is off.
downloadStatusstringNodownloadVideosAlways present: skipped, downloaded or failed.
videoDownloadUrlstringYesdownloadVideosPublic Key-Value Store URL. Only when downloaded.
videoFileKeystringYesdownloadVideosStore key, short-<id>.<ext>. Only when downloaded.
downloadErrorstringYesdownloadVideosWhy it failed. Present on most — not allfailed rows.

Missing values are genuine null (or [] for hashtags), 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:

Keyshort-<videoId>.<ext> — also on the row as videoFileKey
Content typevideo/mp4, or audio/mp4 when videoQuality: "audio"
Public URLon the row as videoDownloadUrl
Console tabDownloaded 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: null in 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 downloadVideos is on
  • Log: an explicit note when downloadVideos is 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

ViewShows
ShortsTitle, channel, engagement, duration and download state
EngagementViews, likes, comments, duration and hashtags for performance comparison
DownloadsDownload outcome, store key and error per Short
ThumbnailsVisual 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 pageInput 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 os
from apify_client import ApifyClient
client = 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'
# 1. Start execution with a hard spend limit
curl -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. Poll
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/actor-runs/<runId>'
# 3. Retrieve when SUCCEEDED
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 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.

ConditionWhat you observeUnderlying causeDeterministic agent action
MISSING_CHANNEL_URLRun rejected at input validationchannelUrl absent — it is required in the schemaModify Input: supply one channel identifier.
EMPTY_CHANNEL_URLSUCCEEDED, empty dataset; log: No channel name, handle, or URL provided…channelUrl present but blankModify Input: pass a non-empty value.
ARRAY_PASSED_AS_CHANNEL_URLValidation error on typechannelUrl is a string, not an arrayModify Input: one identifier as a string; run once per channel.
CHANNEL_NOT_RESOLVEDSUCCEEDED, 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 tabDo not blind-retry. Pass the full channel URL or @handle; confirm the Shorts tab opens in a browser.
WRONG_CHANNEL_RESOLVEDSUCCEEDED with data, but channelUrl isn't the creator you meant; log: Resolved "…" via search to: …A plain name was matched by channel searchValidate & re-run: pass the exact URL, @handle, or UC… ID.
LISTING_FAILEDSUCCEEDED, empty dataset; log: Failed to list Shorts for channel …Listing request errored — commonly a bot-blockRetry once with {"useApifyProxy": true}; then treat as unavailable.
NO_SHORTS_FOUNDSUCCEEDED, empty dataset; log: No Shorts found for channel …Channel genuinely publishes no ShortsDo not retry. Route long-form needs to the YouTube Scraper.
ALL_SKIPPEDEvery row downloadStatus: "skipped"downloadVideos is false (the default)Expected. Set it true if you wanted files.
DOWNLOAD_FAILED_ROWSSome rows failed, usually with downloadError; log: Failed to download … or Download produced no file for …That Short couldn't be fetched at the requested qualityExpected, partial. Retry those IDs with videoQuality: "best", or accept the gap. Run is still valid.
NULL_ENGAGEMENTviewCount / likeCount null; log: View/like counts unavailable for …Creator hid stats, or extendedMetadata offCheck extendedMetadata. If on, the stats are genuinely hidden. Null ≠ zero.
METADATA_FETCH_FAILEDA row has nulls across engagement fields; log: Could not fetch extended metadata for …That single video's detail request failedPartial, not fatal. Re-run for those IDs if the fields matter.
NO_DESCRIPTION_OR_HASHTAGSdescription null, hashtags [] across all rowsextendedMetadata is offModify Input: enable it.
RUN_LONGER_THAN_EXPECTEDRun still going well past estimatedownloadVideos: true with a large or 0 maxShortsAbort and lower maxShorts, or wait with a spend cap in place. Rows already saved are kept.
RUN_TIMED_OUTTIMED-OUT with a partial datasetWork exceeded the run timeout (1 hour by default)Expected, partial. Raise the timeout or lower maxShorts; saved rows are valid.
UPLOAD_DATE_NOT_PARSEABLEuploadDate won't parse as ISOIt's YYYYMMDD by designFix expectation: parse that format.
FREE_PLAN_CAPPED2 Shorts; log and run status message say soFree Apify planUpgrade the plan.
CHARGE_FAILEDLog: Charge failed for 'short-scraped'…Billing event could not be recordedNone — data is unaffected. The row is still saved.
CONNECTOR_RATE_LIMITEDConnector errors partway throughperShort on a large runModify Input: switch to chunked or summary.
CONNECTOR_TOOL_MISSINGLog: A connector is selected but no tool name was providedmcpTool emptyModify Input: set mcpTool. Dataset already saved.
CONNECTOR_TOOL_NOT_FOUNDLog: Tool 'x' is not available on this connector. Available tools: …mcpTool name is wrongModify Input: copy an exact name from that log line. Dataset unaffected.
CONNECTOR_UNREACHABLELog: Could not reach the connector: …Connector not authorised, or the run isn't on the Apify platformAuthorise it in Apify, or set deliveryMode: "none". Data already saved.
MCP_PROXY_MISSINGLog: APIFY_MCP_PROXY_URL is not setRunning locally with apify runReconfigure: apify push and run on the platform.
AUTH_INVALIDHTTP 401Missing or malformed APIFY_TOKENAbort & prompt user: request a valid API token.
RATE_LIMITEDHTTP 429Apify API rate / concurrency limitsRetry: exponential backoff (2s, 4s, 8s).
SYNC_TIMEOUTHTTP 408 on the sync endpointRun exceeded the synchronous ceilingReconfigure: 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…IntentConstructed 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 monitorFixed input on a schedule; join runs on id
"Just a quick sample"Cheap probe{"channelUrl":"<url>","maxShorts":3,"downloadVideos":false}
"Compare three creators"Multi-channelThree 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 + deliverAbove, plus mcpConnector, mcpTool, deliveryMode
"It returned nothing"Empty resultRead 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.

ChoiceEffect on cost
maxShortsThe direct lever on event charges. 0 means uncapped — budget accordingly.
downloadVideosThe biggest effect on run time and storage. Media fetches per Short, plus Key-Value Store usage.
videoQualityaudio and 480p are far smaller than best. Pick the lowest that answers your question.
extendedMetadataA metadata fetch per Short. Leave on — the engagement data is usually the point.
deliveryModeperShort issues one connector call per Short; summary/chunked issue a handful.
Being blockedCosts 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.

ScenarioShorts billedRelative run costNotes
Smoke test — maxShorts: 3, no download3BaselineConfirms the channel resolved.
Performance analysis — 200 Shorts, no download200≈ 67 × baseline in events, modest computeThe recommended starting point for research.
Same 200 Shorts with extendedMetadata: false200Same events, less computeYou lose every engagement field — rarely worth it.
50 Shorts downloaded at 720p50Events ≈ 17 × baseline, compute and storage far higherMedia fetch dominates. Set a spend cap.
50 Shorts as audio50Same events, much lower storageRight choice for transcription pipelines.
Whole channel, maxShorts: 0, no downloadall of themScales linearly with the channelUnbounded by definition — cap the spend.
Unresolvable channel or no Shorts0Near zeroEnds early with an empty dataset.
Three creators compared3 runsSum of the threeOne run per channel; combine on channelName.

Keeping runs predictable:

  1. First run: maxShorts: 3, downloadVideos: false. Confirm you resolved the right channel and are getting engagement counts.
  2. Do the analysis before downloading. Rank on viewCount first, then download only the Shorts you actually want.
  3. Pick the smallest useful videoQuality. audio if you only need sound; 480p if you need to see it but not in detail.
  4. Always set maxTotalChargeUsd when downloading. Media fetch time varies far more than metadata, and it is the only hard stop.
  5. Cache by id. Re-running a schedule only to re-read unchanged Shorts is a pure cost.
  6. 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 seeMost likely causeFix
1SUCCEEDED but the dataset is emptyThe channel didn't resolve, has no Shorts tab, or the listing was blockedRead the log: it names which. Pass the full channel URL or @handle, and keep Apify Proxy on.
2Data came back, but it's the wrong creatorYou 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.
3Thin or empty results that used to workBot-block — YouTube challenges the platform's shared IPsConfirm proxyConfiguration is {"useApifyProxy": true} and retry.
4Every row says downloadStatus: "skipped"downloadVideos is false — the defaultExpected. Set it true if you wanted files; the log states this at the start of every run.
5Some rows say failedThat Short couldn't be fetched at the requested qualityPartial, not fatal. Retry those IDs with videoQuality: "best". Check the Downloads view.
6A failed row has no downloadErrorOne 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.
7viewCount / likeCount are nullThe creator hid public stats, or extendedMetadata is offCheck extendedMetadata first. If it's on, the stats are genuinely hidden — exclude nulls, don't coerce to 0.
8description is null and hashtags is [] on every rowextendedMetadata is offTurn it on. It is on by default and forced on when downloading.
9uploadDate won't parseIt is YYYYMMDD, not ISO-8601Parse that format, or convert on ingest.
10Rows aren't in date orderShorts are processed concurrently and saved as they finishSort by uploadDate or viewCount client-side.
11Long-form videos are missingBy design — this Actor reads the Shorts tab onlyUse the YouTube Scraper.
12You wanted the comments, not the countOnly commentCount is collectedUse the YouTube Scraper for comment threads.
13Only 2 Shorts returnedFree Apify plan capUpgrade the plan. The log and the run status message both say so.
14Run hit the timeoutdownloadVideos: true with a large or 0 maxShorts against the 1-hour defaultRaise the run timeout or lower maxShorts. Rows saved before the timeout are valid.
15Run is far slower than expectedDownloading media, or maxShorts: 0 on a prolific channelDo the metadata pass first, then download a shortlist.
16videoDownloadUrl returns 404 laterKey-Value Stores expire on the platform's retention scheduleCopy files to your own storage if you need them long term.
17Slack / Notion / Linear received nothingDelivery needs both mcpConnector and mcpTool; otherwise it is skipped with a warningSet both. Every dataset row is still saved — your data is never lost.
18Log: Tool 'x' is not available on this connectorWrong tool name for that connectorCopy an exact name from the Available tools: list in the same log line.
19Connector fired, but the message body is emptymcpArguments did not reference {message}Map {message} in mcpArguments and populate mcpMessageTemplate.
20Every Notion page looks identical{part}/{partCount} missing from the title template in chunked modeAdd them: "Shorts {channelName} (part {part}/{partCount})".
21Hundreds of connector calls fireddeliveryMode: "perShort" sends one call per ShortSwitch to chunked or summary.
22Delivery fired when you only wanted a datasetdeliveryMode defaults to summary, not noneSet deliveryMode: "none" or leave mcpConnector empty.
23Log: APIFY_MCP_PROXY_URL is not setYou ran locally with apify run; connectors only resolve on the platformDeploy with apify push and run on the platform.
24HTTP 401 / 403Missing, expired, or malformed APIFY_TOKENRegenerate in Apify Console → Settings → API & Integrations, send as Authorization: Bearer <token>.
25HTTP 429Apify account concurrency / rate limits — not YouTube blockingRetry with exponential backoff (2s → 4s → 8s).
26HTTP 408 on run-sync-get-dataset-itemsThe synchronous endpoint has a hard time ceilingSwitch to async: POST /runs → poll /actor-runs/<runId> → fetch dataset.

Diagnostic checklist before opening an issue

  1. Open the channel's Shorts tab in a browser (youtube.com/@handle/shorts). If it's empty there, no scraper can produce Shorts.
  2. Pass the exact URL or @handle, not a plain name — this removes the search fallback from the equation.
  3. Confirm Apify Proxy is enabled. It is the single most common cause of empty or thin results.
  4. Baseline run: channelUrl only, maxShorts: 3, downloadVideos: false, deliveryMode: "none".
  5. Check channelName / channelUrl on the returned rows against the creator you meant.
  6. 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

FileWhat it defines
.actor/input_schema.jsonEvery input, its type, default, and the quality and delivery enums
.actor/dataset_schema.jsonAll 17 output fields with nullability, and the four Console views
.actor/key_value_store_schema.jsonThe downloaded-files collection and its content types
.actor/output_schema.jsonWhere the run's output is exposed
.actor/actor.jsonActor 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.


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.

ActorBest forWhy pick it over this one
YouTube Scraper: Videos, Channels & CommentsLong-form video, channel and comment metadata from search queries, channel URLs, video URLs or hashtagsYou need long-form videos, discovery by search or hashtag, or actual comment threads — none of which this Actor returns.
YouTube Transcript ScraperOne video's timestamped transcript, in 13 languagesYou want the words of a specific video (Shorts included) rather than a channel's engagement numbers.
YouTube Full Channel Transcripts ScraperTranscripts for every video on a channel, in one runYou'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

ActorUse it for
Advanced Website Crawling ActorCrawl the creator's own site or link-in-bio for clean HTML/Markdown/text context alongside the Shorts data
Pinterest ScraperCompare the same creative strategy on Pins from searches, boards and profiles
Facebook Public Comment TrackerTrack 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: false is 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 videoDownloadUrl is 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 viewCount or likeCount means 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. url and channelUrl are 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?



🏷️ 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."
}
}
]
}