YouTube Shorts Scraper - $0.40 per 1,000 Shorts
Pricing
from $0.40 / 1,000 short scrapeds
YouTube Shorts Scraper - $0.40 per 1,000 Shorts
No guessing from video length here - Shorts come off the Shorts tab itself, newest first, from any channel or keyword. One row per Short: title, video id, URL, exact view count, exact like count, publish date, duration, thumbnail, channel name and id. $0.40 per 1,000.
Pricing
from $0.40 / 1,000 short scrapeds
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
YouTube Shorts Scraper
Give it YouTube channel URLs or handles and get that channel's Shorts feed back, newest first. Or give it a keyword and get the Shorts that match. Every row carries the title, the video id, the Short URL, the exact view count, the exact like count, the publish date, the duration in seconds, the thumbnail, and the channel name and id.
No account, no cookies, no API key, no browser.
- Shorts specifically, read from the channel's own Shorts tab rather than guessed out of the uploads feed by duration.
- Exact numbers, not the rounded ones the page shows: 37,061,858 views and 1,268,537 likes, not "37M" and "1.2M".
- Channels and keywords in the same run, up to 20 targets, with the row budget shared evenly.
@handles,/channel/UC…,/c/…,/user/…and legacy vanity URLs all resolve, including the old redirect-style ones.- Newest first by real publish date, not by whatever order the feed happened to serve.
Price
$0.40 per 1,000 Shorts, plus a $0.0005 start fee per run (billed per gigabyte of run memory, so exactly that on the default 1 GB).
The rate is the same on the free plan and on every paid one. No volume tiers, no minimum spend, nothing charged monthly.
| Shorts | Total cost |
|---|---|
| 100 | $0.0405 |
| 1,000 | $0.4005 |
| 10,000 | $4.0005 |
| 100,000 | $40.0005 |
What is actually charged
- One
short-scrapedevent per Short row written to the dataset. Nothing else is metered per row. - Never charged: the sample row, and any diagnostic row. A dead handle, a throttled request and
a keyword that matched nothing all come back at no cost, marked
"charged": false. - Shorts already returned earlier in the same run are dropped before they're charged, so overlapping keywords never bill twice.
- A channel that has never posted a Short costs you nothing beyond the start fee. You get one free diagnostic row saying so.
- Ordinary long-form videos YouTube mixes into a filtered search are discarded, not billed.
- A run that finds nothing costs the start fee and nothing more.
- A row without a charge and a charge without a row are both impossible here. The billed event is
named
short-scraped, not Apify's generic per-item event.
Input
{"channelUrls": ["https://www.youtube.com/@MrBeast", "@mkbhd"],"searchQueries": ["cooking hacks"],"maxItems": 60,"sortBy": "newest"}
| Field | What it does |
|---|---|
channelUrls | Channel URLs or handles. https://www.youtube.com/@MrBeast, @mkbhd, https://www.youtube.com/channel/UCX6OQ3DkcsbYNE6H8uQQuVA, youtube.com/c/mkbhd, youtube.com/user/PewDiePie and a bare UC… id all work. Up to 20 targets per run when combined with searchQueries. |
searchQueries | Keywords to search YouTube Shorts for. One row per matching Short. Mix with channelUrls in the same run if you like. |
maxItems | Total rows across all targets, split evenly, so four targets and 60 rows gives fifteen each. Default 20, hard ceiling 5,000. |
maxItemsPerTarget | Optional hard cap per channel or keyword. Leave it empty and maxItems splits evenly. |
includeStats | On by default. Fetches each Short's exact view count, like count, publish date and duration. Turn it off for a faster run that returns only what the feed itself shows. |
sortBy | newest (default) orders the rows this run collected by real publish date. feed keeps YouTube's own order. |
proxyUrls | Leave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for. |
Run it with empty input and you get one labelled sample row, uncharged, so you can see the shape before spending.
Output
One row per Short. Real row from a real run:
{"ok": true,"charged": true,"recordType": "short","input": "https://www.youtube.com/@MrBeast","searchQuery": null,"videoId": "LiH-P4rSkLI","title": "Can You Pass This Classroom Quiz?","url": "https://www.youtube.com/shorts/LiH-P4rSkLI","viewCount": 37061858,"viewCountText": "37M views","viewCountIsApproximate": false,"likeCount": 1268537,"publishedAt": "2026-08-11T16:00:06.000Z","publishedTimeText": null,"durationSeconds": 74,"thumbnail": "https://i.ytimg.com/vi/LiH-P4rSkLI/frame0.jpg","channelName": "MrBeast","channelId": "UCX6OQ3DkcsbYNE6H8uQQuVA","channelUrl": "https://www.youtube.com/channel/UCX6OQ3DkcsbYNE6H8uQQuVA","position": 1,"statsError": null,"scrapedAt": "2026-08-20T19:53:09.251Z"}
Field notes
videoId— the 11-character YouTube id. Stable, and safe as a primary key across re-runs.url— always the/shorts/<id>form. The same video also plays at/watch?v=<id>.viewCount— the exact integer whenincludeStatsis on. With stats off it falls back to the rounded figure the feed prints.viewCountIsApproximate— true when the count is the rounded feed figure rather than the exact one. Check this before doing arithmetic on views.viewCountText— the view count exactly as the feed shows it, e.g. "37M views". Kept so you can reconcile a row against what you see on screen.likeCount— the exact like count. Null when the creator has hidden likes on that Short, around 2 in 100 in testing.publishedAt— publish time as ISO 8601 in UTC.publishedTimeText— the relative date a search result carries, e.g. "13 days ago". Usually null on channel rows, wherepublishedAtis exact.durationSeconds— length in whole seconds. Shorts run up to about three minutes, so this isn't always under 60.thumbnail— the largest still the feed offered. It's a vertical 9:16 frame, not the 16:9 thumbnail a normal video has.channelId— theUC…id, which never changes even when the handle does.channelUrlis built from it.position— the row's place in the delivered order for its target, starting at 1.statsError— set only when the per-Short metadata call couldn't complete for that one video. The row still arrives with everything the feed gave.
How it works
Shorts live behind their own tab on a channel and their own filter in search, so the run reads those surfaces directly rather than pulling the uploads feed and guessing which entries are Shorts by their length. Guessing gets it wrong both ways: it misses Shorts longer than a minute and picks up ordinary short videos that aren't Shorts at all.
Everything is plain JSON, requested and parsed. No page rendering, no headless browser, no login, no cookies and no API key, which is why a run costs what it does.
A second small request per Short turns "37M views" into 37,061,858 and adds the exact like count, the publish date and the duration. It's the smallest response that carries all four; the obvious Shorts-specific alternative returns ninety times more data for less information.
YouTube counts requests per address, so the run spreads them across a large pool of datacenter addresses and steps to the next one instead of sitting out a cooldown.
Channel handles are resolved in cost order. An id already in the URL costs nothing, a handle costs one small lookup, and the old redirect-style vanity URLs cost one more. Only if all of that fails does the run fall back to reading the channel page itself.
Diagnostic rows
A target that doesn't work out comes back as a row instead of killing the run. Those rows carry
"_diagnostic": true, "charged": false and an errorCode: NO_RESULTS when a channel has no
Shorts or a keyword matched none, NOT_FOUND when a handle doesn't exist, RATE_LIMITED when
YouTube throttled the run, SERVER_ERROR when YouTube itself faltered, NETWORK when it couldn't
be reached, TIME_BUDGET when the run ran out of time. Each carries a plain-English error and the
input it belongs to.
The free sample row is tagged "_sample": true and only appears when the run was given no channels
and no keywords. Filter on charged == true for the data alone. Those are the rows you paid for,
one event each, and nothing is billed that isn't there.
Channels versus keywords
The two modes behave differently and it's worth knowing which you want.
A channel returns that channel's Shorts tab, already in newest-first order, and pages through it until your row budget runs out. Ask for 500 and you get the 500 most recent Shorts that channel posted.
A keyword returns the Shorts YouTube considers the best matches for that phrase. YouTube won't
sort a Shorts-filtered search by date. Asking it to do so quietly turns the Shorts filter off and
hands back ordinary long videos instead. So sortBy: "newest" orders the Shorts this run actually
collected by their real publish date. That's "the newest of the matches found", not "the newest
Shorts on YouTube matching this phrase". If you want the latter, run a channel instead. A creator's
tab really is chronological.
Limitations
- A channel that has never posted a Short has no Shorts tab at all. You get one free diagnostic row saying so and no charged rows. Around one channel in twelve in our test set was like this.
likeCountis null when the creator has hidden likes on a Short. Roughly 2 rows in 100, and there's no way to recover the number.- Keyword search returns what YouTube ranks as the best matches, not an exhaustive index. Two runs of the same keyword minutes apart can return a slightly different set.
- Keyword results can't be sorted newest-first by YouTube itself, so
sortBy: "newest"sorts the Shorts the run collected. Channel mode has no such caveat. - View and like counts are a snapshot at read time. A Short that's currently trending will read differently ten minutes later.
- Comment counts, descriptions, hashtags, the audio track and the transcript aren't returned. This is the Shorts feed with its headline metrics, nothing more.
- Age-restricted and members-only Shorts may return without a like count or a publish date. The row
still arrives with everything the feed showed, and
statsErrorsays why. - Private, deleted and region-blocked Shorts never appear in the feed in the first place.
- Ordering is by publish date, not by views. Sort the dataset yourself if you want the biggest rather than the newest.
- Hard ceilings: 5,000 rows and 20 targets per run.
- Everything is read logged out, so anything YouTube only shows a signed-in viewer is out of scope.
Questions
How is this different from scraping a channel's videos and keeping the short ones?
Shorts are a separate surface on YouTube, not a length category. A channel's uploads feed and its Shorts tab are different lists, and a Short can run up to about three minutes while plenty of ordinary videos are under a minute. Filtering by duration therefore misses real Shorts and invents fake ones. This reads the Shorts surfaces themselves.
Why are some like counts empty?
The creator turned like counts off for that video. It's a per-video setting and the number isn't published anywhere, so no scraper can recover it. You still get the row and everything else about it.
Do I need a proxy, an API key or a YouTube account?
None of the three. Egress is included in the rate above. proxyUrls is there only for people who
already pay for their own servers and would rather the traffic left through those.
Will the run fail if a channel doesn't exist?
No. One uncharged diagnostic row for that target and the run carries on to the others, finishing as succeeded. Failing it would still bill the start fee, so you'd be paying to be told about a typo.
Can I run this on a schedule?
Yes. Nothing is held between runs, so the same input is safe to repeat. Use videoId to work out
which rows are new since last time.
What does turning includeStats off actually save?
It skips one small request per Short, so the run finishes noticeably faster. You keep the title, id, URL, thumbnail, channel and a rounded view count, and you lose the exact view count, the like count, the publish date and the duration. Price per row is the same either way.