# Youtube Shorts Engagement Scraper (`andy-ng/youtube-shorts-engagement-scraper`) Actor

Extract public engagement metrics from YouTube Shorts and regular YouTube video URLs — views, likes, comments, engagement count, and engagement rate — without requiring a YouTube API key or browser automation.

- **URL**: https://apify.com/andy-ng/youtube-shorts-engagement-scraper.md
- **Developed by:** [Andy Nguyen](https://apify.com/andy-ng) (community)
- **Categories:**
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## YouTube Shorts Engagement Scraper

Extract **public engagement metrics** from **YouTube Shorts** and regular YouTube video URLs — views, likes, comments, and engagement count. The Actor scrapes public YouTube pages first and uses the official **YouTube Data API v3 internally** as a fallback when scraping is blocked or incomplete. No API key is required from users.

This Apify Actor accepts one or more YouTube URLs, fetches the public page, parses embedded JSON data, and returns normalized results to the default dataset. You can schedule runs, call the Actor via API, export data as JSON/CSV/Excel, and integrate with other Apify tools.

> **Disclaimer:** This is an unofficial Actor and is **not affiliated with, sponsored by, or endorsed by YouTube or Google**.

### What does this Actor do?

Given YouTube Shorts or YouTube video URLs, the Actor returns:

- **Views** (`viewCount`)
- **Likes** (`likeCount`)
- **Comments** (`commentCount`)
- **Engagement count** (`engagementCount` = likes + comments)

It also returns useful metadata such as title, channel info, publish date, duration, and thumbnail URL.

### Why use this Actor?

- **No user API key required** — scraping works out of the box
- **Internal YouTube Data API fallback** — improves results when page scraping is blocked or partial
- **Batch processing** — up to 1,000 URLs per run
- **Normalized output** — consistent schema for analytics and reporting
- **Apify platform benefits** — scheduling, proxy support, dataset exports, API access, monitoring

### Supported URL formats

The Actor accepts these YouTube URL formats (query parameters such as `?si=...`, `?feature=shared`, and `?utm_source=...` are ignored):

```text
https://www.youtube.com/shorts/VIDEO_ID
https://youtube.com/shorts/VIDEO_ID
https://www.youtube.com/watch?v=VIDEO_ID
https://youtu.be/VIDEO_ID
```

Every successful result uses the canonical output URL:

```text
https://www.youtube.com/shorts/VIDEO_ID
```

Invalid URLs are logged and skipped without stopping the run. Duplicate video IDs are deduplicated automatically.

### How to use this Actor

1. Open the Actor in [Apify Console](https://console.apify.com/) or run it locally.
2. Add one or more YouTube Shorts or YouTube video URLs to **Start URLs**.
3. Optionally adjust **Max Concurrency** (default: 10) and **Proxy Configuration**.
4. Start the run.
5. Download results from the **Output** tab or via the Dataset API.

#### Input example

```json
{
    "startUrls": [
        { "url": "https://www.youtube.com/shorts/aqz-KE-bpKQ" },
        { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }
    ],
    "maxConcurrency": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
}
```

### Internal YouTube Data API fallback

The Actor scrapes the public YouTube page first. When scraping fails or returns incomplete engagement metrics, it automatically falls back to the official [YouTube Data API v3](https://developers.google.com/youtube/v3) using an **internal API key** configured by the Actor owner.

Fallback is used when:

- the page fetch fails
- a consent or bot-check page is returned
- embedded JSON is missing or incomplete
- one or more primary engagement fields (`viewCount`, `likeCount`, `commentCount`) are unavailable from HTML scraping

The fallback only fills missing fields. It does not overwrite values already extracted from the page.

**Actor owners** configure the internal key before deployment:

```bash
apify secrets add youtubeApiKey YOUR_YOUTUBE_DATA_API_KEY
apify push
```

For local development, set `YOUTUBE_API_KEY` in the environment or add the same Apify secret locally with `apify secrets add`.

Each fallback lookup uses one `videos.list` quota unit. Hidden likes may still return `null` even through the official API.

### Output example

```json
{
    "videoId": "aqz-KE-bpKQ",
    "url": "https://www.youtube.com/shorts/aqz-KE-bpKQ",
    "originalUrl": "https://www.youtube.com/shorts/aqz-KE-bpKQ",
    "title": "Sample YouTube Short #viral #AI",
    "description": "Video description... Check out #shorts and #tech",
    "hashtags": ["viral", "ai", "shorts", "tech"],
    "channel": {
        "id": "UCxxxxxxxxxxxxxxxx",
        "name": "Channel Name",
        "url": "https://www.youtube.com/channel/UCxxxxxxxxxxxxxxxx"
    },
    "publishedAt": "2024-01-15T10:30:00.000Z",
    "durationSeconds": 45,
    "isShort": true,
    "viewCount": 1234567,
    "likeCount": 42000,
    "commentCount": 1500,
    "engagementCount": 43500,
    "thumbnailUrl": "https://i.ytimg.com/vi/.../maxresdefault.jpg",
    "status": "success",
    "missingFields": [],
    "collectedAt": "2026-08-28T00:00:00.000Z"
}
```

You can download the dataset in various formats such as **JSON, CSV, Excel, XML, RSS**, or fetch items through the **Dataset API**.

### Engagement formula

```text
engagementCount = likeCount + commentCount   (only when both are available)
```

Missing metrics are returned as `null`, **not** zero. A hidden like count is not treated as zero likes.

### Data table

| Field             | Description                                         |
| ----------------- | --------------------------------------------------- |
| `title`           | Video title                                         |
| `description`     | Video description                                   |
| `hashtags`        | Extracted hashtags (lowercase, deduplicated)        |
| `channel.name`    | Channel display name                                |
| `channel.id`      | YouTube channel ID                                  |
| `channel.url`     | Channel profile URL                                 |
| `viewCount`       | Public view count                                   |
| `likeCount`       | Public like count (null if hidden)                  |
| `commentCount`    | Public comment count (null if unavailable/disabled) |
| `engagementCount` | Likes + comments                                    |
| `publishedAt`     | Publish date (ISO 8601 UTC)                         |
| `status`          | `success` or `partial`                              |
| `url`             | Canonical YouTube Shorts URL                        |
| `collectedAt`     | Collection timestamp (ISO 8601 UTC)                 |

#### Available metrics

- Views
- Likes (when publicly shown)
- Comments (when publicly shown)
- Derived engagement count

#### Unavailable metrics

- Share count
- Save count
- Individual comment text
- Private analytics not shown on the public page

### How to run locally

```bash
npm install
apify run
```

To clear previous local storage between runs:

```bash
apify run --purge
```

Local runs store output in the `storage/` directory. Local storage is **not** synced to Apify Cloud automatically.

### How to deploy

```bash
apify login
apify push
```

### How to call through the Apify API

Replace `YOUR_API_TOKEN` and `YOUR_ACTOR_ID` with your values:

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [{ "url": "https://www.youtube.com/shorts/aqz-KE-bpKQ" }],
    "maxConcurrency": 10
  }'
```

Fetch dataset items from the run’s default dataset URL returned by the API.

### Pricing / cost estimation

Cost depends on Apify compute units, run duration, concurrency, and proxy usage. Processing more URLs or using proxies increases cost. Start with a small URL batch to estimate usage for your workload.

### Tips and advanced options

- Use **lower concurrency** (1–5) if you see HTTP 429 rate limiting.
- Enable **Apify Proxy** if you encounter HTTP 403 responses or regional blocks.
- Prefer **YouTube Shorts URLs** when you know the content is a Short (`isShort` will be `true`).
- Check `status` and `missingFields` for partial results instead of assuming all metrics exist.

### Limitations

- Uses **CheerioCrawler** (HTTP + HTML parsing), not a full browser. YouTube is JavaScript-heavy; metrics are extracted from embedded JSON (`ytInitialPlayerResponse`, `ytInitialData`) when present.
- **Hidden likes**, **disabled comments**, **private**, **deleted**, **age-restricted**, and **consent** pages may prevent full metrics.
- YouTube may change page structure without notice; the parser is defensive but not guaranteed against all future changes.
- One failed URL does **not** stop the rest of the run.

If live HTML no longer includes sufficient embedded JSON for your use case, the smallest next step is switching to **PlaywrightCrawler** for JavaScript-rendered content — that migration is **not** included in this Actor.

### Responsible use

- Respect [YouTube Terms of Service](https://www.youtube.com/t/terms) and applicable laws.
- Scrape only public data you are permitted to collect.
- Avoid excessive request rates; use reasonable concurrency.
- Do not use this Actor to collect personal data without a lawful basis.

### Troubleshooting

| Issue                       | What to try                                                                  |
| --------------------------- | ---------------------------------------------------------------------------- |
| HTTP 403 / 429              | Enable Apify Proxy, reduce `maxConcurrency`, retry later                     |
| Consent page / missing JSON | Enable Apify Proxy and retry; internal API fallback may recover metrics      |
| `status: "partial"`         | Check `missingFields`; internal API fallback may fill missing public metrics |
| Invalid URL skipped         | Confirm URL format and 11-character video ID                                 |
| No results locally          | Check `storage/datasets/default/*.json` after `apify run`                    |

### FAQ and support

- **Is this official?** No. This Actor is unofficial and not affiliated with YouTube or Google.
- **Do I need an API key?** No. Users do not provide a YouTube API key. The Actor owner configures the internal fallback key.
- **Does it download videos?** No. It only reads public page HTML/metadata.
- Report issues or request features via the Apify Console **Issues** tab.

### Project structure

```text
.actor/                 # Actor configuration and schemas
src/
  main.ts               # Orchestration entry point
  config.ts             # Internal environment configuration
  types.ts              # Input/output types
  youtube-url.ts        # URL validation and normalization
  youtube-parser.ts       # Embedded JSON extraction
  youtube-api.ts          # YouTube Data API fallback
  metrics.ts              # Engagement calculations
  handler.ts            # Cheerio request handlers
test/                   # Vitest unit tests with fixtures
```

### Development

```bash
npm install
npm run build
npm test
npm run lint
npm run format:check
apify validate-schema
```

# Actor input Schema

## `startUrls` (type: `array`):

One or more YouTube Shorts or regular YouTube video URLs to extract public engagement metrics from. Supported formats: https://www.youtube.com/shorts/VIDEO\_ID, https://www.youtube.com/watch?v=VIDEO\_ID, https://youtu.be/VIDEO\_ID. Up to 1,000 URLs per run.

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

Maximum number of YouTube pages to fetch in parallel. Higher values are faster but may increase the risk of rate limiting.

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

Proxy settings for anti-bot protection. Enable Apify Proxy if you encounter HTTP 403 or 429 responses.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.youtube.com/shorts/aqz-KE-bpKQ"
    },
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "startUrls": [
        {
            "url": "https://www.youtube.com/shorts/aqz-KE-bpKQ"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("andy-ng/youtube-shorts-engagement-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 = { "startUrls": [{ "url": "https://www.youtube.com/shorts/aqz-KE-bpKQ" }] }

# Run the Actor and wait for it to finish
run = client.actor("andy-ng/youtube-shorts-engagement-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 '{
  "startUrls": [
    {
      "url": "https://www.youtube.com/shorts/aqz-KE-bpKQ"
    }
  ]
}' |
apify call andy-ng/youtube-shorts-engagement-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,andy-ng/youtube-shorts-engagement-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/xRJ2IrZROPrD3pBTH/builds/4F11pdVUSUn2B7SLe/openapi.json
