# YouTube Fast Scraper - Videos, Channels & Comments (`viralanalyzer/youtube-fast-scraper`) Actor

Fast YouTube data extraction: videos, channels, comments, playlists, and analytics data.

- **URL**: https://apify.com/viralanalyzer/youtube-fast-scraper.md
- **Developed by:** [viralanalyzer](https://apify.com/viralanalyzer) (community)
- **Categories:** Social media, Videos
- **Stats:** 23 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.00 / 1,000 video scrapeds

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

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Fast Scraper — Video Metrics & Engagement (No API Key Required)

> 🔗 [View on Apify Store](https://apify.com/viralanalyzer/youtube-fast-scraper) | 🇺🇸 English | [🇧🇷 Português](#português)

Extract complete **YouTube video data** from channels, playlists, and individual videos: views, likes, comments, duration, hashtags, thumbnails, Shorts detection, and channel info. Powered by yt-dlp with multi-proxy failover. No API key required — optional YouTube Data API v3 enrichment for accurate stats.

### ✨ Features

- 📺 **Channels, Playlists & Videos** — Scrape any YouTube URL type (channel, playlist, or single video)
- 📊 **Full video metrics** — Views, likes, comments count extracted from each video
- ⏱️ **Duration & thumbnails** — ISO 8601 duration format and high-resolution thumbnail URLs
- 🏷️ **Hashtag extraction** — From video tags and description text, deduplicated
- 📱 **Shorts detection** — Automatic identification of YouTube Shorts based on duration, URL, and aspect ratio
- 📝 **Subtitle download** — Optional auto-generated captions in English and Portuguese
- 🔑 **Optional API enrichment** — Provide a YouTube Data API v3 key for accurate likes, comments, and subscriber counts
- 🔄 **Multi-proxy failover** — Residential, Datacenter, Direct strategies tried automatically
- ✅ **Contract validation** — Every output item validated against a strict data contract before push
- 💰 **Pay per result** — $0.004/video

### ✅ Capabilities & Limits

What this actor **can** and **cannot** do, based on its real input/output schema. (Use this to avoid surprises — see the FAQ for workarounds.)

| Discover / accept input by | Supported? |
|---|---|
| 🔗 Channel / Playlist / Video **URL** (incl. `@handle` channel URL) | ✅ Yes — via `startUrls` |
| 👤 Channel **handle / username** | ✅ Yes — as a channel URL (e.g. `https://www.youtube.com/@MrBeast`) |
| 🔍 **Keyword / search query** (find videos by topic) | ✅ Yes — via `searchQuery` (+ `sortBy`: relevance/date/views) |
| 🏷️ **Hashtag** as a seed (e.g. discover videos for `#shorts`) | ✅ Partial — pass the hashtag text as a `searchQuery` (e.g. `"#shorts"`); hashtags are also **extracted from output** |

> ℹ️ **`startUrls` is no longer strictly required.** Provide **at least one** of `startUrls`, `seeds`, or `searchQuery`. (The `startUrls` prefill is kept so the Apify Automated Quality Test still passes.)

**Output includes:** views ✅ · likes ✅ · comments **count** ✅ · comment **text** ✅ *(optional — set `includeComments: true`; off by default)* · uploader/channel ✅ · upload date (`uploadDate`, `YYYY-MM-DD`) ✅ · duration ✅ · thumbnail ✅ · hashtags (extracted from tags + description) ✅ · Shorts flag ✅ · subscriber & channel video count ✅ *(only with optional API key — null otherwise)*

### 📥 Input

> **One of `startUrls`, `seeds`, or `searchQuery` is required** (not all three). If all are empty, the run fails fast with a clear error.

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `startUrls` | object\[] | Conditional¹ | — | YouTube URLs (channel, playlist, or video) to scrape |
| `searchQuery` | string | Conditional¹ | — | Keyword search: find videos by topic instead of by URL. When set, it **replaces** `startUrls`/`seeds`. |
| `sortBy` | string (enum) | No | `relevance` | Order for `searchQuery` results: `relevance`, `date` (newest first), or `views` (highest first). Ignored unless `searchQuery` is set. |
| `includeComments` | boolean | No | false | If true, each video gets a `comments[]` array of `{ author, text, likes }`. **Slow — off by default.** |
| `commentsLimit` | integer | No | 20 | Max top-level comments per video when `includeComments` is true (capped at 100). |
| `maxItems` | integer | No | 50 | Maximum number of videos to scrape (also = number of search results when `searchQuery` is set) |
| `maxResults` | integer | No | — | Optional alias of `maxItems`. If set, overrides `maxItems`. |
| `seeds` | string\[] | Conditional¹ | — | Optional alias of `startUrls` (plain YouTube URLs). Used when non-empty and `seedType` is `url`. |
| `seedType` | string (enum) | No | `url` | Type of `seeds` values. Only `url` is supported. |
| `normalizeOutput` | boolean | No | false | Add an additive `_normalized` block + `_dataQuality` flag per item. |
| `downloadSubtitles` | boolean | No | false | Extract auto-generated captions (EN, PT) |
| `proxyConfiguration` | object | No | `{ "useApifyProxy": true }` | Apify proxy settings. YouTube blocks datacenter IPs aggressively; residential is recommended as fallback |
| `youtubeApiKey` | string | No | — | YouTube Data API v3 key for enriched stats (likes, comments, subscribers). *Read by the actor at runtime; pass it via JSON input.* |

¹ *Conditional: provide at least one of `startUrls`, `seeds`, or `searchQuery`.*

#### Input Example

```json
{
  "startUrls": [
    { "url": "https://www.youtube.com/@MrBeast" },
    { "url": "https://www.youtube.com/playlist?list=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf" },
    { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }
  ],
  "maxItems": 20,
  "downloadSubtitles": false
}
```

**Keyword search + comments example:**

```json
{
  "searchQuery": "mr beast challenge",
  "sortBy": "views",
  "maxResults": 10,
  "includeComments": true,
  "commentsLimit": 10
}
```

### 📤 Output

Every video includes these fields:

| Field | Type | Description |
|---|---|---|
| `id` | string | YouTube video ID |
| `url` | string | Full video URL |
| `title` | string | Video title |
| `description` | string | Video description text |
| `viewCount` | integer | Number of views |
| `likes` | integer | Number of likes |
| `commentsCount` | integer | Number of comments |
| `duration` | string | ISO 8601 duration (e.g. `PT3M12S`) |
| `durationSecs` | integer | Duration in seconds |
| `uploadDate` | string | Upload date (YYYY-MM-DD) |
| `thumbnail` | string | High-resolution thumbnail URL |
| `hashtags` | string\[] | Tags and hashtags from description |
| `uploader` | string | Channel/uploader name |
| `uploaderUrl` | string | Channel URL |
| `uploaderFollowers` | integer | Channel subscriber count (via API enrichment) |
| `channelId` | string | YouTube channel ID |
| `channelVideoCount` | integer | Total videos on channel (via API enrichment) |
| `isShort` | boolean | Whether the video is a YouTube Short |
| `comments` | object\[] | Top-level comments `{ author, text, likes }`, truncated to `commentsLimit`. *Present only when `includeComments` is true.* |

#### Output Example

```json
{
  "id": "fC7oUOUEEi4",
  "url": "https://www.youtube.com/watch?v=fC7oUOUEEi4",
  "title": "$456,000 Squid Game In Real Life!",
  "description": "I recreated every single game from Squid Game in real life...",
  "viewCount": 592000000,
  "likes": 18200000,
  "commentsCount": 1400000,
  "duration": "PT25M44S",
  "durationSecs": 1544,
  "uploadDate": "2021-11-24",
  "thumbnail": "https://i.ytimg.com/vi/fC7oUOUEEi4/maxresdefault.jpg",
  "hashtags": ["squidgame", "mrbeast", "challenge"],
  "uploader": "MrBeast",
  "uploaderUrl": "https://www.youtube.com/@MrBeast",
  "uploaderFollowers": 345000000,
  "channelId": "UCX6OQ3DkcsbYNE6H8uQQuVA",
  "channelVideoCount": 820,
  "isShort": false
}
```

### 📋 Use Cases

- 🎯 **Content Strategy** — Analyze top-performing videos to inform your content plan
- 🔍 **Competitor Research** — Track competitor channels, uploads, and engagement rates
- 📈 **Trend Analysis** — Identify trending topics and formats across channels
- 👥 **Influencer Vetting** — Evaluate creator metrics (subscribers, avg views, engagement)
- 🏷️ **SEO & Keywords** — Extract hashtags and tags used by successful videos
- 📱 **Shorts vs Long-Form** — Compare performance between Shorts and regular videos

### ❓ FAQ

**Q: Do I need a YouTube Data API key?**
A: No. The actor works without an API key using yt-dlp data extraction. However, providing a YouTube Data API v3 key enables more accurate likes, comments, and subscriber counts via batch API calls.

**Q: Does it work with private or age-restricted videos?**
A: No. The actor can only extract data from publicly accessible videos. Private, unlisted, or age-restricted videos will be skipped.

**Q: How does Shorts detection work?**
A: Videos are classified as Shorts based on multiple signals: duration under 61 seconds, `/shorts/` in the URL, `#shorts` in the title, or vertical aspect ratio (height > width).

**Q: Why do some videos have null likes or comments?**
A: Some creators disable like counts or comments on their videos. The actor reports these as null when the data is not available from YouTube.

**Q: Can I scrape entire channels?**
A: Yes. Provide the channel URL (e.g. `https://www.youtube.com/@MrBeast`) and the actor will extract videos up to the `maxItems` limit.

### 💰 Pricing

This actor uses **Pay Per Event (PPE)** pricing:

| Metric | Cost |
|--------|------|
| `video-scraped` | $0.004 per video |

**Example**: Scraping 1,000 videos costs $4.00.

### 🔗 Related Actors

- [TikTok Viral Scanner](https://apify.com/viralanalyzer/tiktok-viral-scanner) — TikTok profile & video data
- [Instagram Reels Scraper](https://apify.com/viralanalyzer/instagram-reels-scraper) — Instagram metrics
- [SEO Intelligence Suite](https://apify.com/viralanalyzer/seo-intelligence-suite) — Full website SEO audit
- [Website Change Monitor](https://apify.com/viralanalyzer/website-change-monitor) — Track page changes

### 🆕 Available — Optional Unified / Normalized Mode

> ✅ The items below are **live now** and fully backward-compatible. If you leave the new fields unset (and `normalizeOutput` as `false`, the default), the output is **byte-for-byte identical** to previous versions — existing integrations are unaffected.

**New optional input fields** (all optional; existing inputs keep working):

| Parameter | Type | Default | Description |
|---|---|---|---|
| `maxResults` | integer | — | Alias of `maxItems` (unified ViralAnalyzer vocabulary). If set, it overrides `maxItems`. |
| `seeds` | string\[] | — | Alias of `startUrls` — a plain list of YouTube URLs. When non-empty **and** `seedType` is `url`, they are mapped into `startUrls`. |
| `seedType` | string (enum) | `url` | Type of values in `seeds`. This actor discovers videos **only from URLs**, so `url` is the only supported value. |
| `normalizeOutput` | boolean | `false` | When `true`, each item gets an additive `_normalized` block + a `_dataQuality` flag. When `false`, output is unchanged. |

**`_normalized` block** (added per item when `normalizeOutput: true`):

```json
"_normalized": {
  "platform": "youtube",
  "url": "https://www.youtube.com/watch?v=fC7oUOUEEi4",
  "author": "MrBeast",
  "text": "$456,000 Squid Game In Real Life!",
  "views": 592000000,
  "likes": 18200000,
  "comments": 1400000,
  "shares": null,
  "publishedAtISO": "2021-11-24T00:00:00+00:00",
  "lang": null,
  "hashtags": ["squidgame", "mrbeast", "challenge"],
  "engagementVelocity": 612.4
},
"_dataQuality": "full"
```

- `shares` is `null` (YouTube exposes no public share count) and `lang` is `null` (not extracted) — these are reported as `null`, never invented.
- `publishedAtISO` is an ISO-8601 UTC string derived from `uploadDate`.
- `engagementVelocity` = `(likes + comments + shares) / max(hours_since_published, 1)`, or `null` when no date is available.

#### 🔍 Keyword search (`searchQuery` + `sortBy`)

Find videos by **topic** instead of by URL. When `searchQuery` is set, it **replaces** `startUrls`/`seeds`, and the actor runs a YouTube keyword search returning up to `maxResults`/`maxItems` videos.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `searchQuery` | string | — | Topic / keywords to search for. When set, replaces `startUrls`/`seeds`. |
| `sortBy` | string (enum) | `relevance` | `relevance`, `date` (newest first), or `views` (highest first). |

- **`relevance`** — YouTube's default relevance ranking (`ytsearch`).
- **`date`** — uses the date-ordered search (`ytsearchdate`) **and** re-sorts the results by `uploadDate` descending in the actor, because YouTube's server-side date ordering is currently unreliable.
- **`views`** — YouTube has **no native sort by views**, so the actor fetches the results and sorts them by `viewCount` descending in Python.

#### 💬 Comment text (`includeComments` + `commentsLimit`)

| Parameter | Type | Default | Description |
|---|---|---|---|
| `includeComments` | boolean | `false` | When `true`, attach a `comments[]` array per video. |
| `commentsLimit` | integer | `20` | Max top-level comments per video (capped at 100). |

When `includeComments: true`, each item gets an additive `comments` field:

```json
"comments": [
  { "author": "@someuser", "text": "First! Amazing video 🔥", "likes": 1240 },
  { "author": "@another",  "text": "How did you film this?",   "likes": 87 }
]
```

> ⚠️ Comment fetching is **slow** and adds run time. It is **off by default** and capped aggressively (top-level comments only, no replies, limit ≤ 100) to protect the run timeout. When `includeComments` is `false`, only `commentsCount` is returned and there is **no extra cost**.

*Roadmap items now implemented: #1 unified input vocabulary, #2 normalized output block, **#3 keyword search**, **#5 comment text**, #6 ISO date + engagement velocity, #7 `_dataQuality` quality flag.*

### 📝 Changelog

#### v1.3 (Current)

- YouTube Data API v3 enrichment for accurate likes/comments/subscribers
- Batch API calls (50 videos per request) for efficiency
- Channel statistics lookup (subscriber count, video count)
- Graceful fallback — API key optional, yt-dlp data used if no key
- PPE billing via Actor.charge()

#### v1.2

- Residential proxy via --proxy flag (YouTube blocks datacenter IPs)
- Multi-strategy: proxy residential, datacenter, direct
- Full video data via --dump-json (description, likes, comments, hashtags)
- YouTube Shorts detection
- Contract validation on all output items

#### v1.0

- Channel, playlist, and video URL scraping
- yt-dlp powered extraction
- View count, duration, thumbnail, uploader info

***

<a name="português"></a>

## 🎬 YouTube Fast Scraper — Métricas de Vídeos e Engajamento (Sem API Key)

> [🇺🇸 English](#-youtube-fast-scraper--video-metrics--engagement-no-api-key-required) | 🇧🇷 Português

Extraia dados completos de **vídeos do YouTube** de canais, playlists e vídeos individuais: visualizações, curtidas, comentários, duração, hashtags, thumbnails, detecção de Shorts e informações do canal. Alimentado por yt-dlp com failover multi-proxy. Sem necessidade de API key — enriquecimento opcional com YouTube Data API v3 para estatísticas precisas.

### ✨ Funcionalidades

- 📺 **Canais, Playlists e Vídeos** — Extraia dados de qualquer tipo de URL do YouTube (canal, playlist ou vídeo individual)
- 📊 **Métricas completas** — Visualizações, curtidas e contagem de comentários extraídos de cada vídeo
- ⏱️ **Duração e thumbnails** — Formato de duração ISO 8601 e URLs de thumbnails em alta resolução
- 🏷️ **Extração de hashtags** — De tags do vídeo e texto da descrição, sem duplicatas
- 📱 **Detecção de Shorts** — Identificação automática de YouTube Shorts baseada em duração, URL e proporção de tela
- 📝 **Download de legendas** — Legendas auto-geradas opcionais em inglês e português
- 🔑 **Enriquecimento opcional via API** — Forneça chave da YouTube Data API v3 para curtidas, comentários e inscritos precisos
- 🔄 **Failover multi-proxy** — Estratégias Residencial, Datacenter e Direto tentadas automaticamente
- ✅ **Validação de contrato** — Cada item de saída validado contra contrato de dados rigoroso antes do push
- 💰 **Pague por resultado** — $0.004/vídeo

### ✅ Capacidades e Limites

O que este actor **consegue** e **não consegue** fazer, com base no schema real de entrada/saída. (Use para evitar surpresas — veja workarounds no FAQ.)

| Descobre / aceita entrada por | Suportado? |
|---|---|
| 🔗 **URL** de canal / playlist / vídeo (incl. URL de canal com `@handle`) | ✅ Sim — via `startUrls` |
| 👤 **Handle / username** do canal | ✅ Sim — como URL de canal (ex: `https://www.youtube.com/@MrBeast`) |
| 🔍 **Palavra-chave / busca** (achar vídeos por tema) | ✅ Sim — via `searchQuery` (+ `sortBy`: relevância/data/views) |
| 🏷️ **Hashtag** como semente (ex: descobrir vídeos de `#shorts`) | ✅ Parcial — passe o texto da hashtag como `searchQuery` (ex: `"#shorts"`); hashtags também são **extraídas na saída** |

> ℹ️ **`startUrls` não é mais estritamente obrigatório.** Forneça **pelo menos um** entre `startUrls`, `seeds` ou `searchQuery`. (O prefill de `startUrls` é mantido para que o Teste de Qualidade Automatizado da Apify continue passando.)

**A saída inclui:** visualizações ✅ · curtidas ✅ · **contagem** de comentários ✅ · **texto** dos comentários ✅ *(opcional — defina `includeComments: true`; desligado por padrão)* · uploader/canal ✅ · data de upload (`uploadDate`, `AAAA-MM-DD`) ✅ · duração ✅ · thumbnail ✅ · hashtags (extraídas de tags + descrição) ✅ · flag de Shorts ✅ · inscritos & total de vídeos do canal ✅ *(somente com chave de API opcional — null caso contrário)*

### 📥 Entrada

> **Um entre `startUrls`, `seeds` ou `searchQuery` é obrigatório** (não os três). Se todos estiverem vazios, a execução falha imediatamente com um erro claro.

| Parâmetro | Tipo | Obrigatório | Padrão | Descrição |
|---|---|---|---|---|
| `startUrls` | object\[] | Condicional¹ | — | URLs do YouTube (canal, playlist ou vídeo) para extrair |
| `searchQuery` | string | Condicional¹ | — | Busca por palavra-chave: acha vídeos por tema em vez de por URL. Quando definido, **substitui** `startUrls`/`seeds`. |
| `sortBy` | string (enum) | Não | `relevance` | Ordem dos resultados de `searchQuery`: `relevance`, `date` (mais recentes primeiro) ou `views` (mais vistos primeiro). Ignorado sem `searchQuery`. |
| `includeComments` | boolean | Não | false | Se true, cada vídeo ganha um array `comments[]` de `{ author, text, likes }`. **Lento — desligado por padrão.** |
| `commentsLimit` | inteiro | Não | 20 | Máximo de comentários de nível superior por vídeo quando `includeComments` é true (limite de 100). |
| `maxItems` | inteiro | Não | 50 | Número máximo de vídeos para extrair (= número de resultados da busca quando `searchQuery` é definido) |
| `maxResults` | inteiro | Não | — | Alias opcional de `maxItems`. Se definido, sobrepõe `maxItems`. |
| `seeds` | string\[] | Condicional¹ | — | Alias opcional de `startUrls` (URLs simples do YouTube). Usado quando não-vazio e `seedType` for `url`. |
| `seedType` | string (enum) | Não | `url` | Tipo dos valores em `seeds`. Apenas `url` é suportado. |
| `normalizeOutput` | boolean | Não | false | Adiciona um bloco aditivo `_normalized` + flag `_dataQuality` por item. |
| `downloadSubtitles` | boolean | Não | false | Extrair legendas auto-geradas (EN, PT) |
| `proxyConfiguration` | object | Não | `{ "useApifyProxy": true }` | Configuração de proxy Apify. O YouTube bloqueia IPs de datacenter agressivamente; residencial é recomendado como fallback |
| `youtubeApiKey` | string | Não | — | Chave da YouTube Data API v3 para estatísticas enriquecidas (curtidas, comentários, inscritos). *Lida pelo actor em runtime; passe via JSON de entrada.* |

¹ *Condicional: forneça pelo menos um entre `startUrls`, `seeds` ou `searchQuery`.*

#### Exemplo de Entrada

```json
{
  "startUrls": [
    { "url": "https://www.youtube.com/@MrBeast" },
    { "url": "https://www.youtube.com/playlist?list=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf" },
    { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }
  ],
  "maxItems": 20,
  "downloadSubtitles": false
}
```

**Exemplo de busca por palavra-chave + comentários:**

```json
{
  "searchQuery": "mr beast desafio",
  "sortBy": "views",
  "maxResults": 10,
  "includeComments": true,
  "commentsLimit": 10
}
```

### 📤 Saída

Cada vídeo inclui estes campos:

| Campo | Tipo | Descrição |
|---|---|---|
| `id` | string | ID do vídeo no YouTube |
| `url` | string | URL completa do vídeo |
| `title` | string | Título do vídeo |
| `description` | string | Texto da descrição |
| `viewCount` | inteiro | Número de visualizações |
| `likes` | inteiro | Número de curtidas |
| `commentsCount` | inteiro | Número de comentários |
| `duration` | string | Duração ISO 8601 (ex: `PT3M12S`) |
| `durationSecs` | inteiro | Duração em segundos |
| `uploadDate` | string | Data de upload (AAAA-MM-DD) |
| `thumbnail` | string | URL da thumbnail em alta resolução |
| `hashtags` | string\[] | Tags e hashtags da descrição |
| `uploader` | string | Nome do canal/uploader |
| `uploaderUrl` | string | URL do canal |
| `uploaderFollowers` | inteiro | Número de inscritos do canal (via enriquecimento API) |
| `channelId` | string | ID do canal no YouTube |
| `channelVideoCount` | inteiro | Total de vídeos no canal (via enriquecimento API) |
| `isShort` | boolean | Se o vídeo é um YouTube Short |
| `comments` | object\[] | Comentários de nível superior `{ author, text, likes }`, truncados em `commentsLimit`. *Presente apenas quando `includeComments` é true.* |

#### Exemplo de Saída

```json
{
  "id": "fC7oUOUEEi4",
  "url": "https://www.youtube.com/watch?v=fC7oUOUEEi4",
  "title": "$456,000 Squid Game In Real Life!",
  "description": "I recreated every single game from Squid Game in real life...",
  "viewCount": 592000000,
  "likes": 18200000,
  "commentsCount": 1400000,
  "duration": "PT25M44S",
  "durationSecs": 1544,
  "uploadDate": "2021-11-24",
  "thumbnail": "https://i.ytimg.com/vi/fC7oUOUEEi4/maxresdefault.jpg",
  "hashtags": ["squidgame", "mrbeast", "challenge"],
  "uploader": "MrBeast",
  "uploaderUrl": "https://www.youtube.com/@MrBeast",
  "uploaderFollowers": 345000000,
  "channelId": "UCX6OQ3DkcsbYNE6H8uQQuVA",
  "channelVideoCount": 820,
  "isShort": false
}
```

### 📋 Casos de Uso

- 🎯 **Estratégia de conteúdo** — Analise vídeos de melhor desempenho para planejar seu conteúdo
- 🔍 **Pesquisa de concorrentes** — Acompanhe canais, uploads e taxas de engajamento dos concorrentes
- 📈 **Análise de tendências** — Identifique tópicos e formatos em alta entre canais
- 👥 **Avaliação de influenciadores** — Avalie métricas de criadores (inscritos, média de views, engajamento)
- 🏷️ **SEO e palavras-chave** — Extraia hashtags e tags usadas por vídeos de sucesso
- 📱 **Shorts vs longa duração** — Compare performance entre Shorts e vídeos regulares

### ❓ Perguntas Frequentes

**P: Preciso de uma chave da YouTube Data API?**
R: Não. O actor funciona sem chave de API usando extração de dados via yt-dlp. Porém, fornecer uma chave da YouTube Data API v3 permite curtidas, comentários e contagem de inscritos mais precisos através de chamadas em lote.

**P: Funciona com vídeos privados ou com restrição de idade?**
R: Não. O actor só consegue extrair dados de vídeos acessíveis publicamente. Vídeos privados, não listados ou com restrição de idade serão ignorados.

**P: Como funciona a detecção de Shorts?**
R: Vídeos são classificados como Shorts com base em múltiplos sinais: duração abaixo de 61 segundos, `/shorts/` na URL, `#shorts` no título ou proporção vertical (altura > largura).

**P: Por que alguns vídeos têm curtidas ou comentários nulos?**
R: Alguns criadores desabilitam contagem de curtidas ou comentários em seus vídeos. O actor reporta esses valores como null quando os dados não estão disponíveis no YouTube.

**P: Posso extrair canais inteiros?**
R: Sim. Forneça a URL do canal (ex: `https://www.youtube.com/@MrBeast`) e o actor extrairá vídeos até o limite definido em `maxItems`.

### 💰 Preços

Este actor usa precificação **Pay Per Event (PPE)**:

| Métrica | Custo |
|---------|-------|
| Por vídeo extraído | $0.004 |

**Exemplo**: Extrair 1.000 vídeos custa $4.00.

### 🔗 Actors Relacionados

- [TikTok Viral Scanner](https://apify.com/viralanalyzer/tiktok-viral-scanner) — Dados de perfis e vídeos do TikTok
- [Instagram Reels Scraper](https://apify.com/viralanalyzer/instagram-reels-scraper) — Métricas do Instagram
- [SEO Intelligence Suite](https://apify.com/viralanalyzer/seo-intelligence-suite) — Auditoria SEO completa
- [Website Change Monitor](https://apify.com/viralanalyzer/website-change-monitor) — Monitoramento de mudanças

### 🆕 Disponível — Modo Unificado / Normalizado (opcional)

> ✅ Os itens abaixo já estão **ativos** e são totalmente retrocompatíveis. Se você deixar os novos campos sem preencher (e `normalizeOutput` como `false`, o padrão), a saída é **byte a byte idêntica** às versões anteriores — integrações existentes não são afetadas.

**Novos campos de entrada opcionais** (todos opcionais; as entradas existentes continuam funcionando):

| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| `maxResults` | inteiro | — | Alias de `maxItems` (vocabulário unificado ViralAnalyzer). Se definido, sobrepõe `maxItems`. |
| `seeds` | string\[] | — | Alias de `startUrls` — uma lista simples de URLs do YouTube. Quando não-vazia **e** `seedType` for `url`, são mapeadas para `startUrls`. |
| `seedType` | string (enum) | `url` | Tipo dos valores em `seeds`. Este actor descobre vídeos **apenas por URL**, então `url` é o único valor suportado. |
| `normalizeOutput` | boolean | `false` | Quando `true`, cada item ganha um bloco aditivo `_normalized` + uma flag `_dataQuality`. Quando `false`, a saída é inalterada. |

**Bloco `_normalized`** (adicionado por item quando `normalizeOutput: true`):

```json
"_normalized": {
  "platform": "youtube",
  "url": "https://www.youtube.com/watch?v=fC7oUOUEEi4",
  "author": "MrBeast",
  "text": "$456,000 Squid Game In Real Life!",
  "views": 592000000,
  "likes": 18200000,
  "comments": 1400000,
  "shares": null,
  "publishedAtISO": "2021-11-24T00:00:00+00:00",
  "lang": null,
  "hashtags": ["squidgame", "mrbeast", "challenge"],
  "engagementVelocity": 612.4
},
"_dataQuality": "full"
```

- `shares` é `null` (o YouTube não expõe contagem pública de compartilhamentos) e `lang` é `null` (não extraído) — reportados como `null`, nunca inventados.
- `publishedAtISO` é uma string ISO-8601 em UTC derivada de `uploadDate`.
- `engagementVelocity` = `(likes + comments + shares) / max(horas_desde_publicação, 1)`, ou `null` quando não há data disponível.

#### 🔍 Busca por palavra-chave (`searchQuery` + `sortBy`)

Acha vídeos por **tema** em vez de por URL. Quando `searchQuery` é definido, ele **substitui** `startUrls`/`seeds`, e o actor roda uma busca por palavra-chave no YouTube retornando até `maxResults`/`maxItems` vídeos.

| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| `searchQuery` | string | — | Tema / palavras-chave a buscar. Quando definido, substitui `startUrls`/`seeds`. |
| `sortBy` | string (enum) | `relevance` | `relevance`, `date` (mais recentes primeiro) ou `views` (mais vistos primeiro). |

- **`relevance`** — ranking de relevância padrão do YouTube (`ytsearch`).
- **`date`** — usa a busca ordenada por data (`ytsearchdate`) **e** reordena os resultados por `uploadDate` decrescente no actor, porque a ordenação por data do lado do YouTube está atualmente instável.
- **`views`** — o YouTube **não tem ordenação nativa por views**, então o actor busca os resultados e os ordena por `viewCount` decrescente em Python.

#### 💬 Texto dos comentários (`includeComments` + `commentsLimit`)

| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| `includeComments` | boolean | `false` | Quando `true`, anexa um array `comments[]` por vídeo. |
| `commentsLimit` | inteiro | `20` | Máximo de comentários de nível superior por vídeo (limite de 100). |

Quando `includeComments: true`, cada item ganha um campo aditivo `comments`:

```json
"comments": [
  { "author": "@someuser", "text": "Primeiro! Vídeo incrível 🔥", "likes": 1240 },
  { "author": "@another",  "text": "Como você filmou isso?",      "likes": 87 }
]
```

> ⚠️ A coleta de comentários é **lenta** e aumenta o tempo de execução. Vem **desligada por padrão** e é limitada agressivamente (apenas comentários de nível superior, sem respostas, limite ≤ 100) para proteger o timeout da execução. Quando `includeComments` é `false`, apenas `commentsCount` é retornado e **não há custo extra**.

*Itens do roadmap agora implementados: #1 vocabulário de entrada unificado, #2 bloco de saída normalizado, **#3 busca por palavra-chave**, **#5 texto dos comentários**, #6 data ISO + velocidade de engajamento, #7 flag de qualidade `_dataQuality`.*

### 📝 Changelog

#### v1.3 (Atual)

- Enriquecimento via YouTube Data API v3 para curtidas/comentários/inscritos precisos
- Chamadas API em lote (50 vídeos por requisição) para eficiência
- Consulta de estatísticas do canal (contagem de inscritos e vídeos)
- Fallback gracioso — chave de API opcional, dados do yt-dlp usados se sem chave
- Cobrança PPE via Actor.charge()

#### v1.2

- Proxy residencial via flag --proxy (YouTube bloqueia IPs de datacenter)
- Multi-estratégia: proxy residencial, datacenter, direto
- Dados completos de vídeo via --dump-json (descrição, curtidas, comentários, hashtags)
- Detecção de YouTube Shorts
- Validação de contrato em todos os itens de saída

#### v1.0

- Extração de URLs de canal, playlist e vídeo
- Extração alimentada por yt-dlp
- Contagem de views, duração, thumbnail, info do uploader

# Actor input Schema

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

List of YouTube URLs (Channel, Playlist, or Video) to scrape.

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

Maximum number of videos to scrape.

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

Optional alias for 'Max Items' (unified ViralAnalyzer vocabulary). If set, it overrides 'Max Items'. Leave empty to keep using 'Max Items'.

## `seeds` (type: `array`):

Optional unified-vocabulary alias for inputs. Provide a list of plain YouTube URLs (channel, playlist, or video). When non-empty AND 'Seed Type' is 'url', these are mapped into 'Start URLs'. Leave empty to keep using 'Start URLs'.

## `seedType` (type: `string`):

Type of values provided in 'Seeds'. This actor discovers videos only from URLs (channel/playlist/video, including @handle channel URLs), so the only supported seed type is 'url'.

## `searchQuery` (type: `string`):

Optional. Find videos by topic instead of by URL. When set, the actor runs a YouTube keyword search (yt-dlp ytsearch) for this term and ignores 'Start URLs'/'Seeds'. Number of results = resolved Max Results / Max Items. Leave empty to scrape from URLs.

## `sortBy` (type: `string`):

Order for keyword-search results (only used when 'Search Query' is set). 'relevance' (default) uses YouTube relevance; 'date' uses the date-ordered search and is also re-sorted newest-first in the actor (YouTube's server-side date order is unreliable); 'views' has no native YouTube sort, so results are fetched then sorted by view count (descending) in the actor.

## `includeComments` (type: `boolean`):

If true, each video gets an additive 'comments' array of { author, text, likes }, truncated to 'Comments Limit'. WARNING: comment fetching is slow and adds run time — keep this OFF unless you need the text. When false (default), only 'commentsCount' is returned and there is no extra cost.

## `commentsLimit` (type: `integer`):

Maximum number of top-level comments to return per video when 'Include Comment Text' is on. Capped at 100 to protect the run timeout.

## `normalizeOutput` (type: `boolean`):

If true, each item gets an additive '\_normalized' block (platform, url, author, text, views, likes, comments, shares, publishedAtISO, lang, hashtags, engagementVelocity) for easy cross-platform aggregation. Existing fields are unchanged. Default false.

## `downloadSubtitles` (type: `boolean`):

If true, will attempt to extract auto-generated captions.

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

Proxy settings. YouTube aggressively blocks datacenter IPs. Residential proxy is recommended.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "maxItems": 1,
  "seedType": "url",
  "sortBy": "relevance",
  "includeComments": false,
  "commentsLimit": 20,
  "normalizeOutput": false,
  "downloadSubtitles": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Dataset containing all scraped results. Each item follows the dataset schema.

# 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/watch?v=dQw4w9WgXcQ"
        }
    ],
    "maxItems": 1,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("viralanalyzer/youtube-fast-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/watch?v=dQw4w9WgXcQ" }],
    "maxItems": 1,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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/watch?v=dQw4w9WgXcQ"
    }
  ],
  "maxItems": 1,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call viralanalyzer/youtube-fast-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=viralanalyzer/youtube-fast-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/KWkAKa9mTo11AibZW/builds/uqBADCvwtFKc8Ob1F/openapi.json
