# YouTube Shorts Scraper (channel Shorts, views, no login) (`datahamster/youtube-shorts`) Actor

YouTube Shorts scraper with no API key and no login: every Short on a channel's public Shorts tab as one row with title, view count, thumbnail and channel. Optional exact views, likes and publish date per Short. Monitor mode alerts when a channel posts a new Short.

- **URL**: https://apify.com/datahamster/youtube-shorts.md
- **Developed by:** [Viktor Dubnytskiy](https://apify.com/datahamster) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 result items

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 (channel Shorts, views, no login)

Get the latest Shorts of any YouTube channel as one flat row per Short: video id, Shorts URL, title, view count,
vertical thumbnail and the channel behind it. No API key, no OAuth, no cookies — the actor reads only what YouTube
serves to a logged-out visitor on the channel's public **Shorts** tab.

Run it once for a list, or run it on a schedule in **monitor** mode and get a webhook or Telegram alert the moment a
channel posts a new Short.

### What you get

One row per Short: `id` (video id), `url` (`https://www.youtube.com/shorts/<id>`), `videoId`, `title`, `viewCountText`,
`viewCount`, `likeCount`, `publishedAt`, `durationSeconds`, `thumbnailUrl`, `channelId`, `channelHandle`,
`channelName`, `channelSubscribers`, `detailsFetched`, `rank`, `scrapedAt`.

Rows come back in tab order — newest Short first, `rank: 1`.

### Example output

| Field | Example value |
|---|---|
| `id` / `url` | `wSh8fAFWEWs` / `https://www.youtube.com/shorts/wSh8fAFWEWs` |
| `title` | `Survivor vs Beast Games` |
| `viewCountText` / `viewCount` | `39M views` / `39916641` (exact, with `includeDetails`) |
| `likeCount` / `publishedAt` | `921474` / `2026-01-16T11:00:01-08:00` (with `includeDetails`) |
| `durationSeconds` | `27` |
| `channelHandle` / `channelId` | `@MrBeast` / `UCX6OQ3DkcsbYNE6H8uQQuVA` |
| `thumbnailUrl` | `https://i.ytimg.com/vi/wSh8fAFWEWs/oardefault.jpg?...` |

### Use cases

- Track what a set of creators is posting in Shorts and how each Short performs, without opening YouTube.
- Build a content-research list for one niche: 30-48 recent Shorts per channel with titles and view counts.
- Watch competitors on a schedule and get alerted on every new Short (monitor mode keys on the video id).

### How it works

1. Each channel's public Shorts tab (`https://www.youtube.com/@handle/shorts`) is opened as a logged-out visitor. The
   page embeds the whole grid, so one request returns every Short the tab renders — typically 30 to 48, newest first.
2. With `includeDetails` on, each Short's own page (`https://www.youtube.com/shorts/<id>`) is opened for the exact
   view count, like count, publish date and duration (one extra request per Short).
3. A bot-check or consent wall is retried on a stronger proxy tier before the run gives up, and a walled run is
   reported as **blocked** — an empty dataset never hides a wall.

### Input

| Field | Meaning | Default |
|---|---|---|
| `channels` | Channels to read, one per line: `@MrBeast`, a channel URL, or a `UC...` id | `["@MrBeast"]` |
| `maxShortsPerChannel` | Stop after this many Shorts per channel (1-50) | `48` |
| `includeDetails` | Open each Short for exact views, likes, publish date, duration | `false` |
| `tier` | Proxy tier: `auto` climbs none → datacenter → residential on a wall | `auto` |
| `proxyCountry` | Exit country for the proxy | `US` |
| `maxItems` | Stop after this many rows in total | `100` |
| `mode` | `scrape` or `monitor` (only new/changed since the last run) | `scrape` |
| `monitorStateId`, `webhookUrl`, `telegramBotToken`, `telegramChatId` | Monitor state key and alert targets | empty |

### Pricing

| Event | Price |
|---|---|
| result | $0.001 per Short ($1 per 1,000) |
| monitor-check | $0.005 per monitor run |
| change | $0.001 per new or changed Short |

You are charged only for rows actually pushed. A run that finds nothing pushes nothing and charges no result events.
Proxy traffic is billed by Apify on top, and only when a wall forces the run above the free `none` tier.

**Monitor mode and `includeDetails`.** A change event is billed per row whose view count, like count or title moved
since the last run. With the default (`includeDetails: false`) the view count is YouTube's own rounded figure
(`39M views`), which stands still for hours or days — so a monitor run normally bills only genuinely new Shorts.
With `includeDetails: true` the exact `viewCount` is read instead, and on an active channel that number is different
on **every** run: expect a change event for essentially every Short you watch, on every check, plus one extra page
request per Short. Watch for new Shorts with the default, and turn details on only when you want the exact curve and
have budgeted for it.

### Limits

- **One page per channel.** YouTube's `robots.txt` disallows `/youtubei/`, the endpoint that loads "more Shorts", so
  this actor takes only what the first page of the Shorts tab renders: 30-48 Shorts, newest first. It is not an
  archive tool for a channel's entire Shorts history.
- **No Shorts search.** `robots.txt` also disallows `/results`, so there is no keyword search here — the input is a
  list of channels.
- Without `includeDetails` the view count is the rounded number YouTube prints on the tab (`39M views` → `39000000`);
  `likeCount`, `publishedAt` and `durationSeconds` are empty. Turn `includeDetails` on for exact numbers.
- A channel with no Shorts, or a handle that does not exist, returns no rows and costs no result events.
- Counts are what YouTube shows a logged-out visitor and can lag the creator's own analytics.

### What is NOT collected

No comments, no commenter names, no subscriber lists, no viewer data, no personal profiles — nothing about private
individuals. Rows describe a public video and the channel that published it, which is a creator/business entity.

### FAQ

**Does it need a YouTube account, API key or cookies?** No. There is no credential field at all; every request is made
as a logged-out visitor.

**Why do I get 48 Shorts and not 200?** Because loading Shorts 49+ requires an endpoint YouTube's `robots.txt`
disallows. We do not build against an explicit `Disallow`; see the Limits section.

**What happens when a run comes back empty?** No rows are pushed and no result events are charged. The `RUN_SUMMARY`
record in the run's key-value store carries `emptyReason`, which separates "this channel has no Shorts" from "YouTube
answered with a bot check" — the second is reported as `blocked`, never as `no_matches`.

### Changelog

- 0.1: initial release — channel Shorts tab, optional exact per-Short stats, proxy-tier ladder on the bot wall,
  monitor mode for new Shorts.

***

If this actor saved you time, a short review on its Store page genuinely helps other people find it. Found a bug or
need a field that is missing? Open a ticket on the **Issues** tab.

# Actor input Schema

## `maxItems` (type: `integer`):

Stop after this many results (you are charged only for pushed items)

## `mode` (type: `string`):

scrape = full results; monitor = only new/changed items since the previous run of this task

## `monitorStateId` (type: `string`):

Optional state id when not running as a saved task (monitor mode)

## `webhookUrl` (type: `string`):

POST a change summary here in monitor mode

## `telegramBotToken` (type: `string`):

Optional: bot token for monitor-mode change summaries

## `telegramChatId` (type: `string`):

Optional: chat id that receives monitor-mode summaries

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

YouTube channels to read Shorts from, one per line: a handle "@MrBeast", a channel URL "https://www.youtube.com/@MrBeast" or a channel id "UCX6OQ3DkcsbYNE6H8uQQuVA".

## `maxShortsPerChannel` (type: `integer`):

How many Shorts to take per channel, newest first, e.g. 48. One page of the Shorts tab renders 30-48 Shorts and there is no page two (YouTube's robots.txt disallows the "load more" endpoint), so values above 48 change nothing. Range 1-50.

## `includeDetails` (type: `boolean`):

true = open every Short's own page for the exact view count, like count, publish date and duration (one extra request per Short). false = keep the rounded count the tab shows ("39M views") and leave likes and publish date empty. Example: false.

## `tier` (type: `string`):

auto starts without a proxy and climbs to datacenter, then residential, only when YouTube answers with a bot check — the cheapest option. Pin a tier to force it.

## `proxyCountry` (type: `string`):

Two-letter country of the proxy exit, e.g. "US" or "DE". Affects which Shorts YouTube serves.

## Actor input object example

```json
{
  "maxItems": 100,
  "mode": "scrape",
  "channels": [
    "@MrBeast"
  ],
  "maxShortsPerChannel": 48,
  "includeDetails": false,
  "tier": "auto",
  "proxyCountry": "US"
}
```

# Actor output Schema

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

All pushed rows (dataset, JSON)

## `resultsTable` (type: `string`):

Dataset in the Console viewer

## `runSummary` (type: `string`):

RUN\_SUMMARY record

# 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": [
        "@MrBeast"
    ],
    "maxShortsPerChannel": 48
};

// Run the Actor and wait for it to finish
const run = await client.actor("datahamster/youtube-shorts").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": ["@MrBeast"],
    "maxShortsPerChannel": 48,
}

# Run the Actor and wait for it to finish
run = client.actor("datahamster/youtube-shorts").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": [
    "@MrBeast"
  ],
  "maxShortsPerChannel": 48
}' |
apify call datahamster/youtube-shorts --silent --output-dataset

```

## MCP server setup

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

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/dPZwDtm3lquqpkkVQ/builds/Qdg5EyWogu5VMaKS7/openapi.json
