# YouTube Shorts Scraper — Views, Music & Hashtags, No Login (`yugenox/youtube-shorts-scraper`) Actor

Scrape YouTube Shorts from channels, Shorts searches, #hashtags or URLs: views, likes, comment count, publish date, duration, channel and subscribers, hashtags, the song used and thumbnail. Newest, popular or oldest first, newer-than date. From $1.00 per 1,000 Shorts. No login, no API key.

- **URL**: https://apify.com/yugenox/youtube-shorts-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** Videos, Social media, Marketing
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 shorts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## YouTube Shorts Scraper — Channels, Search & URLs, No Login

**Get YouTube Shorts as clean data.** Give it channels, keywords or Shorts links and get one row per Short: views, likes, comment count, publish date, length, channel and subscribers, hashtags, the song it uses, and the thumbnail.

> 🚫 No login · 🔑 No API key · 🍪 No cookies · ⏱️ Newer-than date filter · 💵 From $1.00 per 1,000 Shorts

### 🎯 Three ways in

| You give | You get |
|---|---|
| **Channels** — `https://www.youtube.com/@NASA`, `@NASA`, a channel id (`UC…`) or a channel name | the channel's Shorts tab, **newest, most popular or oldest first** (the same three buttons YouTube shows). Links to other sites (TikTok, Instagram …) are skipped, never guessed |
| **Search Shorts** — keywords or `#hashtags` | YouTube's own Shorts search (the "Shorts" filter, so every result is a Short), **by relevance or popularity** |
| **Shorts URLs** — `youtube.com/shorts/…`, `youtu.be/…`, watch links or video ids | those Shorts (a regular video that isn't a Short is not saved — it is listed as `NOT_A_SHORT`) |

**Newer than** (`2026-09-01`, `7 days`, `today` …) keeps only Shorts published on or after that day. It works **to the day**, because YouTube shows a Short's publish date, not its time: an age counts back to the start of that calendar day (UTC), so `1 hour` keeps everything published today. Channels are then read newest first and stop at the date; searches use YouTube's upload-date filter plus the exact publish day. Shorts older than the date are never charged.

Many channels and searches are read side by side, so a long list of channels checked for new Shorts finishes in seconds.

### 🚀 Examples

The latest 50 Shorts of two channels:

```json
{ "channels": ["https://www.youtube.com/@NASA", "@MrBeast"], "maxResults": 50 }
```

A channel's most popular Shorts:

```json
{ "channels": ["@NASA"], "channelSortBy": "popular", "maxResults": 20 }
```

Everything a channel posted in the last week:

```json
{ "channels": ["@NASA"], "newerThan": "7 days", "maxResults": 200 }
```

The most popular Shorts for a keyword this month, as YouTube shows them in Canada:

```json
{ "searchQueries": ["cooking hacks", "#asmr"], "searchSortBy": "popular", "newerThan": "30 days", "country": "CA", "maxResults": 100 }
```

Specific Shorts:

```json
{ "shortsUrls": ["https://www.youtube.com/shorts/qolQY2JaKwI", "https://youtu.be/xD5Ajj3gFDs"] }
```

Already using another Shorts scraper? `channels`, `maxResultsShorts`, `oldestPostDate` and `sortChannelShortsBy` (`NEWEST` / `POPULAR` / `OLDEST`) work as they are; so do `startUrls`, `searchTerms` and `maxItems`.

### 📦 Output

A real row (2026-10-03):

```json
{
  "id": "qolQY2JaKwI",
  "url": "https://www.youtube.com/shorts/qolQY2JaKwI",
  "title": "Nika loves to eat chicken #cat #cats",
  "text": null,
  "hashtags": ["cat", "cats"],
  "viewCount": 418596019,
  "likes": 6167656,
  "commentsCount": 13000,
  "date": "2024-07-22",
  "duration": "0:17",
  "durationSeconds": 17,
  "channelName": "Princess Nika cat",
  "channelId": "UC0X1dp5Gkhos0QJpainsjUA",
  "channelUrl": "https://www.youtube.com/@PrincessNikacat",
  "channelUsername": "PrincessNikacat",
  "numberOfSubscribers": 12700000,
  "isChannelVerified": true,
  "channelAvatarUrl": "https://yt3.ggpht.com/ID3nIvzjZc…",
  "musicTitle": "Apprehensive at Best",
  "musicArtist": "Biz Baz Studio",
  "music": [{ "song": "Apprehensive at Best", "artist": "Biz Baz Studio", "album": "Apprehensive at Best" }],
  "engagementRate": 0.0148,
  "viewsPerDay": 520977,
  "thumbnailUrl": "https://i.ytimg.com/vi/qolQY2JaKwI/hqdefault.jpg",
  "input": "https://www.youtube.com/shorts/qolQY2JaKwI",
  "inputType": "url",
  "order": 1,
  "scrapedAt": "2026-10-03T23:35:57.240Z"
}
```

- `viewCount` is exact; `numberOfSubscribers` and large `commentsCount` values are rounded the way YouTube rounds them.
- `likes` is empty when the creator hides the like count; `commentsCount` is empty when comments are turned off.
- `duration` comes from YouTube's Shorts search results or the channel's Shorts playlist; in the rare case YouTube shows neither, it is empty.
- `musicTitle` / `musicArtist` / `music` are filled when YouTube lists the song in the Short's "Music" section. An "original sound" without a listed song leaves them empty.
- `engagementRate` = (likes + comments) ÷ views; `viewsPerDay` = views ÷ days since publishing.
- Each Short appears once per run, even if two of your inputs find it.
- Channels and searches are read at the same time, so rows of different inputs can be interleaved; `input` and `order` give each input's own order.

The dataset has three table views: Shorts, Engagement and Music. The run's key-value store also holds `RUN_REPORT` (how the run ended, Shorts per input) and, only when needed, `FAILED_INPUTS` (channels that don't exist, unavailable Shorts, Shorts URLs that are regular videos (`NOT_A_SHORT`), inputs YouTube blocked — none of them charged as Shorts).

### 💵 Pricing

Pay per event:

| Event | When | Price |
|---|---|---|
| Actor start | once per run (per GB of memory; the default 256 MB = 1) | $0.001 |
| Short | one Short saved to your dataset | $0.0013 ($1.30 per 1,000) on the Free and Bronze plans, $0.0012 ($1.20 per 1,000) on Silver, $0.001 ($1.00 per 1,000) on Gold, Platinum and Diamond |
| Channel, search or URL checked | each channel or search YouTube answered (with or without new Shorts), and each Shorts URL that turned out private, deleted or not a Short | $0.0003 |

Examples on the Free plan: the latest 20 Shorts of a channel = $0.001 + $0.0003 + 20 × $0.0013 = **$0.0273**; 1,000 Shorts from one channel = **$1.3013**; 20 Shorts URLs = $0.001 + 20 × $0.0013 = **$0.027**; an hourly check of 10 channels that finds nothing new = $0.001 + 10 × $0.0003 = **$0.004** per run. Shorts older than your date, regular videos and unavailable Shorts are never charged as Shorts; channels that don't exist, a channel given twice and inputs YouTube blocks cost nothing. Set a maximum cost per run and the actor stops before going over it — it never reads a Short or checks an input your limit can't pay for.

### ❓ FAQ

**Do I need a YouTube account, cookies or an API key?**
No. Everything comes from the pages YouTube shows any visitor without logging in. The actor never logs in, never uses cookies, and never touches private or members-only content.

**Is it legal to scrape YouTube?**
This actor only collects publicly available data that anyone can see on YouTube without signing in. Collecting publicly available data is generally legal, but you're responsible for how you use it. Results include channel names, handles and links, which can count as personal data, so follow privacy laws such as GDPR, PIPEDA and CCPA, and YouTube's terms. If you're unsure, check with a lawyer. More on this: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it download videos, captions or transcripts?**
No. It reads the Shorts' public details only: no video files, no captions, no transcripts, and no comment text.

**Why is a search different from what I see in the app?**
YouTube personalizes and reshuffles search results; this actor sees what a logged-out visitor in your **Country** sees. Channel Shorts tabs are the same for everyone.

**Can I run it every hour to catch new Shorts?**
Yes — schedule it with **Newer than** set to `today` (or `1 day`). The filter works to the day, so each run returns that day's Shorts; use the `id` column to keep only the ones you haven't seen yet. A check that finds nothing new costs $0.0003 per channel.

**What does "Newer than" do with a popular or oldest order?**
A date needs the newest-first order to stop at the right place, so with a date set, channels are read newest first (the run's status message says so).

**What happens if YouTube blocks a request?**
Each request is retried on fresh IPs — Apify datacenter IPs first, then the proxy in your input (residential by default). An input that stays blocked is listed in `FAILED_INPUTS`, not charged, and the run continues. If the very first input is blocked, or two in a row later on, YouTube is blocking the whole run: it stops there and says so instead of spending your money on retries. Runs always finish with the Shorts collected so far and a plain status message.

**What if my run's timeout is short?**
The actor stops reading a little before your timeout and keeps every Short it already saved, so even a 30-second run returns rows.

**Is this affiliated with YouTube?**
No. Not affiliated with, endorsed by or sponsored by YouTube or Google. YouTube and YouTube Shorts are trademarks of Google LLC.

# Actor input Schema

## `channels` (type: `array`):

Channels whose Shorts tab to read: a YouTube channel link (https://www.youtube.com/@NASA, …/@NASA/shorts, youtube.com/c/… or a legacy youtube.com/name link), an @handle, a channel id (UC…) or a channel name. Links to other sites (TikTok, Instagram, websites) are skipped with a note. One per line.

## `searchQueries` (type: `array`):

Keywords or #hashtags to search YouTube Shorts for (YouTube's own "Shorts" search filter, so every result is a Short). One per line.

## `shortsUrls` (type: `array`):

Individual Shorts: youtube.com/shorts/… links, youtu.be links, watch links or 11-character video ids. A regular (non-Short) video is not saved: it is listed in FAILED_INPUTS as NOT_A_SHORT. One per line.

## `maxResults` (type: `integer`):

How many Shorts to read from each channel and each search (Shorts URLs are always read in full). You pay per Short saved, plus a small check per channel or search.

## `newerThan` (type: `string`):

Only Shorts published on or after this day — a date (2026-09-01) or an age ("7 days", "2 weeks", "today"). The filter works to the day (YouTube shows a Short's publish date, not its time): an age counts back to the start of that calendar day (UTC), so "1 hour" keeps everything published today. Channels are then read newest first and stop at the date; searches use YouTube's upload-date filter and the exact publish day. Older Shorts are never charged.

## `channelSortBy` (type: `string`):

The three buttons on a channel's Shorts tab: newest first, most popular first, or oldest first.

## `searchSortBy` (type: `string`):

Order of Shorts search results: most relevant first (YouTube's default) or most popular first. YouTube offers no upload-date order for Shorts — use "Newer than" instead.

## `country` (type: `string`):

Two-letter country code (US, GB, IN, BR, DE …). Searches run as YouTube does for people in that country.

## `proxyConfiguration` (type: `object`):

YouTube is read through Apify datacenter IPs first (cheapest); this proxy (residential by default) is used only for the kinds of request YouTube blocks on datacenter IPs. An account without residential proxies falls back to Apify datacenter IPs automatically. "Own proxies" are used for every request.

## `maxConcurrency` (type: `integer`):

How many Shorts of one channel, search or URL list are read at once (1–20). Channels and searches are also read side by side (up to 8 at a time).

## Actor input object example

```json
{
  "channels": [
    "https://www.youtube.com/@NASA"
  ],
  "maxResults": 5,
  "channelSortBy": "newest",
  "searchSortBy": "relevance",
  "country": "US",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 8
}
```

# Actor output Schema

## `results` (type: `string`):

All Shorts read in this run.

## `run` (type: `string`):

Status and statistics for this run.

## `report` (type: `string`):

RUN_REPORT: how the run ended, Shorts per input, where lengths came from, requests and proxy use.

## `failedInputs` (type: `string`):

FAILED_INPUTS: channels not found, unavailable Shorts and inputs YouTube blocked (none of them charged). Absent when there were none.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "channels": [
        "https://www.youtube.com/@NASA"
    ],
    "maxResults": 5,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/youtube-shorts-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "channels": ["https://www.youtube.com/@NASA"],
    "maxResults": 5,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/youtube-shorts-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "channels": [
    "https://www.youtube.com/@NASA"
  ],
  "maxResults": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call yugenox/youtube-shorts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yugenox/youtube-shorts-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/llezIThi0DHu2mNbK/builds/9HmSdtBqYygyYzbQC/openapi.json
