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
Scrape YouTube Shorts 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. Newest first. The cheapest YouTube Shorts scraper on the market. No API key, no cookies, no browser.
Pricing
from $0.40 / 1,000 short scrapeds
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 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 it. 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, not filtered out of the uploads feed by guessing at duration.
- Exact numbers, not the rounded ones the page shows: 27,549,434 views and 1,023,955 likes, not "27M" and "1M".
- Channels and keywords in the same run, up to 20 targets, with the row budget shared evenly between them.
- @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.
- Runs with empty input return a labelled sample row, free, so you can see the shape before spending anything.
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).
This is the cheapest YouTube Shorts scraper on the market, and it is the same rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.
| 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. - Free: the sample row an empty run returns, and every diagnostic row — a blocked target, a dead URL, a search that matched nothing. Those rows all carry
"charged": false. - Shorts already returned earlier in the same run are dropped before they are 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 that YouTube mixes into a filtered search are discarded, not billed as Shorts.
- A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row. The billed event is a named one, so there is no price quietly attached to
apify-default-dataset-item— the trick that makes some scrapers bill you for their own error messages.
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. Combine with channelUrls in the same run if you like. |
maxItems | Total rows across all targets. The budget is split evenly, so four targets and 60 rows gives you fifteen each. Default 20, hard ceiling 5,000. Keep it low while testing — you pay per row. |
maxItemsPerTarget | Optional hard cap per channel or keyword. Leave it empty to let maxItems split 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: title, id, URL, thumbnail and a rounded view count. |
sortBy | newest (default) orders the rows this run collected by their real publish date. feed keeps YouTube's own order instead. |
proxyUrls | Leave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for, as http://user:pass@host:port. |
Run it with empty input and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.
Output
One row per Short. A 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": 27549434,"viewCountText": "27M views","viewCountIsApproximate": false,"likeCount": 1023955,"publishedAt": "2026-08-11T16:00:06.000Z","publishedTimeText": null,"durationSeconds": 74,"thumbnail": "https://i.ytimg.com/vi/LiH-P4rSkLI/sardefault.jpg","channelName": "MrBeast","channelId": "UCX6OQ3DkcsbYNE6H8uQQuVA","channelUrl": "https://www.youtube.com/channel/UCX6OQ3DkcsbYNE6H8uQQuVA","position": 1,"statsError": null,"scrapedAt": "2026-08-15T21:52:26.584Z"}
Field notes
videoId— the 11-character YouTube id. Stable, and safe to use as a primary key across re-runs.url— always the/shorts/<id>form. The same video also plays at/watch?v=<id>if you prefer that.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. "27M 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 is not always under 60.thumbnail— the largest still the feed offered. It is 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 could not complete for that one video; the row is still delivered with everything the feed gave.
Every real row carries "charged": true. Sample rows carry "_sample": true and diagnostic rows carry "_diagnostic": true with an errorCode you can filter on, and neither is ever billed.
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 it picks up ordinary short videos that are not 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 is what turns "27M views" into 27,549,434, and adds the exact like count, the publish date and the duration. It is the smallest response that carries all four; the obvious Shorts-specific alternative returns ninety times more data for less information.
- Requests leave through a large pool of rotating datacenter addresses, so a per-address rate limit is answered by moving rather than by waiting.
- 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.
What people use it for
- Tracking a creator's Shorts output over time — schedule the same channels daily and diff on
videoIdto see only what is new, with view and like counts attached. - Competitive research on a niche: run five channels in one go and sort the rows by
viewCountto see which Shorts actually landed. - Finding hooks that work — pull a keyword's Shorts, sort by views per day since
publishedAt, and read the top twenty titles before writing your own. - Building a Shorts dataset for analysis: the rows are flat and typed, so they load straight into a spreadsheet or a database with no cleaning.
- Influencer vetting — a channel's recent Shorts with real like-to-view ratios tell you more about engagement than a subscriber count does.
- Feeding a dashboard or a newsletter with a clean table instead of scraping a rendered page.
Reading the output
Every run writes three kinds of row, and they are easy to tell apart:
- Real rows carry
"charged": trueand"recordType": "short". These are the rows you paid for, one billed event each. - The sample row carries
"_sample": trueand"charged": false. There is exactly one, it only appears when the input had no channels and no keywords, and it exists so you can look at the output shape before you spend anything. - Diagnostic rows carry
"_diagnostic": true,"charged": falseand anerrorCodeyou can switch on:NO_RESULTSwhen a channel has no Shorts or a keyword matched none,NOT_FOUNDwhen a handle does not exist,RATE_LIMITEDwhen YouTube throttled the run,SERVER_ERRORwhen YouTube itself faltered,NETWORKwhen it could not be reached, andTIME_BUDGETwhen the run ran out of time before reaching a target. Each carries a plain-Englisherrorand theinputit belongs to.
If you only want the data, filter on charged == true. The count of those rows always equals the number of events you were billed for, so the dataset is its own invoice.
Channels versus keywords
The two modes behave differently, and it is worth knowing which you want.
A channel returns that channel's Shorts tab, which is 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 will not 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 is "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. That is roughly 2 rows in 100 and there is 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, and the row budget decides how deep it goes.
- Keyword results cannot 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 is currently trending will read differently ten minutes later.
- Comment counts, descriptions, hashtags, the audio track and the transcript are not returned. This Actor 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, so they cannot be returned.
- Ordering is by publish date, not by views. Sort the dataset yourself if you want the biggest rather than the newest.
- The hard ceilings are 5,000 rows and 20 targets per run. Split larger jobs across runs.
- Everything is read logged out, so anything YouTube only shows to a signed-in viewer is out of scope by design.
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 Actor reads the Shorts surfaces themselves.
Why are some like counts empty?
The creator turned like counts off for that video. It is a per-video setting and the number is simply not 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. The run brings its own egress and its cost is already inside the price you see. The proxyUrls field exists only for callers who specifically want traffic to leave through servers they already own.
Will the run fail if a channel does not exist?
No. You get one uncharged diagnostic row for that target and the run carries on to the others, finishing as succeeded. A failed run would still bill the start fee, which would mean 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. The price per row is the same either way.
How do I get exactly the rows I paid for?
Filter the dataset on "charged": true. Sample and diagnostic rows are always false, and the number of charged rows always equals the number of billed events.