# YouTube Outlier Videos Finder - AI Video Research & Scraper (`rich_minds/youtube-video-research-ai`) Actor

First 25 videos free. YouTube outlier videos finder + AI video research: each video scored 0-100 against your brief, views vs its channel, grounded takeaways and brand mentions. Pay only for qualified videos. Any Apify plan with your own free YouTube API key.

- **URL**: https://apify.com/rich\_minds/youtube-video-research-ai.md
- **Developed by:** [Rich Minds](https://apify.com/rich_minds) (community)
- **Categories:** Videos, Social media, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 qualified video (rules only)s

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?

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 Outlier Videos Finder - AI Video Research & Scraper

**Find the YouTube outlier videos that matter for your research question — scored 0–100 against your brief, with how far each one out-performed its channel, what it actually says, and where your brands are mentioned.**

⚡ First **25 videos free** · 💵 **$0.004** per qualified video · 🤖 **$0.012** with AI research · 🔑 **No API key needed** on Apify plans that can run Store Actors — on the free plan, search with your own free YouTube API key ([which plan?](#-faq)) · ⏱️ demo in seconds, AI ≈ 3 s per video (measured)

![One row per qualified YouTube video — relevance score, label, views ÷ subscribers, channel, format and why it matches your brief](https://api.apify.com/v2/key-value-stores/d8Ftg56vz7geIcbdS/records/shortlist.svg)

> **Try it in 30 seconds.** Click **Try it** — the form is pre-filled with a free demo (7 sample videos, nothing charged).
> Then type your own search terms and brief: a real search runs YouTube Scraper on your account (needs a paid Apify plan)
> or the YouTube Data API with your own free key (any plan). Your first **25 qualified videos** are free, and videos that
> fail your filters are never charged.
> → [What a run costs](#-pricing--what-a-run-really-costs) · [What the AI adds](#-what-the-ai-tier-adds) · [Run it weekly](#-run-it-weekly)

### ⚡ At a glance

| | |
|---|---|
| **What you get** | a ranked shortlist of videos: relevance score, outlier ratio, format, takeaways, brand mentions + `PATTERNS` |
| **Best for** | weekly monitoring of 5–10 competitor channels or niche searches — only new videos are delivered and charged |
| **You provide** | a few search terms (or channel / playlist URLs) and one sentence on what you are researching |
| **Output** | JSON / CSV / Excel dataset, best-first, plus `RUN_SUMMARY`, `PATTERNS` and a Slack-ready `DIGEST` |
| **Typical run** | 2 terms × 50 videos = 100 scraped → ~30 qualified (estimate); AI ≈ 3 s per video (measured) |
| **Cost of that run** | $0.40 YouTube Scraper + 30 × $0.012 here + ~$0.15 AI tokens ≈ $0.91 (first 25 videos free) |
| **Keys / setup** | none on paid Apify plans (Store Actors + Apify's model access); on the free plan: `youtubeApi` with a free YouTube API key, `llmProvider: byok` |
| **Works with** | Schedules, webhooks, Google Sheets, Slack, Make / n8n / Zapier, MCP & AI agents |

### 🎯 What this YouTube video research tool does

It runs the YouTube scraper you already know (`streamers/youtube-scraper`) on your account — or the official YouTube API
with your own free key — throws away what does not matter for free, and returns only the videos that fit your brief:

- **A relevance score against *your* question** — 0–100 against your `researchBrief`, with the reasons (the AI's judgement, or keyword overlap when AI is off).
- **Outlier ratio vs the channel** — views ÷ subscribers (or ÷ the channel's median views), views per day, like rate, percentile in this run.
- **What the video actually says** — key takeaways from the transcript with AI; chapter lines without it. Never invented.
- **Brand and product mentions with a quote** — every watch term with the verbatim sentence and its timestamp.
- **What works in your niche** — the free `PATTERNS` record: outlier rate by format, hook, length and title word.

You pay only for videos that qualify, never twice for the same one.

#### 📈 Find YouTube outlier videos (vs subscribers or the channel median)

Set `minOutlierRatio` (views ÷ subscribers: `1` = the video out-reached the channel, `3` = a strong outlier).
**The creators' outlier score:** `channelBaseline` compares each video with its channel's median views over its latest
`channelBaselineVideos` (one extra YouTube Scraper run at $0.004 per video inside `maxDiscoveryChargeUsd`; free in
`youtubeApi` mode) → `outlierVsChannelMedian`, `channelMedianViews`, `channel_outlier` (≥ 3×); `minChannelOutlier: 3` keeps
only those. The **Outliers (metrics)** view:

![Outliers view — views, subscribers, outlier ratio, views per day and like rate per video](https://api.apify.com/v2/key-value-stores/d8Ftg56vz7geIcbdS/records/outliers.svg)

#### 🧭 What works in your niche (`PATTERNS`, free)

Every run groups the videos that passed your filters by format, hook, length and title word and compares each
group's outlier rate with the run's — rules only, no charge. `PATTERNS` from the demo run:

```json
{"videos": 5, "overallOutlierRate": 0.4,
 "insights": ["Tutorial videos: 2 of 3 are outliers, avg 3.81× — vs 40 % across all 5 videos."]}
```

#### 📝 YouTube transcript summary: grounded key takeaways

With AI on, the model reads the transcript (free YouTube subtitles) and returns up to 5 `keyTakeaways`, each checked
against it. **Takeaways from what is *said* need the AI:** AI off gives the description's chapter lines and sentences,
promo lines ("subscribe", links, "perfect if…") dropped.

#### 🏷️ Brand and product mentions (`watchTerms`)

List brands or competitors in `watchTerms`: each video carries `mentions` — term, verbatim quote, subtitle `timestamp`.

#### 💡 Content angle per video (`suggestAngles`)

With AI on, `contentAngle` suggests one video idea per on-brief video (same call); every hot / warm row gets
`generatedText`, the next step (AI angle, or a rule-based "watch first: …").

#### 🔑 Search YouTube on the free Apify plan (`youtubeApi`)

Apify's free plan cannot run Store Actors: pick **Search YouTube with your free YouTube API key** and paste a key from
Google Cloud (enable *YouTube Data API v3* → Credentials) into `youtubeApiKey`. Free: 10,000 quota units a day — a search
page of 50 videos ≈ 100 units, a channel ≈ 3. Same filters, scores and events; no subtitles in this mode.

#### 🩳 YouTube Shorts research: Shorts outliers

Shorts are skipped by default; the **Shorts outliers** task below turns them on (`includeShorts: true`, ≤ 60 s).

### 🆚 Why this instead of `streamers/youtube-scraper`?

| | [YouTube Scraper (`streamers/youtube-scraper`)](https://apify.com/streamers/youtube-scraper) | **YouTube Outlier Videos Finder (this Actor)** |
|---|---|---|
| **Price** | $0.004 per result · 10,713 users / 30 days · 4.8★ (196 reviews) | from $0.004 per qualified video, first 25 free |
| **What you pay for** | every *row scraped*, including irrelevant videos, error rows and duplicates | only *qualified videos* — anything that fails your filters costs **$0** here |
| **Which videos matter?** | none — you sort thousands of rows by hand | 0–100 relevance score against your brief + outlier ratio, best first |
| **AI** | `ai-video-summary` / `ai-video-description` add-ons at $0.015 per video each, not steered by any goal | brief-steered score, takeaways, format, hook and mentions in one grounded call — $0.012 per qualified video |
| **Filters** | date filter is a $0.0013 surcharge per result | views, subscribers, age, duration, Shorts, keywords, outlier ratio — all free |
| **Repeat runs** | the same videos again next week | cross-run memory + exclude list — only new videos are delivered and charged |
| **Free Apify plan** | cannot run | free demo + live search with your own YouTube API key |
| **Same 100 videos, all in** | 100 rows × $0.004 + `ai-video-summary` 100 × $0.015 = **$1.90**, still unsorted | $0.40 source + 30 qualified × $0.012 + ~$0.15 tokens = **$0.91**, ranked — **52 % less** |

**Coming from a transcript Actor or an outlier tool?**

| | Transcript Actors ([`starvibe/youtube-video-transcript`](https://apify.com/starvibe/youtube-video-transcript), [`johnvc/YoutubeTranscripts`](https://apify.com/johnvc/YoutubeTranscripts)) | Outlier SaaS (vidIQ, 1of10, TubeBuddy) | **This Actor** |
|---|---|---|---|
| **You get** | the raw transcript text of the videos you already picked | an outlier score around your own channel | which videos matter for *your* brief, why, grounded takeaways and quoted mentions |
| **Pricing** | per transcript, relevant or not (2,303 / 1,863 users a month) | $10–$50 a month per seat | $0.012 per qualified video with AI, nothing for the rest |
| **Outliers** | — | ✓ | ✓ vs subscribers or the channel median, plus `PATTERNS` |

### 💵 Pricing — what a run really costs

Pay per result: **one event per qualified video, nothing else.** No start fee.

| Event | When it is charged | Price |
|---|---|---|
| `free-tier` | your first 25 qualified videos on this Actor, in any mode | **$0.00** |
| `qualified-video-basic` | AI off (or AI unavailable) — normalised fields, metrics, flags, rule score, format, takeaways, mentions | **$0.004** |
| `qualified-video-ai` | AI on — everything above plus the AI relevance score with reasons, hook, grounded takeaways, mentions, content angle | **$0.012** |

**You are never charged for:** the free demo, `PATTERNS` / `DIGEST`, videos below `minScore`, videos that fail a filter,
excluded Shorts, source error rows, videos on your exclude list, videos you already received in an earlier run.

**How that compares** — the top 10 priced YouTube Actors in the Store search "youtube scraper" charge a median $0.003
per raw row (checked 2026-09-25) and YouTube Scraper charges $0.004 per result. Here $0.004 buys a *qualified* video, not
a raw row — in the worked example (an estimate) 30 of 100 qualify, so one stands in for ~3 raw rows and the other 70
cost $0 here. The AI tier ($0.012) is below the source's $0.015 `ai-video-summary`.

**Worked example** (source funnel an estimate until the first live run) — 2 search terms × 50 videos = 100 videos
scraped → 30 pass your free filters and `minScore` with AI on: YouTube Scraper $0.40 on your account + 30 × $0.012 =
$0.36 here + ~$0.15 AI tokens → **$0.91 total, about $0.030 per qualified video**. With `youtubeApi` the source part is
$0 (your quota). On a new account the first 25 are free.

AI tokens are billed through Apify's model access at OpenRouter rates, or to your own key: measured 2026-09-25, ~1,250
in + ~700 out per video ≈ $0.005 on Claude Haiku 4.5.

### 🚀 How to use it

1. **Click `Try it`** — the form starts on the free demo (7 sample videos about Python web scraping) and works as is.
2. **Switch `sourceMode`** (the first field) to a **Search YouTube** option, say what you are researching in
   `researchBrief` and type your `searchQueries` (or URLs in `startUrls`). The form suggests 3 terms × 30 videos
   (≈ $0.36 at the source) — enough to use your free videos and see the first paid ones.
3. **Add free filters** you care about (`minOutlierRatio`, `maxAgeDays`, `watchTerms`, `minScore`) and press **Start**.
4. **Open the dataset** — qualified videos land best-first; export to CSV/Excel, or wire a webhook.

### 🤖 What the AI tier adds

The same demo video, AI **off** (`qualified-video-basic`, $0.004):

```json
{
  "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
  "score": 83, "label": "hot", "scoreSource": "rules", "ruleScore": 83,
  "reasons": ["mentions 'beginners', 'python', 'web', 'scraping' in the title",
              "mentions 'python', 'beautifulsoup' in the hashtags",
              "outlier: 6.482× views vs the channel's 48,200 subscribers"],
  "format": "tutorial", "hookType": "how-to",
  "keyTakeaways": ["01:12 Installing requests and BeautifulSoup", "05:40 Finding elements with CSS selectors",
                   "14:05 Handling pagination", "21:30 Saving to CSV"],
  "contentAngle": null
}
```

AI **on** (`qualified-video-ai`, $0.012) — a real run of `storage-example/INPUT.ai.json` on 2026-09-25 with
`groq:openai/gpt-oss-120b` (own key; 5 videos assessed in 13.5 s):

```json
{
  "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
  "score": 90, "label": "hot", "scoreSource": "ai",
  "reasons": ["The title calls it a \"BeautifulSoup Tutorial for Beginners\", matching the brief for beginner-friendly content.",
              "The transcript walks through each step—installing, finding elements, handling pagination, and saving to CSV—showing a complete end‑to‑end workflow."],
  "format": "tutorial", "hookType": "how-to",
  "keyTakeaways": ["Install requests and BeautifulSoup via pip.", "Use requests to download a web page.",
                   "Parse HTML and locate elements using CSS selectors with BeautifulSoup.",
                   "Handle pagination by following the next‑page link in a loop.", "Write the scraped data to a CSV file."],
  "mentions": [{"term": "BeautifulSoup", "quote": "BeautifulSoup lets you find elements with CSS selectors", "timestamp": "05:40"}],
  "contentAngle": "Produce a beginner tutorial that adds Playwright to the same project, showing how to scrape JavaScript‑rendered pages after the BeautifulSoup basics.",
  "aiModel": "groq:openai/gpt-oss-120b"
}
```

That is the 3–5 minutes per video you would otherwise spend watching and taking notes — for $0.008 more than the basic
row. Videos the AI scores below your `minScore` are not charged, and the status says how many.

### ⚙️ Input

#### Input options

| Field | Type | Default | What it does |
|---|---|---|---|
| `sourceMode` | `actor` | `youtubeApi` | `dataset` | `list` | `actor` | YouTube Scraper (paid plan), your free YouTube API key (any plan), a dataset, or pasted videos (the form starts on the free `list` demo) |
| `researchBrief` | string | — | What you are researching; every video is scored 0–100 against it |
| `searchQueries` | string\[] | — | YouTube search terms, one per line |
| `maxResultsPerQuery` | integer | `20` | Videos per search term / URL (form: 30; lowered to fit the spend cap) |
| `maxDiscoveryChargeUsd` | number | `2` | Caps what YouTube Scraper may charge your account (form: $0.50) |
| `minOutlierRatio` | number | `0` | Keep only videos with at least this many views per subscriber |
| `minScore` | 0–100 | `40` | Below this = not delivered, not charged |
| `enableAi` | boolean | `true` | AI research assessment on/off |
| `maxQualified` | integer | `100` | Hard cap on output (and on spend) |

`youtubeApiKey`, `channelBaseline` and `watchTerms` are explained in their sections above.
**Free filters** (section 2 of the form): `mustIncludeKeywords`, `excludeKeywords`, `minViews` / `maxViews`,
`minSubscribers` / `maxSubscribers`, `maxAgeDays`, `minDurationSec` / `maxDurationSec`, `includeShorts` (off by default),
`minChannelOutlier`, `channelBaselineVideos` / `channelBaselineMaxChannels`, `targetFlags` (only videos with one of these
flags) and `suppressionList` (videos or channels to never output). `0` means "no limit". On the free demo, every filter
you change applies to the sample too — `minScore: 100` returns "0 of 7 sample videos passed your filters".

**AI and output:** `suggestAngles`, `llmProvider`, `llmModel`, `llmApiKey`, `aiCandidateMultiplier`, `maxToProcess`,
`dedupeStoreName`, `webhookUrl`, `webhookHeaders`, `webhookBatchSize`. **Other sources:** `itemsList`, `datasetId`.

**Flags** (for `targetFlags`): `outlier`, `strong_outlier`, `channel_outlier`, `top_of_batch`, `fast_growing`,
`high_engagement`, `fresh`, `small_channel`, `watch_term_mentioned`, `has_transcript`, `long_form`, `short`.

### 📤 Output

One dataset item per qualified video — JSON, CSV, Excel or a webhook. This row is from the free demo run (AI off,
shortened — every field is in the table below):

```json
{
  "videoId": "k3Vq9TzP1aE",
  "url": "https://www.youtube.com/watch?v=k3Vq9TzP1aE",
  "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
  "score": 83, "label": "hot", "scoreSource": "rules",
  "durationSec": 1458, "views": 312450, "likes": 9870, "comments": 412,
  "channelName": "Code with Mira", "channelSubscribers": 48200,
  "viewsPerDay": 1285.8, "likeRate": 0.0316, "outlierRatio": 6.482, "batchViewPercentile": 83,
  "flags": ["outlier", "strong_outlier", "fast_growing", "has_transcript", "long_form", "watch_term_mentioned"],
  "format": "tutorial",
  "keyTakeaways": ["01:12 Installing requests and BeautifulSoup", "05:40 Finding elements with CSS selectors"],
  "generatedText": "Watch first: this tutorial from Code with Mira — outlier: 6.482× views vs the channel's 48,200 subscribers. Its title uses a how-to hook — note how it frames the topic before you plan yours.",
  "chargedEvent": "demo", "billedAs": "qualified-video-basic",
  "itemId": "2447eb0e9f7c", "dedupeKey": "yt:k3Vq9TzP1aE"
}
```

#### Output fields

| Field | Description |
|---|---|
| `videoId`, `url`, `title`, `itemId`, `dedupeKey` | Identity of the video — stable across runs, safe as a primary key |
| `score`, `label`, `scoreSource`, `reasons`, `ruleScore` | Final 0–100 score (AI, else rules), hot / warm / cold, and why |
| `views`, `likes`, `comments`, `commentsTurnedOff`, `isMonetized`, `publishedAt`, `ageDays`, `durationSec`, `isShort`, `location` | The video's numbers and status as the source returned them |
| `description`, `descriptionLinks`, `hashtags`, `thumbnailUrl`, `hasTranscript` | Description (≤ 2,000 chars), its links, hashtags, thumbnail, whether subtitles were read |
| `channelName`, `channelUrl`, `channelSubscribers`, `channelTotalVideos`, `channelLocation`, `foundVia` | The channel and the search / URL that found the video |
| `outlierRatio`, `channelMedianViews`, `outlierVsChannelMedian`, `viewsPerDay`, `likeRate`, `commentRate`, `batchViewPercentile`, `flags` | Derived metrics and rule flags (channel median only with `channelBaseline`, else `null`) |
| `format`, `hookType`, `keyTakeaways`, `mentions`, `contentAngle`, `generatedText` | Research fields (AI on; rule fallbacks when off) and the next step on hot / warm rows |
| `aiModel`, `chargedEvent`, `billedAs`, `scrapedAt` | Provenance and billing (`billedAs` = the price tier, also on free rows) |

Dataset **views**: **Shortlist** (best first, with the next step) · **Outliers (metrics)** · **Insights (AI)** ·
**Overview** (thumbnails, flags, charges).

`RUN_SUMMARY` (also `OUTPUT`) holds the funnel, skip counts, `topChannels`, `yieldByQuery` (loaded → qualified → hot per
term), `aiTokens`, `timing` (seconds per phase), the top `patterns` and what was charged; `PATTERNS` is the full niche
analysis; `DIGEST` is the run as Slack / e-mail-ready Markdown. Failures are explained in `sourceError` / `aiError`.

⭐ **Found it useful? A review on the Store helps others find it** — it takes a minute on the Actor's page.

### 🔁 Run it weekly

Monitoring is what this Actor is built for:

1. **Actions → Schedule** in the Console (weekly), your input saved as is — or start from the **Competitor monitoring**
   task below: 10 channels × their 20 newest videos, last 14 days.
2. Leave `dedupeAcrossRuns: true` — every video you were charged for is remembered, so the next run delivers and charges
   **only what is new**.
3. Point Slack or e-mail at the `DIGEST` record ("7 new videos (2 hot)" + links + patterns), or use the Sheets template
   (`docs/sheets/` in the repo).

Weekly cost for 10 competitor channels × 20 newest: $0.80 at the source (**$0 with `youtubeApi`**) + ~15 new qualified
videos × $0.012 ≈ **$0.18 here per week**.

#### 🕵️ YouTube competitor analysis on a schedule

Competitor channels in `startUrls`, `sortVideosBy: NEWEST`, your products in `watchTerms`, weekly: every new competitor
video arrives scored, with your products' mentions quoted.

#### 🎯 Try it for your niche

Each saved Task (`storage-example/tasks/*.json`) is one click — run it once, then schedule it.

| Niche | What it looks for | Task |
|---|---|---|
| Competitor monitoring | 10 dev-tool channels · 20 newest each · `watchTerms` Copilot / Cursor / Claude · last 14 days | `storage-example/tasks/competitor-monitor.json` |
| Outlier research for a creator | `home espresso setup`, `budget espresso machine` · `minOutlierRatio: 1` · last 90 days | `storage-example/tasks/outlier-research.json` |
| Product / brand mentions | `notion vs obsidian`, `best note taking app` · `watchTerms` Notion / Obsidian / Evernote | `storage-example/tasks/product-mentions.json` |
| Shorts outliers | `espresso shorts`, `latte art shorts` · Shorts only · `minOutlierRatio: 1` · last 60 days | `storage-example/tasks/shorts-outliers.json` |

### 🔌 Integrations, automation and API

- **Webhook** — `webhookUrl` POSTs each qualified video to Zapier, Make, n8n, Slack or your CMS as
  `{"event": "video.qualified", "video": {…}, "runId": "…"}`; with `webhookBatchSize` N it POSTs
  `{"event": "videos.qualified", "videos": [… N rows], "runId": "…"}` (the last batch may be shorter). A 429 / 5xx /
  network error is retried once after 2 s (15 s timeout); a POST that still fails is counted in `OUTPUT.webhook.failed`
  and never fails the run — the dataset keeps every video.
- **Google Sheets / Slack / HubSpot** — one click from the Actor's **Integrations** tab.
- **AI agents / MCP** — **first call = the free trial:** `{"sourceMode": "list", "itemsList": [...]}` processes the rows
  you send, no source run — the snippets below do exactly that. An empty `{}` runs the free demo.

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("rich_minds/youtube-video-research-ai").call(run_input={
    "sourceMode": "list",
    "itemsList": [{"id": "k3Vq9TzP1aE", "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
                   "url": "https://www.youtube.com/watch?v=k3Vq9TzP1aE", "viewCount": 312450,
                   "numberOfSubscribers": 48200, "date": "8 months ago", "duration": "24:18"}],
    "researchBrief": "beginner Python web scraping tutorials",
    "maxQualified": 50,
    "maxDiscoveryChargeUsd": 0.5,  # spend cap on your account once you switch to "sourceMode": "actor"
}, timeout_secs=600)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["score"], item["outlierRatio"])
```

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('rich_minds/youtube-video-research-ai').call({
    sourceMode: 'list',
    itemsList: [{ id: 'k3Vq9TzP1aE', title: 'Web Scraping with Python - BeautifulSoup Tutorial for Beginners',
        viewCount: 312450, numberOfSubscribers: 48200 }],
    researchBrief: 'beginner Python web scraping tutorials',
    maxQualified: 50,
    maxDiscoveryChargeUsd: 0.5, // spend cap on your account once you switch to sourceMode: 'actor'
}, { timeout: 600 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => [i.title, i.score, i.outlierRatio]));
```

**Use it from Claude, ChatGPT or any MCP client** — add Apify's MCP server with this Actor as a tool:

```json
{"mcpServers": {"apify": {"url": "https://mcp.apify.com/?actors=rich_minds/youtube-video-research-ai"}}}
```

Then ask: *"Run youtube-video-research-ai in `list` mode on these rows first (free), then search YouTube with
`sourceMode: actor` and `maxDiscoveryChargeUsd: 0.5`."*

#### Run outcomes — what your integration sees

| Outcome | Run status | Dataset | `OUTPUT` | Charged? | What to do |
|---|---|---|---|---|---|
| Free demo (the pre-filled sample) | SUCCEEDED | sample videos, `chargedEvent: demo` | `demo: true` | no | switch the source and type your own terms |
| Success | SUCCEEDED | qualified videos, best first | funnel, `chargedEvents`, `timing` | per qualified video | — |
| Nothing matched | SUCCEEDED | empty | `qualified: 0` | no | loosen `minScore` / filters |
| Demo text kept in a real search | SUCCEEDED | empty | `inputNeeded` | no | replace the field the status names |
| Invalid input | FAILED | empty | — | no | fix the field the status message names |
| Source failed | FAILED | empty | `sourceError` | no | follow the reason (plan, API key / quota, timeout) |
| AI unavailable | SUCCEEDED | rule-scored videos | `aiError`, `freeAiReserve` | free units first (5 kept for the AI), then basic | `llmProvider: byok` with your own key |

### 👥 Who is it for?

| You are… | You run it to… | Start with |
|---|---|---|
| A marketing / competitive-intelligence team | monitor every new competitor video each week, scored for relevance, with the key points | the **Competitor monitoring** task · weekly schedule + `DIGEST` to Slack |
| A content strategist / YouTube channel team | find the videos in your niche that out-performed their channel, and the formats that work | `searchQueries` for your niche · `minOutlierRatio: 1` · `maxAgeDays: 90` · `PATTERNS` |
| A product / market researcher | find review and comparison videos that mention a product, and quote what they say | `searchQueries` like `X vs Y` · `watchTerms` with the product names |
| An AI agent builder | a compact, typed, pre-qualified video list instead of thousands of raw rows | the MCP block above, first call in `list` mode |

### 🧠 How the AI works

- **Grounded, not generative.** The model sees one video — title, channel, description, transcript with timestamps —
  and scores it against your `researchBrief` in one typed call.
- **Every claim is checked after the call.** Reasons and takeaways must be supported by the video's own text; a mention
  survives only with a verbatim quote; only the best rule-scored candidates get the AI pass.
- **Model:** Apify's model access (default `anthropic/claude-haiku-4.5`) or your own key (`llmProvider: "byok"`).

### 🔒 Data, compliance and limits

- Public video metadata and captions only. No login, no commenter data; transcripts are read, then discarded.
- Relative dates ("10 months ago") are approximate; no subscriber count = no outlier ratio.
- Use the output within YouTube's (and the YouTube API's) terms and applicable law (GDPR, copyright for quotes).

### ❓ FAQ

**How much will one run cost me?** Source: $0.004 per video loaded ($0 in `youtubeApi` mode); here $0.004 per qualified
video ($0.012 with AI) + ~$0.005 tokens. The form's first search (3 terms × 30) is $0.36 at the source, capped by
`maxDiscoveryChargeUsd` ($0.50 in the form, $2 for API calls); the first 25 qualified videos are free.

**How long does a run take?** Every run writes `OUTPUT.timing` (`sourceSecs`, `aiSecs`, `aiSecsPerUnit`, `totalSecs`).
Measured: the demo 2–3 s; the AI ≈ 2.7 s per video, 8 in parallel (5 videos in 13.5 s on 2026-09-25; the platform BYOK
run, 4 videos: 24.9 s end to end). The YouTube Scraper step is not yet measured on a live run (estimate 1–2 min per 50
videos). API callers: pass `timeoutSecs: 600` (Python `timeout_secs=600`) for a search of up to 100 videos.

**Is this a YouTube video scraper?** It runs YouTube Scraper (`streamers/youtube-scraper`) — or the YouTube API with your
key — and adds the research layer: filters, outlier ratio, a score against your brief, takeaways and mentions. Fewer,
better rows, and you pay only for those.

**How is this different from YouTube content research tools like vidIQ, 1of10 or TubeBuddy?** Those sell seats ($10–$50
per month) around your own channel. Here 100 researched videos with AI cost about $1.70 plus the source run, for any
niche — and `PATTERNS` answers "what works" for free.

**Can I use it for YouTube trend research?** Yes — set `dateFilter: week` or `maxAgeDays: 7`, sort by views, and watch
`viewsPerDay`, `fresh` and `fast_growing`; a weekly schedule shows what is rising in your niche.

**Which Apify plan do I need?** Any plan runs the free demo and `youtubeApi` mode (your free YouTube Data API key).
Searching with YouTube Scraper and the built-in AI (Apify's model access) need a paid plan that can run Store Actors; on
the free plan add `llmProvider: "byok"` with your own model key. If the AI is refused, the status says "AI unavailable",
your free videos are used first (5 are kept for when the AI runs), then the basic price applies. A model error never
fails the run.

**What does "qualified" mean?** A video that passed every free filter you set and scored at least `minScore` against your
brief. Only qualified videos are delivered and charged.

**What happens if nothing matches my filters?** The run succeeds with 0 rows and charges nothing; the status says so.

**Does it transcribe videos without subtitles?** Only if you pick a speech-to-text option in **Transcript source** — the
source charges $0.048 per transcribed minute on your account. By default it uses YouTube's free subtitles.

**Is this legal?** Public video pages and captions only, no login — see Data, compliance and limits.

**Can I schedule it without paying twice?** Yes — cross-run memory means you only pay for new videos
([Run it weekly](#-run-it-weekly)).

### 🧩 More Actors from the same developer

Closest to YouTube research first:

- **[Instagram Creator Qualifier](https://apify.com/rich_minds/instagram-creator-qualifier)** — creator research on Instagram: find and vet creators for your niche
- **[Google Reviews Insights](https://apify.com/rich_minds/google-reviews-insights)** — google reviews analysis: what customers praise and complain about
- **[Linkedin Intent Leads](https://apify.com/rich_minds/linkedin-intent-leads)** — linkedin buying intent: posts from people asking for a tool like yours
- **[Local Business Lead](https://apify.com/rich_minds/local-business-lead)** — local business leads from Google Maps with e-mails

### 🆘 Support

Something missing or wrong? Open an issue on the Actor's page — buyer requests ship first.

### 📝 Changelog

- **0.3** (2026-09-25) — `youtubeApi` source (live search on any Apify plan with your free YouTube API key), free
  `PATTERNS` record, `OUTPUT.timing`, the free tier holds when the AI is unavailable (5 kept for the AI), promo-free
  basic takeaways, 3 × 30 first search, 10-channel monitoring task.
- **0.2** (2026-09-25) — `channelBaseline` outlier score, `generatedText`, hook type, Shorts task, `DIGEST`.
- **0.1.1** (2026-09-25) — first published build: listing, pricing and the platform README.
- **0.1** (2026-09-25) — initial release: YouTube Scraper source with free filters, outlier metrics, brief-scored relevance,
  grounded takeaways, watch-term mentions and content angles.

# Actor input Schema

## `sourceMode` (type: `string`):

<b>Paste videos (free demo)</b> — the form starts here with sample videos, nothing charged. <b>Search YouTube with YouTube Scraper</b> — runs <code>streamers/youtube-scraper</code> on your account with the terms / URLs below; its usage is billed by that Actor and it <b>needs a paid Apify plan</b> (the free plan cannot run Store Actors). <b>Search YouTube with your free YouTube API key</b> — works on every plan, including the free one: paste a YouTube Data API v3 key below (free, 10,000 quota units a day ≈ 90 searches); no subtitles in this mode. <b>Existing dataset</b> — re-qualify a YouTube Scraper dataset you already have (under <i>Other sources</i>).

## `researchBrief` (type: `string`):

Describe the videos you want in plain words, e.g. <i>beginner Python web scraping tutorials that build a complete project</i>. Every video gets a 0–100 relevance score against this (AI on: the model's judgement with reasons; AI off: keyword overlap). Leave empty to rank by out-performance only.

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

What to type into YouTube search, one term per line. Each term returns up to <b>Videos per search term</b> videos from YouTube Scraper on your account ($0.004 each).

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

Competitor channels (<code>https://www.youtube.com/@name</code>), playlists, single videos or search-result URLs to research instead of — or next to — the search terms.

## `maxResultsPerQuery` (type: `integer`):

How many videos are loaded per search term or channel/playlist URL (YouTube Scraper or your YouTube API key). More = better coverage, higher source cost (YouTube Scraper: automatically lowered to fit <b>Max source spend</b>; API key: about 100 quota units per 50 videos).

## `maxDiscoveryChargeUsd` (type: `number`):

Hard cap on what the YouTube Scraper run may charge your account in <b>Search YouTube</b> mode — the videos per term are lowered to fit it ($0.004 per video, +$0.0013 with an oldest-post date). The form starts at $0.50; an API or agent call that leaves it out is capped at $2 per run.

## `youtubeApiKey` (type: `string`):

Only for <i>Search YouTube with your free YouTube API key</i>. Create it in Google Cloud → APIs & Services → enable <b>YouTube Data API v3</b> → Credentials → Create API key. Free: 10,000 quota units a day — one search page of 50 videos costs about 100. Stored encrypted, used only for this run's calls to the YouTube API.

## `mustIncludeKeywords` (type: `array`):

Keep only videos whose title, description or hashtags contain at least one of these words or phrases. Free filter — dropped videos are never charged.

## `excludeKeywords` (type: `array`):

Drop videos whose title, description or hashtags contain any of these words (e.g. <i>music</i>, <i>live stream</i>).

## `watchTerms` (type: `array`):

Brand, product or competitor names. Every delivered video lists where each one is said (transcript quote with timestamp) or written (description). Turns on free subtitles in the source run.

## `minViews` (type: `integer`):

Drop videos with fewer views.

## `maxViews` (type: `integer`):

Drop videos with more views (0 = no limit) — useful to find rising videos before they peak.

## `minSubscribers` (type: `integer`):

Drop videos from channels smaller than this.

## `maxSubscribers` (type: `integer`):

Drop videos from channels bigger than this (0 = no limit) — small channels with big videos are the clearest outliers.

## `maxAgeDays` (type: `integer`):

Drop videos published longer ago than this (0 = any age). Relative dates like "10 months ago" are approximate.

## `minDurationSec` (type: `integer`):

Drop videos shorter than this.

## `maxDurationSec` (type: `integer`):

Drop videos longer than this (0 = no limit).

## `includeShorts` (type: `boolean`):

Off = Shorts (≤ 60 s or a /shorts/ URL) are skipped for free and not requested from the source. On = the source also loads Shorts for each search term.

## `minOutlierRatio` (type: `number`):

Keep only videos with at least this many views per channel subscriber, e.g. <b>1</b> = the video out-reached the whole channel, <b>3</b> = a strong outlier. 0 = off. Videos without a subscriber count are dropped when this is on.

## `channelBaseline` (type: `boolean`):

The creators' outlier score: loads the latest videos of the top candidate channels with YouTube Scraper on your account ($0.004 per video, inside <b>Max source spend</b>) and adds <code>outlierVsChannelMedian</code> = views ÷ the channel's median views, plus the <code>channel\_outlier</code> flag (≥ 3×). Not run on the free demo.

## `minChannelOutlier` (type: `number`):

Keep only videos with at least this many times the channel's median views, e.g. <b>3</b> = a clear outlier for that channel. 0 = off. Turns on the channel baseline; videos whose channel was not measured are dropped.

## `channelBaselineVideos` (type: `integer`):

How many of each channel's latest videos make its median (lowered to fit <b>Max source spend</b>).

## `channelBaselineMaxChannels` (type: `integer`):

How many channels to measure — the channels of the most-viewed candidates first. 10 channels × 20 videos ≈ $0.80 on your account.

## `minScore` (type: `integer`):

Videos scoring below this against your research brief are discarded and <b>not charged</b>.

## `targetFlags` (type: `array`):

Only deliver videos with at least one of these flags, e.g. <code>outlier</code>, <code>fresh</code>, <code>small\_channel</code>, <code>watch\_term\_mentioned</code> (all flags are listed in the README).

## `suppressionList` (type: `array`):

Video URLs, video ids, channel names or channel URLs to never output — your own channel, videos you already covered. Skipped before any processing, never charged.

## `enableAi` (type: `boolean`):

Scores each video against your brief with reasons, classifies format and hook, writes grounded key takeaways from the transcript, finds watch-term quotes and suggests a content angle. Off = rule-based scores, format and takeaways only (cheaper).

## `suggestAngles` (type: `boolean`):

The AI adds one video idea that builds on each on-brief video (same model call, no extra cost).

## `llmProvider` (type: `string`):

<b>Apify (no keys)</b> — the AI runs through Apify's built-in model access on plans that can run Store Actors; tokens are billed to your Apify account at OpenRouter rates. <b>My own key</b> — use your OpenAI / Anthropic / Gemini / Groq key instead (any plan).

## `llmModel` (type: `string`):

Leave empty for the default (<code>anthropic/claude-haiku-4.5</code>). Apify mode takes an OpenRouter slug such as <code>openai/gpt-4.1-mini</code>; own-key mode takes <code>provider:model</code>, e.g. <code>anthropic:claude-haiku-4-5-20251001</code>.

## `llmApiKey` (type: `string`):

Required when <b>AI model access</b> is <i>My own API key</i>. Stored encrypted by Apify, never logged.

## `aiCandidateMultiplier` (type: `integer`):

How many of the best rule-scored videos get the AI pass, as a multiple of <b>Max qualified videos</b>. Higher = more thorough, slower, more tokens.

## `maxQualified` (type: `integer`):

Hard cap on results (and therefore on what you pay for). The best-scoring videos are output first.

## `maxToProcess` (type: `integer`):

Upper bound on how many videos are scored in one run (controls run time). Default = 3 × max qualified videos.

## `dedupeAcrossRuns` (type: `boolean`):

Remembers every video you were charged for (in a named key-value store on your account) and skips it in future runs — a weekly schedule delivers only new videos.

## `dedupeStoreName` (type: `string`):

Key-value store used for cross-run memory. Use different names for different research projects.

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

Qualified videos are POSTed here as JSON (Zapier, Make, n8n, Slack, your CMS). For Google Sheets / Slack you can also use Apify's built-in Integrations tab.

## `webhookHeaders` (type: `object`):

Extra HTTP headers for the webhook, e.g. <code>{"Authorization": "Bearer …"}</code>.

## `webhookBatchSize` (type: `integer`):

1 = one POST per video the moment it is ready. Higher = one POST per N videos.

## `sortingOrder` (type: `string`):

How YouTube orders the search results the source loads.

## `dateFilter` (type: `string`):

YouTube's own upload-date search filter (free). For an exact window use <b>Max age (days)</b>.

## `lengthFilter` (type: `string`):

YouTube's own length search filter.

## `videoType` (type: `string`):

Restrict search results to regular videos or movies.

## `sortVideosBy` (type: `string`):

For channel URLs: load the newest, the most popular or the oldest videos first.

## `oldestPostDate` (type: `string`):

For channel URLs: only videos published after this date (<code>YYYY-MM-DD</code> or e.g. <code>30 days</code>). The source charges $0.0013 extra per video when set — <b>Max age (days)</b> does the same for free after loading.

## `maxResultStreams` (type: `integer`):

Also load this many past live streams per search term or channel (0 = none).

## `subtitlesLanguage` (type: `string`):

Which subtitle track the transcript comes from.

## `transcriptMode` (type: `string`):

<b>Auto</b> — YouTube's existing subtitles (free) whenever the AI or watch terms read them. The two speech-to-text modes are the source's paid transcription ($0.048 per transcribed minute on your account) — pick them only for videos without subtitles.

## `discoveryActorId` (type: `string`):

Actor used in <b>Search YouTube</b> mode. Any Actor whose output has YouTube Scraper's fields works.

## `discoveryInput` (type: `object`):

Merged over the input this Actor builds for YouTube Scraper (e.g. <code>{"isHD": true}</code>). The source's paid AI add-ons stay off — this Actor's AI tier replaces them.

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

Not needed: YouTube Scraper handles its own proxies. Kept for compatibility.

## `itemsList` (type: `array`):

Only for <b>Paste videos</b> mode. JSON array of YouTube Scraper rows (<code>title</code>, <code>url</code>, <code>viewCount</code>, <code>numberOfSubscribers</code>, <code>text</code>, <code>subtitles</code> …). The prefilled sample is the free demo.

## `datasetId` (type: `string`):

Only for <b>Existing dataset</b> mode. Pick a YouTube Scraper dataset so the Actor is granted read access to it.

## Actor input object example

```json
{
  "sourceMode": "list",
  "researchBrief": "Beginner-friendly Python web scraping tutorials (requests, BeautifulSoup, Scrapy, Playwright) that teach a complete project step by step",
  "searchQueries": [
    "python web scraping tutorial",
    "beautifulsoup tutorial",
    "scrapy tutorial for beginners"
  ],
  "maxResultsPerQuery": 30,
  "maxDiscoveryChargeUsd": 0.5,
  "minViews": 0,
  "maxViews": 0,
  "minSubscribers": 0,
  "maxSubscribers": 0,
  "maxAgeDays": 0,
  "minDurationSec": 0,
  "maxDurationSec": 0,
  "includeShorts": false,
  "minOutlierRatio": 0,
  "channelBaseline": false,
  "minChannelOutlier": 0,
  "channelBaselineVideos": 20,
  "channelBaselineMaxChannels": 10,
  "minScore": 40,
  "enableAi": true,
  "suggestAngles": true,
  "llmProvider": "apify",
  "aiCandidateMultiplier": 2,
  "maxQualified": 100,
  "dedupeAcrossRuns": true,
  "dedupeStoreName": "youtube-video-research-ai-seen",
  "webhookBatchSize": 1,
  "sortingOrder": "relevance",
  "dateFilter": "any",
  "lengthFilter": "any",
  "videoType": "any",
  "sortVideosBy": "NEWEST",
  "oldestPostDate": "",
  "maxResultStreams": 0,
  "subtitlesLanguage": "en",
  "transcriptMode": "auto",
  "discoveryActorId": "streamers/youtube-scraper",
  "discoveryInput": {},
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "itemsList": [
    {
      "id": "k3Vq9TzP1aE",
      "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
      "url": "https://www.youtube.com/watch?v=k3Vq9TzP1aE",
      "thumbnailUrl": "https://i.ytimg.com/vi/k3Vq9TzP1aE/maxresdefault.jpg",
      "viewCount": 312450,
      "date": "8 months ago",
      "likes": 9870,
      "commentsCount": 412,
      "duration": "24:18",
      "channelName": "Code with Mira",
      "channelUrl": "https://www.youtube.com/@codewithmira",
      "numberOfSubscribers": 48200,
      "channelTotalVideos": 96,
      "channelLocation": "Canada",
      "isMonetized": true,
      "commentsTurnedOff": false,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
      "text": "Learn web scraping with Python from zero: we download a page with requests, parse it with BeautifulSoup and save the results to CSV. Perfect if you have never scraped a website before.\n\n00:00 Intro\n01:12 Installing requests and BeautifulSoup\n05:40 Finding elements with CSS selectors\n14:05 Handling pagination\n21:30 Saving to CSV\n\n#python #webscraping #beautifulsoup",
      "subtitles": [
        {
          "language": "en",
          "type": "auto_generated",
          "srt": "1\n00:00:01,000 --> 00:00:04,500\nin this video we build a web scraper in Python from scratch\n\n2\n00:01:12,000 --> 00:01:16,000\nfirst install requests and BeautifulSoup with pip\n\n3\n00:05:40,000 --> 00:05:45,000\nBeautifulSoup lets you find elements with CSS selectors\n\n4\n00:14:05,000 --> 00:14:09,000\nto handle pagination we follow the next page link in a loop\n\n5\n00:21:30,000 --> 00:21:35,000\nfinally we write every product row to a CSV file"
        }
      ]
    },
    {
      "id": "Rw8nD2xYp4Q",
      "title": "Build a Price Tracker in Python (requests + BeautifulSoup) - Full Project",
      "url": "https://www.youtube.com/watch?v=Rw8nD2xYp4Q&list=PLx9sampleplaylist&index=3",
      "viewCount": 41700,
      "date": "3 weeks ago",
      "likes": 2150,
      "commentsCount": 133,
      "duration": "38:05",
      "channelName": "Byte Sized Dev",
      "channelUrl": "https://www.youtube.com/@bytesizeddev",
      "numberOfSubscribers": 8900,
      "channelTotalVideos": 41,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
      "text": "A complete beginner project: a Python price tracker that checks a product page every day and emails you when the price drops. Step by step with requests, BeautifulSoup and a scheduler.\n\n00:00 What we build\n03:20 Scraping the price\n17:45 Storing history\n29:10 Email alerts",
      "subtitles": [
        {
          "language": "en",
          "type": "auto_generated",
          "srt": "today we build a complete price tracker project step by step\nwe fetch the product page with requests\nthen BeautifulSoup pulls the price out of the HTML\nwe store every price in a small SQLite table\nand send an email when the price drops below your target"
        }
      ]
    },
    {
      "id": "Tq4LmZ8sV0c",
      "title": "Scrapy vs Playwright vs BeautifulSoup: which Python scraper should you use?",
      "url": "https://www.youtube.com/watch?v=Tq4LmZ8sV0c",
      "viewCount": 88300,
      "date": "2026-07-02",
      "likes": 3410,
      "commentsCount": 198,
      "duration": "16:42",
      "channelName": "DataLane",
      "channelUrl": "https://www.youtube.com/@datalane",
      "numberOfSubscribers": 126000,
      "channelTotalVideos": 212,
      "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
      "text": "Three Python scraping tools compared on the same site: speed, JavaScript support and how much code each needs. Which one should a beginner learn first?"
    },
    {
      "id": "Hy7cXe2KkPs",
      "title": "Playwright Python Tutorial: Scrape JavaScript Websites",
      "url": "https://www.youtube.com/watch?v=Hy7cXe2KkPs",
      "viewCount": 57900,
      "date": "5 months ago",
      "likes": 1890,
      "commentsCount": 76,
      "duration": "19:57",
      "channelName": "Automate Everything",
      "channelUrl": "https://www.youtube.com/@automateeverything",
      "numberOfSubscribers": 212000,
      "channelTotalVideos": 318,
      "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
      "text": "When requests and BeautifulSoup return an empty page, the site renders with JavaScript. This tutorial shows how Playwright for Python opens a real browser, waits for the content and extracts it."
    },
    {
      "id": "Lf0BeatsM1x",
      "title": "lofi beats to code to - 3 hour mix",
      "url": "https://www.youtube.com/watch?v=Lf0BeatsM1x",
      "viewCount": 902000,
      "date": "1 year ago",
      "likes": 21400,
      "commentsCount": 640,
      "duration": "3:02:11",
      "channelName": "Chill Hours",
      "channelUrl": "https://www.youtube.com/@chillhours",
      "numberOfSubscribers": 1450000,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
      "text": "Three hours of calm lofi beats for coding and studying."
    },
    {
      "id": "Sh0rtPy1lnR",
      "title": "One-line Python scraper #shorts",
      "url": "https://www.youtube.com/shorts/Sh0rtPy1lnR",
      "viewCount": 120500,
      "date": "2 weeks ago",
      "likes": 6100,
      "duration": "0:45",
      "channelName": "Byte Sized Dev",
      "numberOfSubscribers": 8900,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial"
    },
    {
      "url": "https://www.youtube.com/watch?v=Xx0Unavail1",
      "input": "https://www.youtube.com/watch?v=Xx0Unavail1",
      "error": "VIDEO_UNAVAILABLE",
      "note": "Video unavailable"
    }
  ]
}
```

# Actor output Schema

## `qualified` (type: `string`):

All qualified videos as JSON, best-scoring first.

## `sheet` (type: `string`):

The same videos as a spreadsheet.

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

JSON record with the funnel (loaded -> filtered -> AI-assessed -> qualified), per-reason skip counts, charged events by type, free-tier videos used and remaining, whether a budget limit was reached, webhook delivery counts, the dedupe store size, the measured seconds per phase (timing) and the top patterns.

## `digest` (type: `string`):

Slack / e-mail-ready summary of the run: how many new and hot videos, the top 10 with links and why they matter.

## `patterns` (type: `string`):

Free rules-only analysis of the videos that passed your filters: outlier rate by format, title hook, duration bucket and title word, with 3-5 plain-language insights.

# 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 = {
    "sourceMode": "list",
    "researchBrief": "Beginner-friendly Python web scraping tutorials (requests, BeautifulSoup, Scrapy, Playwright) that teach a complete project step by step",
    "searchQueries": [
        "python web scraping tutorial",
        "beautifulsoup tutorial",
        "scrapy tutorial for beginners"
    ],
    "maxResultsPerQuery": 30,
    "maxDiscoveryChargeUsd": 0.5,
    "discoveryInput": {},
    "proxyConfiguration": {
        "useApifyProxy": false
    },
    "itemsList": [
        {
            "id": "k3Vq9TzP1aE",
            "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
            "url": "https://www.youtube.com/watch?v=k3Vq9TzP1aE",
            "thumbnailUrl": "https://i.ytimg.com/vi/k3Vq9TzP1aE/maxresdefault.jpg",
            "viewCount": 312450,
            "date": "8 months ago",
            "likes": 9870,
            "commentsCount": 412,
            "duration": "24:18",
            "channelName": "Code with Mira",
            "channelUrl": "https://www.youtube.com/@codewithmira",
            "numberOfSubscribers": 48200,
            "channelTotalVideos": 96,
            "channelLocation": "Canada",
            "isMonetized": true,
            "commentsTurnedOff": false,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
            "text": "Learn web scraping with Python from zero: we download a page with requests, parse it with BeautifulSoup and save the results to CSV. Perfect if you have never scraped a website before.\n\n00:00 Intro\n01:12 Installing requests and BeautifulSoup\n05:40 Finding elements with CSS selectors\n14:05 Handling pagination\n21:30 Saving to CSV\n\n#python #webscraping #beautifulsoup",
            "subtitles": [
                {
                    "language": "en",
                    "type": "auto_generated",
                    "srt": "1\n00:00:01,000 --> 00:00:04,500\nin this video we build a web scraper in Python from scratch\n\n2\n00:01:12,000 --> 00:01:16,000\nfirst install requests and BeautifulSoup with pip\n\n3\n00:05:40,000 --> 00:05:45,000\nBeautifulSoup lets you find elements with CSS selectors\n\n4\n00:14:05,000 --> 00:14:09,000\nto handle pagination we follow the next page link in a loop\n\n5\n00:21:30,000 --> 00:21:35,000\nfinally we write every product row to a CSV file"
                }
            ]
        },
        {
            "id": "Rw8nD2xYp4Q",
            "title": "Build a Price Tracker in Python (requests + BeautifulSoup) - Full Project",
            "url": "https://www.youtube.com/watch?v=Rw8nD2xYp4Q&list=PLx9sampleplaylist&index=3",
            "viewCount": 41700,
            "date": "3 weeks ago",
            "likes": 2150,
            "commentsCount": 133,
            "duration": "38:05",
            "channelName": "Byte Sized Dev",
            "channelUrl": "https://www.youtube.com/@bytesizeddev",
            "numberOfSubscribers": 8900,
            "channelTotalVideos": 41,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
            "text": "A complete beginner project: a Python price tracker that checks a product page every day and emails you when the price drops. Step by step with requests, BeautifulSoup and a scheduler.\n\n00:00 What we build\n03:20 Scraping the price\n17:45 Storing history\n29:10 Email alerts",
            "subtitles": [
                {
                    "language": "en",
                    "type": "auto_generated",
                    "srt": "today we build a complete price tracker project step by step\nwe fetch the product page with requests\nthen BeautifulSoup pulls the price out of the HTML\nwe store every price in a small SQLite table\nand send an email when the price drops below your target"
                }
            ]
        },
        {
            "id": "Tq4LmZ8sV0c",
            "title": "Scrapy vs Playwright vs BeautifulSoup: which Python scraper should you use?",
            "url": "https://www.youtube.com/watch?v=Tq4LmZ8sV0c",
            "viewCount": 88300,
            "date": "2026-07-02",
            "likes": 3410,
            "commentsCount": 198,
            "duration": "16:42",
            "channelName": "DataLane",
            "channelUrl": "https://www.youtube.com/@datalane",
            "numberOfSubscribers": 126000,
            "channelTotalVideos": 212,
            "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
            "text": "Three Python scraping tools compared on the same site: speed, JavaScript support and how much code each needs. Which one should a beginner learn first?"
        },
        {
            "id": "Hy7cXe2KkPs",
            "title": "Playwright Python Tutorial: Scrape JavaScript Websites",
            "url": "https://www.youtube.com/watch?v=Hy7cXe2KkPs",
            "viewCount": 57900,
            "date": "5 months ago",
            "likes": 1890,
            "commentsCount": 76,
            "duration": "19:57",
            "channelName": "Automate Everything",
            "channelUrl": "https://www.youtube.com/@automateeverything",
            "numberOfSubscribers": 212000,
            "channelTotalVideos": 318,
            "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
            "text": "When requests and BeautifulSoup return an empty page, the site renders with JavaScript. This tutorial shows how Playwright for Python opens a real browser, waits for the content and extracts it."
        },
        {
            "id": "Lf0BeatsM1x",
            "title": "lofi beats to code to - 3 hour mix",
            "url": "https://www.youtube.com/watch?v=Lf0BeatsM1x",
            "viewCount": 902000,
            "date": "1 year ago",
            "likes": 21400,
            "commentsCount": 640,
            "duration": "3:02:11",
            "channelName": "Chill Hours",
            "channelUrl": "https://www.youtube.com/@chillhours",
            "numberOfSubscribers": 1450000,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
            "text": "Three hours of calm lofi beats for coding and studying."
        },
        {
            "id": "Sh0rtPy1lnR",
            "title": "One-line Python scraper #shorts",
            "url": "https://www.youtube.com/shorts/Sh0rtPy1lnR",
            "viewCount": 120500,
            "date": "2 weeks ago",
            "likes": 6100,
            "duration": "0:45",
            "channelName": "Byte Sized Dev",
            "numberOfSubscribers": 8900,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial"
        },
        {
            "url": "https://www.youtube.com/watch?v=Xx0Unavail1",
            "input": "https://www.youtube.com/watch?v=Xx0Unavail1",
            "error": "VIDEO_UNAVAILABLE",
            "note": "Video unavailable"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("rich_minds/youtube-video-research-ai").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 = {
    "sourceMode": "list",
    "researchBrief": "Beginner-friendly Python web scraping tutorials (requests, BeautifulSoup, Scrapy, Playwright) that teach a complete project step by step",
    "searchQueries": [
        "python web scraping tutorial",
        "beautifulsoup tutorial",
        "scrapy tutorial for beginners",
    ],
    "maxResultsPerQuery": 30,
    "maxDiscoveryChargeUsd": 0.5,
    "discoveryInput": {},
    "proxyConfiguration": { "useApifyProxy": False },
    "itemsList": [
        {
            "id": "k3Vq9TzP1aE",
            "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
            "url": "https://www.youtube.com/watch?v=k3Vq9TzP1aE",
            "thumbnailUrl": "https://i.ytimg.com/vi/k3Vq9TzP1aE/maxresdefault.jpg",
            "viewCount": 312450,
            "date": "8 months ago",
            "likes": 9870,
            "commentsCount": 412,
            "duration": "24:18",
            "channelName": "Code with Mira",
            "channelUrl": "https://www.youtube.com/@codewithmira",
            "numberOfSubscribers": 48200,
            "channelTotalVideos": 96,
            "channelLocation": "Canada",
            "isMonetized": True,
            "commentsTurnedOff": False,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
            "text": """Learn web scraping with Python from zero: we download a page with requests, parse it with BeautifulSoup and save the results to CSV. Perfect if you have never scraped a website before.

00:00 Intro
01:12 Installing requests and BeautifulSoup
05:40 Finding elements with CSS selectors
14:05 Handling pagination
21:30 Saving to CSV

#python #webscraping #beautifulsoup""",
            "subtitles": [{
                    "language": "en",
                    "type": "auto_generated",
                    "srt": """1
00:00:01,000 --> 00:00:04,500
in this video we build a web scraper in Python from scratch

2
00:01:12,000 --> 00:01:16,000
first install requests and BeautifulSoup with pip

3
00:05:40,000 --> 00:05:45,000
BeautifulSoup lets you find elements with CSS selectors

4
00:14:05,000 --> 00:14:09,000
to handle pagination we follow the next page link in a loop

5
00:21:30,000 --> 00:21:35,000
finally we write every product row to a CSV file""",
                }],
        },
        {
            "id": "Rw8nD2xYp4Q",
            "title": "Build a Price Tracker in Python (requests + BeautifulSoup) - Full Project",
            "url": "https://www.youtube.com/watch?v=Rw8nD2xYp4Q&list=PLx9sampleplaylist&index=3",
            "viewCount": 41700,
            "date": "3 weeks ago",
            "likes": 2150,
            "commentsCount": 133,
            "duration": "38:05",
            "channelName": "Byte Sized Dev",
            "channelUrl": "https://www.youtube.com/@bytesizeddev",
            "numberOfSubscribers": 8900,
            "channelTotalVideos": 41,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
            "text": """A complete beginner project: a Python price tracker that checks a product page every day and emails you when the price drops. Step by step with requests, BeautifulSoup and a scheduler.

00:00 What we build
03:20 Scraping the price
17:45 Storing history
29:10 Email alerts""",
            "subtitles": [{
                    "language": "en",
                    "type": "auto_generated",
                    "srt": """today we build a complete price tracker project step by step
we fetch the product page with requests
then BeautifulSoup pulls the price out of the HTML
we store every price in a small SQLite table
and send an email when the price drops below your target""",
                }],
        },
        {
            "id": "Tq4LmZ8sV0c",
            "title": "Scrapy vs Playwright vs BeautifulSoup: which Python scraper should you use?",
            "url": "https://www.youtube.com/watch?v=Tq4LmZ8sV0c",
            "viewCount": 88300,
            "date": "2026-07-02",
            "likes": 3410,
            "commentsCount": 198,
            "duration": "16:42",
            "channelName": "DataLane",
            "channelUrl": "https://www.youtube.com/@datalane",
            "numberOfSubscribers": 126000,
            "channelTotalVideos": 212,
            "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
            "text": "Three Python scraping tools compared on the same site: speed, JavaScript support and how much code each needs. Which one should a beginner learn first?",
        },
        {
            "id": "Hy7cXe2KkPs",
            "title": "Playwright Python Tutorial: Scrape JavaScript Websites",
            "url": "https://www.youtube.com/watch?v=Hy7cXe2KkPs",
            "viewCount": 57900,
            "date": "5 months ago",
            "likes": 1890,
            "commentsCount": 76,
            "duration": "19:57",
            "channelName": "Automate Everything",
            "channelUrl": "https://www.youtube.com/@automateeverything",
            "numberOfSubscribers": 212000,
            "channelTotalVideos": 318,
            "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
            "text": "When requests and BeautifulSoup return an empty page, the site renders with JavaScript. This tutorial shows how Playwright for Python opens a real browser, waits for the content and extracts it.",
        },
        {
            "id": "Lf0BeatsM1x",
            "title": "lofi beats to code to - 3 hour mix",
            "url": "https://www.youtube.com/watch?v=Lf0BeatsM1x",
            "viewCount": 902000,
            "date": "1 year ago",
            "likes": 21400,
            "commentsCount": 640,
            "duration": "3:02:11",
            "channelName": "Chill Hours",
            "channelUrl": "https://www.youtube.com/@chillhours",
            "numberOfSubscribers": 1450000,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
            "text": "Three hours of calm lofi beats for coding and studying.",
        },
        {
            "id": "Sh0rtPy1lnR",
            "title": "One-line Python scraper #shorts",
            "url": "https://www.youtube.com/shorts/Sh0rtPy1lnR",
            "viewCount": 120500,
            "date": "2 weeks ago",
            "likes": 6100,
            "duration": "0:45",
            "channelName": "Byte Sized Dev",
            "numberOfSubscribers": 8900,
            "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
        },
        {
            "url": "https://www.youtube.com/watch?v=Xx0Unavail1",
            "input": "https://www.youtube.com/watch?v=Xx0Unavail1",
            "error": "VIDEO_UNAVAILABLE",
            "note": "Video unavailable",
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("rich_minds/youtube-video-research-ai").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 '{
  "sourceMode": "list",
  "researchBrief": "Beginner-friendly Python web scraping tutorials (requests, BeautifulSoup, Scrapy, Playwright) that teach a complete project step by step",
  "searchQueries": [
    "python web scraping tutorial",
    "beautifulsoup tutorial",
    "scrapy tutorial for beginners"
  ],
  "maxResultsPerQuery": 30,
  "maxDiscoveryChargeUsd": 0.5,
  "discoveryInput": {},
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "itemsList": [
    {
      "id": "k3Vq9TzP1aE",
      "title": "Web Scraping with Python - BeautifulSoup Tutorial for Beginners",
      "url": "https://www.youtube.com/watch?v=k3Vq9TzP1aE",
      "thumbnailUrl": "https://i.ytimg.com/vi/k3Vq9TzP1aE/maxresdefault.jpg",
      "viewCount": 312450,
      "date": "8 months ago",
      "likes": 9870,
      "commentsCount": 412,
      "duration": "24:18",
      "channelName": "Code with Mira",
      "channelUrl": "https://www.youtube.com/@codewithmira",
      "numberOfSubscribers": 48200,
      "channelTotalVideos": 96,
      "channelLocation": "Canada",
      "isMonetized": true,
      "commentsTurnedOff": false,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
      "text": "Learn web scraping with Python from zero: we download a page with requests, parse it with BeautifulSoup and save the results to CSV. Perfect if you have never scraped a website before.\\n\\n00:00 Intro\\n01:12 Installing requests and BeautifulSoup\\n05:40 Finding elements with CSS selectors\\n14:05 Handling pagination\\n21:30 Saving to CSV\\n\\n#python #webscraping #beautifulsoup",
      "subtitles": [
        {
          "language": "en",
          "type": "auto_generated",
          "srt": "1\\n00:00:01,000 --> 00:00:04,500\\nin this video we build a web scraper in Python from scratch\\n\\n2\\n00:01:12,000 --> 00:01:16,000\\nfirst install requests and BeautifulSoup with pip\\n\\n3\\n00:05:40,000 --> 00:05:45,000\\nBeautifulSoup lets you find elements with CSS selectors\\n\\n4\\n00:14:05,000 --> 00:14:09,000\\nto handle pagination we follow the next page link in a loop\\n\\n5\\n00:21:30,000 --> 00:21:35,000\\nfinally we write every product row to a CSV file"
        }
      ]
    },
    {
      "id": "Rw8nD2xYp4Q",
      "title": "Build a Price Tracker in Python (requests + BeautifulSoup) - Full Project",
      "url": "https://www.youtube.com/watch?v=Rw8nD2xYp4Q&list=PLx9sampleplaylist&index=3",
      "viewCount": 41700,
      "date": "3 weeks ago",
      "likes": 2150,
      "commentsCount": 133,
      "duration": "38:05",
      "channelName": "Byte Sized Dev",
      "channelUrl": "https://www.youtube.com/@bytesizeddev",
      "numberOfSubscribers": 8900,
      "channelTotalVideos": 41,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
      "text": "A complete beginner project: a Python price tracker that checks a product page every day and emails you when the price drops. Step by step with requests, BeautifulSoup and a scheduler.\\n\\n00:00 What we build\\n03:20 Scraping the price\\n17:45 Storing history\\n29:10 Email alerts",
      "subtitles": [
        {
          "language": "en",
          "type": "auto_generated",
          "srt": "today we build a complete price tracker project step by step\\nwe fetch the product page with requests\\nthen BeautifulSoup pulls the price out of the HTML\\nwe store every price in a small SQLite table\\nand send an email when the price drops below your target"
        }
      ]
    },
    {
      "id": "Tq4LmZ8sV0c",
      "title": "Scrapy vs Playwright vs BeautifulSoup: which Python scraper should you use?",
      "url": "https://www.youtube.com/watch?v=Tq4LmZ8sV0c",
      "viewCount": 88300,
      "date": "2026-07-02",
      "likes": 3410,
      "commentsCount": 198,
      "duration": "16:42",
      "channelName": "DataLane",
      "channelUrl": "https://www.youtube.com/@datalane",
      "numberOfSubscribers": 126000,
      "channelTotalVideos": 212,
      "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
      "text": "Three Python scraping tools compared on the same site: speed, JavaScript support and how much code each needs. Which one should a beginner learn first?"
    },
    {
      "id": "Hy7cXe2KkPs",
      "title": "Playwright Python Tutorial: Scrape JavaScript Websites",
      "url": "https://www.youtube.com/watch?v=Hy7cXe2KkPs",
      "viewCount": 57900,
      "date": "5 months ago",
      "likes": 1890,
      "commentsCount": 76,
      "duration": "19:57",
      "channelName": "Automate Everything",
      "channelUrl": "https://www.youtube.com/@automateeverything",
      "numberOfSubscribers": 212000,
      "channelTotalVideos": 318,
      "fromYTUrl": "https://www.youtube.com/results?search_query=beautifulsoup+tutorial",
      "text": "When requests and BeautifulSoup return an empty page, the site renders with JavaScript. This tutorial shows how Playwright for Python opens a real browser, waits for the content and extracts it."
    },
    {
      "id": "Lf0BeatsM1x",
      "title": "lofi beats to code to - 3 hour mix",
      "url": "https://www.youtube.com/watch?v=Lf0BeatsM1x",
      "viewCount": 902000,
      "date": "1 year ago",
      "likes": 21400,
      "commentsCount": 640,
      "duration": "3:02:11",
      "channelName": "Chill Hours",
      "channelUrl": "https://www.youtube.com/@chillhours",
      "numberOfSubscribers": 1450000,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial",
      "text": "Three hours of calm lofi beats for coding and studying."
    },
    {
      "id": "Sh0rtPy1lnR",
      "title": "One-line Python scraper #shorts",
      "url": "https://www.youtube.com/shorts/Sh0rtPy1lnR",
      "viewCount": 120500,
      "date": "2 weeks ago",
      "likes": 6100,
      "duration": "0:45",
      "channelName": "Byte Sized Dev",
      "numberOfSubscribers": 8900,
      "fromYTUrl": "https://www.youtube.com/results?search_query=python+web+scraping+tutorial"
    },
    {
      "url": "https://www.youtube.com/watch?v=Xx0Unavail1",
      "input": "https://www.youtube.com/watch?v=Xx0Unavail1",
      "error": "VIDEO_UNAVAILABLE",
      "note": "Video unavailable"
    }
  ]
}' |
apify call rich_minds/youtube-video-research-ai --silent --output-dataset

```

## MCP server setup

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

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/Au88c0HczNMLd1dPB/builds/8M5Wft1FNQWiMWdzT/openapi.json
