Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

YouTube Shorts API

youtube-shorts-api

checks license

Most people building a youtube shorts api pipeline start with the search endpoint and get nothing. This repo starts by proving why, then gives the route that works: the Shorts tab for discovery at 1 credit, and the metadata endpoint for the numbers.

Built on ScrapingBee's web scraping API and its dedicated YouTube endpoints. Verified live on 2026-09-15.

Search does not return Shorts. At all.

This is worth settling before you write any code. Three content queries, 20 results each, every duration recorded:

Query Results Shortest video
satisfying asmr 20 4:38
kitchen hack 20 5:34
gym fail 20 5:10

Sixty videos, and not one under four and a half minutes. The full duration list for satisfying asmr runs 8:02, 20:01, 2:23:20, 3:07:51, 1:00:03 ... 24:00:00, including two twenty four hour livestreams.

Searching for the word "shorts" is no better. It returns long compilations about the format:

  13:03   2,789,749 views  World's FUNNIEST YouTube Shorts!
  46:43   2,882,326 views  I Tested Clickbait Shorts
1:04:53     687,709 views  1 HOUR of Genius Secrets I Learned on Yo

And in that entire 459,893 character response: zero /shorts/ URLs, zero reelWatchEndpoint, zero reelItemRenderer.

The reason is structural. YouTube puts Shorts in a separate shelf on the results page, and the endpoint returns the videoRenderer items only, so the Shorts shelf never reaches you. No parameter changes this. Filtering search output by duration returns an empty list, which is exactly what you would expect once you know the shelf is missing.

The discovery route that works

A channel's Shorts tab, through the standard HTML API. 1 credit.

curl -G "https://app.scrapingbee.com/api/v1/" \
  -H "Authorization: Bearer $SCRAPINGBEE_API_KEY" \
  --data-urlencode "url=https://www.youtube.com/@MrBeast/shorts" \
  -d mode=auto

That returned 1,179,717 bytes containing 48 unique Shorts video IDs, every one of them a genuine Short because the tab only lists Shorts. Pull them with one regex:

ids = sorted(set(re.findall(r"/shorts/([A-Za-z0-9_-]{11})", html)))
len(ids)   # 48

Do not turn on JavaScript rendering here

This is the counterintuitive part. Five runs against the same URL:

Configuration Credits Bytes Shorts IDs found
mode=auto 1 1,177,147 48
mode=auto 1 1,178,599 48
mode=auto 1 1,186,131 48
render_js=true 5 585,819 0
render_js plus premium_proxy 25 1,670,707 48

The 5 credit rendered fetch returns roughly half the bytes and not a single Short. Paying more for a browser actively breaks this page. The delivered HTML already carries everything, so mode=auto at 1 credit is both the cheapest and the most reliable option, and it was consistent across three consecutive runs.

Guard against the occasional empty response anyway. If the ID list comes back empty, retry rather than concluding the channel has no Shorts.

Two markers in that page worth knowing. shortsLockupViewModel appears 49 times and /shorts/ 48 times. reelItemRenderer, which most older scraping guides tell you to look for, appears zero times. YouTube renamed that renderer, so a parser written against the old name silently finds nothing on a page that is full of Shorts.

Authentication on the HTML API is the same header: Authorization: Bearer YOUR_API_KEY.

The numbers: metadata

GET https://app.scrapingbee.com/api/v1/youtube/metadata?video_id=<id>

5 credits. The parameter is video_id. It is not search, and sending the wrong one produces a precise error at 0 credits:

{"errors": {"query": {"search": ["Unknown field."], "video_id": ["Missing data for required field."]}}}

Unlike search, metadata returns a clean flat object with 19 keys and no wrappers:

video_id  title  description  duration  view_count  like_count  comment_count
channel_id  channel_url  uploader  uploader_id  uploader_url  upload_date
categories  tags  thumbnails  formats  is_live  age_limit

duration is an integer in seconds, which is the definitive Shorts test. Live results from three IDs pulled off the tab above:

video_id duration views likes title
5mU6SRS2Bxo 36 12,840,911 280,412 World's Largest Tennis Match
6W_841xoprg 30 121,014,585 2,224,624 Can a Window Stop a Wrecking Ball?
897gvApD6V4 8 800,626,321 3,822,847 Will a Zebra Subscribe to me?

like_count and comment_count exist nowhere else. Search gives you a view count string and nothing on engagement, so the metadata call is what makes performance analysis possible at all.

Metadata fails intermittently, and a retry fixes it

One of the four IDs tested returned this instead of a video:

{"error": "Something went wrong, please try again later or contact support."}

The same ID retried immediately returned duration: 36 and a full record. So treat a response whose only key is error as transient rather than as a dead video, and retry once before you write the ID off. The call is still billed, so a naive retry loop costs real credits.

Cost of a channel sweep

Discovery is nearly free and confirmation is where the money goes:

1 channel Shorts tab        =  1 credit   -> 48 video IDs
48 metadata calls           = 240 credits -> durations, views, likes, comments
                              -----------
                              241 credits for one channel's Shorts catalogue

At the entry paid tier of 250,000 credits that is roughly 1,000 channel sweeps a month, or far more if you only pull metadata for the IDs you have not seen before. Cache the IDs. A channel's back catalogue does not change, only the tail does.

If all you need is the ID list and posting cadence, stop after the 1 credit call.

Engagement rate

def engagement(meta):
    views = meta["view_count"]
    return (meta["like_count"] + meta["comment_count"]) / views if views else None

On the wrecking ball Short: 2,224,624 likes over 121,014,585 views is 1.84 percent. On the zebra Short: 3,822,847 over 800,626,321 is 0.48 percent. Same channel, same format, nearly four times the rate on the lower view count, which is the kind of comparison the raw view number hides.

Subtitles

GET /api/v1/youtube/subtitles takes video_id on the same convention and returns the caption track, which is how you classify what a Short is about without watching it. Also 5 credits. The YouTube captions API and YouTube transcript scraper API pages cover that surface.

Credit cost

Measured from spb-cost response headers:

Call Credits
Channel Shorts tab via the HTML API, mode=auto 1
/youtube/metadata 5
/youtube/subtitles 5
/youtube/search, which will not help you here 5
Rejected request (wrong parameter name) 0

The YouTube endpoints are flat 5 with no light request discount and no proxy tier to pick, so budgeting is 5 * calls. The discovery step is on the HTML API instead, which is why it lands at 1.

Failed requests are retried inside the API for up to 30 seconds, so keep client timeouts above that. Plan tiers are on the pricing page.

Scope

Public channel Shorts tabs and public video metadata. Private and unlisted videos, members only content, YouTube Studio analytics and anything requiring a signed in session are out of reach, and scraping under login credentials is prohibited by ScrapingBee's terms of service.

Channel names, handles and uploader IDs belong to real people, so treat them as personal data where that applies. YouTube's Terms of Service govern use of the data, and Google publishes an official YouTube Data API which is the right tool when its quota and scope fit your case.

Adjacent endpoints: YouTube video scraper API, YouTube comment scraper API, YouTube playlist results API, YouTube music API, YouTube title scraper API, YouTube metatags, YouTube movie results API.

FAQ

Why does my Shorts search return no Shorts? Because the search endpoint returns only the long form video shelf. Measured across 60 results from three queries, the shortest was 4:38. Use a channel Shorts tab for discovery instead.

How do I tell a Short from a normal video? duration <= 180 from the metadata endpoint. It is an integer in seconds, so it is one comparison and no string parsing.

What replaced reelItemRenderer? shortsLockupViewModel. Parsers written against the old renderer name find nothing on a current Shorts tab.

Can I get Shorts by hashtag or topic? Not from these endpoints. Discovery is channel scoped. Build a channel list first, then sweep.

Why did one metadata call return an error object? Transient. Retry once. A response whose only key is error is not the same as a removed video.

License

MIT. See LICENSE.