# YouTube Channel Email Scraper — Creator & Influencer Leads (`foxlabs/youtube-channel-email-scraper`) Actor

Find YouTube creators by keyword or channel list. One lead row per channel: public e-mail and where it was found, website and social links, plus recent-video average and median views, like rate, upload frequency and sponsor signals. No login, no API key.

- **URL**: https://apify.com/foxlabs/youtube-channel-email-scraper.md
- **Developed by:** [Berkan Kaplan](https://apify.com/foxlabs) (community)
- **Categories:** Lead generation, Social media
- **Stats:** 16 total users, 6 monthly users, 91.5% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 channel leads

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 Channel Email Scraper — Creator & Influencer Leads

**foXLabs YouTube series:** [Channel Finder](https://apify.com/foxlabs/youtube-channel-finder) · [Channel Videos Scraper](https://apify.com/foxlabs/youtube-channel-videos-scraper) · [Search Scraper](https://apify.com/foxlabs/youtube-search-scraper) · [Comments Scraper](https://apify.com/foxlabs/youtube-comments-scraper)

📧 Find YouTube creators for a niche and get one lead row per channel: the public e-mail and where it was found, the website and social links — and the figures that tell you whether the channel is worth writing to: median views of recent uploads, like rate, upload frequency and sponsorship signals.
Built for influencer outreach, sponsorship sales, creator CRMs and recurring prospecting. No YouTube API key, no login, no cookies.

### 🔍 What is YouTube Channel Email Scraper — and when should you use it?

Give this Actor **keywords** — it finds the channels YouTube ranks for them and the creators behind the top videos — or **your own channel list**, and it returns one enriched row per channel, read live from what YouTube and the creator show to every visitor: the channel's About panel and links, its latest uploads, and, when YouTube shows no usable address, the creator's own website.

**Use it when you need:** a list of active creators in a niche with a contact address and enough audience data to rank them; contacts and current figures for a channel list you already have; the channels that already work with sponsors; new creators only on every weekly run.

**Use something else when:** you need the address behind YouTube's "View email address" button — it needs a signed-in Google account and a CAPTCHA, and this Actor never logs in (`hasHiddenBusinessEmail` tells you the address exists); channels without contact columns at a lower price ([YouTube Channel Finder](https://apify.com/foxlabs/youtube-channel-finder)); or a channel's videos ([YouTube Channel Videos Scraper](https://apify.com/foxlabs/youtube-channel-videos-scraper)).

### 🤖 Use with AI agents

**Already on the Apify MCP server?** Ask for this Actor by name: `foxlabs/youtube-channel-email-scraper`. Checked on 2026-10-08: the server lists it as the tool `foxlabs--youtube-channel-email-scraper`.

**Your agent can pay for its own runs.** The Actor is pay-per-event only, runs with limited permissions and does not use Standby — Apify's conditions for agentic payments — and Apify Store lists it as eligible for them (read from the Store on 2026-10-08). For an agent with a wallet and no human to sign in, Apify sells a prepaid, spend-capped API token over **x402** at `agi.apify.com`; the terms of that path are Apify's own (see `apify.com/agents.md`). Billing is the same either way: per delivered channel row — more for a row with a public e-mail; status rows are free.

Otherwise paste this into Claude, ChatGPT, Cursor or any MCP-enabled assistant:

```
I want YouTube creator leads from the Apify Actor `foxlabs/youtube-channel-email-scraper`.
Input: `searchKeywords` is a list of niches or topics; `channels` is a list of channel URLs, @handles or channel IDs to enrich. `maxChannels` caps the rows. Filters: `minSubscribers`, `maxSubscribers`, `countries`, `uploadedWithinDays`, `minMedianViews`; `onlyWithEmail: true` delivers only channels with a public e-mail.
Start with: {"searchKeywords":["drone review"],"maxChannels":20,"minSubscribers":10000}
Ask me for the niche, run the Actor, then show the leads as a table: channel, subscribers, median views, e-mail and where it was found.
```

The machine-readable API, MCP config and OpenAPI definition live at `apify.com/foxlabs/youtube-channel-email-scraper.md`.

### 📋 Overview

Everything you need to turn a niche into a list of creators you can reach — in one Actor, with no login, cookies or API key.

**Why pick this Actor:**

- 📧 **Public e-mail with its source** — from the channel description, the channel's links, recent video descriptions or the channel's own website. Each address says where it came from and how far it can be trusted.
- 🔍 **Whose address is it?** — an address seen once in a video description is often a guest's or an agency's: it becomes the row's `email` only when the words in front of it say it is the creator's own, or its domain carries the channel's name; otherwise it is only listed. An address on the channel's website counts only on that site's own domain, or as a free mailbox shown there.
- 📊 **Audience you can judge** — average and median views of the latest uploads, like rate, uploads per month, days since the last upload, share of Shorts. From unrounded counts — and when YouTube's feed does not answer, views and activity still come, rounded, from the channel's uploads list (each row says which).
- 🤝 **Sponsorship signals** — recent videos that name a sponsor, promo codes, affiliate links and the brand domains linked in descriptions.
- 🔎 **Keyword in, leads out** — type a niche; or paste your own channel list.
- 💰 **Pay by what you get** — $0.01 for a lead with a public e-mail, $0.003 for one without. Status rows and filtered-out channels are free.
- 🤖 **Agent-ready** — on the Apify MCP server and open to agentic payments (x402).

### ✨ Features

- 🔑 **Two ways in** — `searchKeywords` (with `discoverFrom`: YouTube's channel search, the creators behind the top videos, or both) and `channels` (URLs, @handles and IDs can be mixed).
- 🧹 **Filters** — `minSubscribers`, `maxSubscribers`, `countries`, `uploadedWithinDays`, `minMedianViews`, `onlyWithEmail`.
- 🌐 **Website look-up** — `scanWebsites` (on by default): when YouTube shows no usable address, the channel's own site is read — its home page and one contact page.
- ✅ **Mail-server check** — `checkEmailDomains` (on by default): a DNS check that the address's domain has mail servers (MX records). Nothing is sent.
- 🔗 **Every link** — website, link-in-bio page, eight social networks, and the rest in `otherLinks`.
- 🔁 **Only new** — `onlyNewChannels` with a `listName`: scheduled runs bring only creators you have not received.

### 🎬 Quick Start

```bash
curl -X POST "https://api.apify.com/v2/acts/foxlabs~youtube-channel-email-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchKeywords":["drone review"],"maxChannels":20,"minSubscribers":10000}'
```

The answer is the dataset itself: 20 creator rows, usually after four to seven seconds of run time.

### 🚀 Getting Started (3 steps)

1. **Name the niche, or paste your list** — type a keyword into **Search keywords**, or channels into **Channels**.
2. **Set the filters and the limit** — subscriber range, countries, recent activity, `onlyWithEmail`; `maxChannels` (default 50) caps the rows.
3. **Run and export** — download the dataset as JSON, CSV, Excel, HTML or XML. For recurring prospecting, tick **Only channels not delivered before**, give the list a name and put the run on a schedule.

### 📥 Input

```json
{"searchKeywords":["drone review"],"maxChannels":20,"minSubscribers":10000}
```

| Field | Type | Description |
|---|---|---|
| `searchKeywords` | array | Niches or topics, one per line. Each brings the channels YouTube ranks for it. |
| `channels` | array | Your own list: channel URLs, @handles or channel IDs. |
| `maxChannels` | integer | How many channel rows to deliver in total (default 50). |
| `discoverFrom` | string | Where a keyword looks: `channels` (YouTube's channel search), `videos` (the creators behind the top videos) or `channels-and-videos` (default). |
| `maxChannelsPerKeyword` | integer | Upper limit per keyword, so several keywords are covered evenly. |
| `minSubscribers` / `maxSubscribers` | integer | Subscriber range (default from 0). |
| `countries` | array | Country names or two-letter codes: `United States`, `US`, `DE`. |
| `uploadedWithinDays` | integer | Only channels whose latest upload is this recent. |
| `minMedianViews` | integer | Only channels whose recent uploads reach this median. |
| `onlyWithEmail` | boolean | Deliver a channel only if a public e-mail was found. Default off. |
| `scanWebsites` | boolean | When YouTube shows no usable address, look on the channel's own website. Default on. |
| `checkEmailDomains` | boolean | DNS check that the address's domain accepts mail. Default on. |
| `onlyNewChannels` | boolean | Remember delivered channels under `listName` and skip them in later runs. Default off. |
| `listName` | string | Name of that memory: letters, digits and hyphens, up to 40 characters (default `default`). |
| `proxyConfiguration` | object | Nothing to change for normal use: requests go out directly, and a kind of request YouTube refuses is moved to Apify Proxy by the Actor itself. |

Give keywords, channels, or both. Channels that do not pass a filter are not delivered and not charged.

**More recipes**

- Reachable creators in a niche — active channels with a real audience and a public e-mail: `{"searchKeywords":["skincare routine","drone review"],"maxChannels":100,"minSubscribers":10000,"uploadedWithinDays":30,"onlyWithEmail":true}`
- Enrich a list you already have: `{"channels":["https://www.youtube.com/@mkbhd","@jameshoffmann","UCHnyfMqiRRG1u-2MsSQLbXA"],"maxChannels":100}`
- Micro-creators in two countries, new ones only on every weekly run: `{"searchKeywords":["home workout"],"maxChannels":200,"minSubscribers":5000,"maxSubscribers":100000,"countries":["US","GB"],"onlyNewChannels":true,"listName":"home-workout-q4"}`

### 📤 Output

One row per channel (`type: "lead"`). A line that could not be delivered — a channel that does not exist, a keyword without results, a channel line that was left for a later run because YouTube did not give its recent videos (see the FAQ) — comes back as a free row with `type: "status"` and the reason in `error`, so no line of your input disappears silently. A run that delivers nothing and wrote no such row — its filters removed every channel, or the channels a keyword found were left for a later run — writes one with the run's last word, so a dataset never comes back empty without a reason (an "only new" run that finds nothing new is the exception). Every row has the same 58 columns.

| Field | Description |
|---|---|
| `type` | `lead`, or `status` |
| `channelId` / `channelUrl` / `handle` / `title` | The channel. `channelId` is the stable key |
| `description` | The channel's full description |
| `country` / `countryCode` / `joinedDate` | Country as the channel states it and its ISO code (`null` when the channel states none); when the channel was created |
| `subscribers` / `subscribersText` | Subscriber count as YouTube publishes it (rounded: "14.4K" → 14400); `null` when YouTube shows no count, never 0 |
| `totalViews` / `videoCount` | Lifetime views and number of videos |
| `email` | The best public address: never a low-confidence one, and never one whose domain the DNS check found without mail servers. `null` when there is none |
| `emailSource` / `emailConfidence` | `channel-description`, `channel-link`, `video-description` or `website` · `high` or `medium` (see *How e-mails are judged*). In a row read from the uploads list, `video-description` means the page of an upload the Actor opened: two or three of the channel's latest fifteen, long ones first (see *How e-mails are judged*) |
| `emailDomainHasMx` | `true` when the DNS check found mail servers for the address's domain; `null` when the check is off or DNS gave no answer |
| `emails` / `emailsFound` | Up to 5 addresses, best first: `email`, `source`, `sourceUrl`, `mentions`, `confidence`, `isFreeMailbox`, `domainHasMx` — low-confidence addresses appear only here; and how many different addresses were found in total |
| `hasHiddenBusinessEmail` | The channel has an e-mail behind YouTube's "View email address" button. The Actor does not open it; the flag tells you it exists |
| `website` / `websiteScan` | The creator's own site as far as the channel's links tell, and what happened there: `ok`, `no-email`, `not-own-site`, `unreachable`, `not-needed`, `off`, `no-website` |
| `contactPageUrl` | A contact or about page found on that site (looked for only when the site is opened) |
| `linkPage` | Link-in-bio page (Linktree, Beacons, …) |
| `instagram` / `tiktok` / `twitter` / `facebook` / `linkedin` / `discord` / `twitch` / `patreon` | Profile links the channel lists |
| `otherLinks` | Every other link the channel lists (`title`, `url`) |
| `recentVideosAnalyzed` / `recentVideosSource` | How many recent uploads the figures below are based on (the latest 15 at most) · where they were read: `feed` (the channel's public feed: unrounded counts, exact times) or `uploads list` (the channel's uploads list, read when the feed does not answer: rounded views, ages as YouTube words them, no likes — see *When YouTube's feed fails*). When neither answered both are `null`, and so are the figures below: not known, which is not the same as none — such a row is delivered only when it carries an e-mail. A channel without public videos shows `0` and no source |
| `avgViewsRecent` / `medianViewsRecent` / `avgLikesRecent` | Over those uploads, from unrounded counts. From the uploads list the views are YouTube's rounded figures ("9.8M views") and `avgLikesRecent` is `null` |
| `likeRate` / `viewsPerSubscriber` / `shortsShareRecent` | Likes ÷ views (0.02 = 2%) · median recent views ÷ subscribers · share of those uploads that are Shorts (0–1). `likeRate` and `shortsShareRecent` are `null` from the uploads list |
| `lastUploadAt` / `daysSinceLastUpload` | The latest upload. From the uploads list `lastUploadAt` is `null`, and `daysSinceLastUpload` is within a day for an upload of the last two weeks, whose age YouTube words in days, and an estimate beyond — the middle of the days the wording covers ("3 weeks ago" → 24); `null` unless each of those uploads shows an age (a channel that uploads Shorts only shows none) |
| `uploadsLast30Days` / `uploadsPerMonth` / `recentVideosSpanDays` | Activity. `uploadsLast30Days` counts within the latest 15 uploads, so it tops out at 15; from the uploads list it counts the uploads YouTube words as less than a month old, and the other two rest on estimated ages — they are `null` when the newest and the oldest of the uploads are worded alike in weeks, months or years ("2 years ago"), which does not tell them apart, and all three are `null` unless each upload shows an age |
| `latestVideo` / `topRecentVideo` | `videoId`, `title`, `url`, `publishedAt`, `views`, `likes`, `isShort` — handy for a personal first line. From the uploads list `views` is rounded and `publishedAt`, `likes` and `isShort` are `null` |
| `sponsorMentionsRecent` / `promoCodes` / `hasAffiliateLinks` | Recent videos whose description names a sponsor or a paid partnership · promo codes in those descriptions · affiliate short links in them. `sponsorMentionsRecent` and `hasAffiliateLinks` are `null` when the feed did not answer, and `promoCodes` is then an empty list — not read, which is not "none" |
| `domainsLinkedInRecentVideos` | Brand and tool domains linked in those descriptions, with the number of videos (`domain`, `videos`), the 15 most linked at most; the channel's own places, social networks, stores and short links are left out. (`promoCodes` holds 10 codes at most.) An empty list as well when the feed did not answer |
| `discoveredBy` / `searchKeyword` / `searchRank` / `input` | How the channel got here: `channel-search`, `video-search` or `input` |
| `scrapedAt` / `error` | When the row was made · `null` on lead rows, the reason on status rows |

Sample — a row from a platform run on 2026-10-07 as the Actor returned it, with two things done to it here: the local part of the e-mail address is hidden, and the column added on 2026-10-08, `recentVideosSource`, is filled in with what it says for a row read from the feed (58 columns in all):

```json
{
  "type": "lead",
  "channelId": "UCX-CcKu2jwiwqXQmCiKjRRA",
  "channelUrl": "https://www.youtube.com/channel/UCX-CcKu2jwiwqXQmCiKjRRA",
  "handle": "@rooiwhoo",
  "title": "Rooi",
  "description": "life amirite\n",
  "country": "United States",
  "countryCode": "US",
  "joinedDate": "2023-10-25",
  "subscribers": 31900,
  "subscribersText": "31.9K subscribers",
  "totalViews": 733093,
  "videoCount": 21,
  "email": "b…@rooiwhoo.com",
  "emailSource": "website",
  "emailConfidence": "high",
  "emailDomainHasMx": true,
  "emails": [{ "email": "b…@rooiwhoo.com", "source": "website", "sourceUrl": "https://rooiwhoo.com/pages/contact", "mentions": null, "confidence": "high", "isFreeMailbox": false, "domainHasMx": true }],
  "emailsFound": 1,
  "hasHiddenBusinessEmail": true,
  "website": "https://www.rooiwhoo.com/",
  "linkPage": "https://bio.site/rooiwhoo",
  "instagram": "https://www.instagram.com/rooiwhoo",
  "tiktok": "https://www.tiktok.com/@rooiwhoo",
  "twitter": "https://www.x.com/rooiwhoo",
  "facebook": null,
  "linkedin": null,
  "discord": null,
  "twitch": null,
  "patreon": null,
  "otherLinks": [],
  "websiteScan": "ok",
  "contactPageUrl": "https://rooiwhoo.com/pages/contact",
  "recentVideosAnalyzed": 15,
  "recentVideosSource": "feed",
  "lastUploadAt": "2026-09-03T03:07:52+00:00",
  "daysSinceLastUpload": 34,
  "uploadsLast30Days": 0,
  "uploadsPerMonth": 1.2,
  "recentVideosSpanDays": 342,
  "avgViewsRecent": 44168,
  "medianViewsRecent": 36344,
  "avgLikesRecent": 2494,
  "likeRate": 0.0565,
  "viewsPerSubscriber": 1.139,
  "shortsShareRecent": 0,
  "latestVideo": { "videoId": "WK7Jm2ve-yg", "title": "Half a year making my first real game", "url": "https://www.youtube.com/watch?v=WK7Jm2ve-yg", "publishedAt": "2026-09-03T03:07:52+00:00", "views": 99501, "likes": 2906, "isShort": false },
  "topRecentVideo": { "videoId": "9mxoLv_61xU", "title": "Chasing the indie game dev dream… as a beginner. | Making My First Real Game", "url": "https://www.youtube.com/watch?v=9mxoLv_61xU", "publishedAt": "2025-12-24T16:00:18+00:00", "views": 230389, "likes": 12892, "isShort": false },
  "sponsorMentionsRecent": 0,
  "hasAffiliateLinks": false,
  "promoCodes": [],
  "domainsLinkedInRecentVideos": [{ "domain": "thmatc.co", "videos": 7 }, { "domain": "figma.com", "videos": 3 }, { "domain": "apple.com", "videos": 1 }, { "domain": "nintendo.com", "videos": 1 }],
  "discoveredBy": "channel-search",
  "searchKeyword": "indie game dev",
  "searchRank": 4,
  "input": null,
  "scrapedAt": "2026-10-07T14:03:14.321Z",
  "error": null
}
```

### 🧠 How e-mails are judged

The Actor reads four places, all of them public, and says which one an address came from:

1. **Channel description** and **channel links** — `high`. The creator wrote the address on the channel itself.
2. **Recent video descriptions** (the latest 15 uploads) — `high` when the address is in three or more of them, or in two and introduced as the contact ("business inquiries: …"); `medium` when it is in two, or in one and the words in front of it say it is the creator's own ("business inquiries: …", "e-mail me at …") or its domain carries the channel's name; `low` otherwise. A lone address is often somebody else's — a guest's, a travel agency's, a booking office's — so a `low` address is listed in `emails` but never becomes the row's `email`. When YouTube's feed does not answer and the channel itself shows no address that counts, the Actor opens video pages instead: of the channel's latest fifteen uploads, the two newest that run longer than three minutes (shorter ones when there are not two), and a third while an address was seen that does not count yet. The same rule is applied to those two or three descriptions — so an address the feed's fifteen would make `high` can come out `medium`, and one that stands only under other uploads is not found.
3. **The website the channel links to** — only when YouTube shows no address that counts for the row (a `low` one does not, nor one whose domain the DNS check found without mail servers). The home page is read and, if it shows no address, one contact page. An address counts only if the page belongs to the channel (the linked domain, or a domain carrying the channel's name) **and** the address is on that same domain, or is a free mailbox shown on that site. It is `high` when its domain carries the channel's name, `medium` otherwise. In a 1,000-channel run on 2026-10-07, 31 of the 106 addresses seen on creators' websites were on some other domain than the site's own (a site also shows the addresses of its platform, its theme's developer, its partners). Those are not delivered; `websiteScan: "not-own-site"` tells you when the link itself led elsewhere.

Only addresses written out plainly are read. One that is spelled in words ("name at domain dot com"), protected by a script, or encoded in the page source was hidden from harvesting by its owner, and is left alone.

**How channels are found.** For a keyword the Actor asks YouTube's channel search and its video search for page after page (about 20 results each) until it has `maxChannels` rows or the results end (a pass is 60 pages at most), and then follows the search again — up to five passes — while a pass still brings enough channels the earlier ones did not: at least 10, and at least 5% of what that search had brought before. A run whose filters let almost nothing through stops after reading 50 channels per requested row (at least 300) — and so does a run in which channels are being left for a later run because YouTube did not give their recent videos.

### 🧪 Data quality — measured

Measured on the Apify platform on 2026-10-07.

| | Two niches, 10,000+ subscribers (100 channels) | Eight niches, no filter (1,000 channels) |
|---|---|---|
| Public e-mail (`email`) | 53% | 35% |
| Country | 88% | 75% |
| Subscriber count | 100% | 96% (YouTube shows none for the rest — mostly channels with no subscribers yet) |
| Recent-video figures (`medianViewsRecent`, `likeRate`) | 100% | 99% |
| At least one link of any kind | 86% (a website or one of the eight social profiles: 82%) | 62% (56%) |
| `contactPageUrl` | 15% | 11% |
| Unique channels | 100 of 100 | 1,000 of 1,000 |

- **Where the addresses came from.** First column, 53 addresses: channel description 26, recent video descriptions 20, the channel's website 7. Second column, 348: channel description 191, video descriptions 88, website 61, channel links 8. Every one of these row e-mails is on a domain with mail servers — by construction: an address whose domain shows none is listed in `emails` only, which happened to 1 of the 60 addresses found in the first run and 3 of the 409 in the second.
- **Hidden e-mail.** 80 of the 100 channels keep an address behind YouTube's "View email address" button; for 48 of the 80 the Actor found a public address anyway (603 and 315 in the 1,000-channel run).
- **Small channels rarely publish an address.** In the unfiltered run 264 channels had under 1,000 subscribers and 18 of them had a public e-mail. `minSubscribers` keeps the list relevant.
- **Checked against the source.** 25 rows were read again from YouTube after the run: handle, country, subscriber count, joining date, the hidden-e-mail flag, title, number of recent videos and latest video were identical in all 25 (200 of 200 values), and each of the 12 row e-mails that had come from YouTube's own pages was found there again. Of 25 URLs taken from rows, 25 led to what the row says: 10 of 10 video links matched title and channel, 10 of 10 website-sourced addresses were on the page they name, 5 of 5 contact pages existed.
- **Whose address is it — video descriptions.** Of the 88 row e-mails that came from video descriptions, 74 appear in four or more of the channel's 15 latest videos and 12 in two or three. Two rest on a single video; we read both, and the words in front say they are the creator's own.
- **Whose address is it — websites.** Of the 61 website-sourced row e-mails, 34 are on a domain that carries the channel's name (`high`); 15 are on the linked site's own domain and 12 are free mailboxes shown on that site (`medium`).
- **Websites.** In the 1,000-channel run the Actor opened the websites of 213 delivered channels with 292 page requests: 259 answered directly, 8 more on a second route, 25 got no answer (refusals, expired certificates, dead domains). 26 rows carry `websiteScan: "unreachable"`; they are delivered with everything YouTube shows.
- **When YouTube's feed fails.** The figures above are from a day on which the feeds answered. On 2026-10-08 YouTube's feed service answered "not found" or "server error" for channels that have a feed: by our run records from 01:25 UTC at the latest and until some time between 06:47 UTC, when our last run with failing feeds ended, and 09:48, when all 40 feeds we asked for answered again. We asked the feeds of 40 channels that had answered the day before, up to six times each: at 02:01 UTC 8 answered at the first request and 25 within six; at 02:25, 14 and 39; at 02:58, none and 2; at 04:56, none at all. At 03:02, 20 of those channels asked from eight addresses gave 13 feeds in 312 answers — 10 of the 20 from at least one address, 10 from none — and single requests from outside Apify, made through a web tool at about 04:58, 05:54 and 06:55, got "not found" as well. Others describe the same fault of this endpoint as recurring, in windows of hours, since December 2025 (threads at n8n and Google's developer forum, an issue at Miniflux, a pull request at RSS-Bridge, as we read them on 2026-10-08). In the 1,000-channel run of 2026-10-07 every one of the 993 channels that have videos had come with its feed's figures. The Actor therefore asks a feed again (see the FAQ), and for a channel whose feed does not come it reads the channel's uploads list and, for an address, the pages of two or three of its latest uploads instead.
- **What the uploads list gives instead.** At 05:19 UTC that day, with the feeds still failing, the uploads list of each of those 40 channels answered (read locally, through a proxy — not in a platform run). It holds a channel's latest uploads of every kind, 100 at most: on three channels we checked, their newest videos and Shorts — and one channel's past streams — stood in it. Against the feeds recorded the day before: median views within ±10% for 38 of the 40 channels (the list shows rounded figures: "9.8M views"). For the 35 channels whose 15 newest uploads were the same in both — 33 of them with ages in the list: the number of uploads in the last 30 days was the same for all 33; days since the last upload the same for all 10 that had uploaded within two weeks, and within ±15 days for 24 of the 33 (the estimate is rough for a channel that has been silent for long: the largest difference was 163 days); uploads per month within ±25% for 24 of the 31 for which both sources give one. Two of the 40 upload Shorts only: their list shows views but no ages, so their activity columns stay empty. In a second comparison, made at one moment (10:05 UTC, 120 channels, the feeds answering again and the list read in a local test run), median views were within ±10% for 118 of 119 channels. The list carries no likes and no descriptions: the like columns, `sponsorMentionsRecent` and `hasAffiliateLinks` are `null`, and `promoCodes` and `domainsLinkedInRecentVideos` are empty lists. On the platform, during the failure, the prefilled input brought 19 of its 20 rows with figures read from the list (build 0.1.25): all 19 with view figures, 18 with activity figures.
- **E-mails without the feed.** In the two runs of 2026-10-07 counted above, 20 of 53 and 88 of 348 row e-mails had come from video descriptions, which the feed carries. Without the feed, and when the channel itself shows no address that counts, such an address is looked for on video pages: of the channel's latest fifteen uploads the two newest that run longer than three minutes (shorter ones when there are not two), and a third while an address was seen that does not count yet. Offline, on 762 channels recorded on 2026-10-07 whose own description and links gave no usable address, that found 84 of the 106 row e-mails the feed's fifteen descriptions give (another address in 3 cases), at 1.98 pages a channel — there, a long video was one the feed does not mark as a Short. Live on 2026-10-08 — a local test run through a proxy, website look-ups off, the feed made to fail for 120 channels that had just been read with it — it found 12 of 15 and took no other address, at 1.92 pages for each of the 98 channels without an address of their own; the other 105 rows carried the same e-mail, or none, as with the feed. On the platform, two test builds with this code (0.1.26 and 0.1.27) were told to treat the feed as unanswered and each read again the 60 channels a run had just found and read with their feeds: of the 16 row e-mails that had come from video descriptions, 15 were found again (8 of 8, then 7 of 8) — 11 as `high` and 4 as `medium`, where the feed had made all 16 `high` — and no other address was taken. The one that was missed stood in 8 of the feed's 15 descriptions and under neither of the two uploads opened; the others stood in 4 to 15. The other 52 rows of each run carried the same e-mail, or none; 94 and 95 pages were opened for the 49 channels without an address of their own, and no third page was needed. Not measured: this build during a real failure (the feeds had come back when it was built), and a run that opens more than 188 video pages — see *Performance* for what limits them.

### ⏱️ Performance — measured

A channel costs two small YouTube requests (its About panel and its RSS feed; a feed that does not come is asked up to five more times, then the channel's uploads list once, and up to three video pages when the channel itself shows no address that counts) — one more for a channel you give by @handle or address, to look it up, and one more for a channel that neither its feed nor the search names (nor its uploads list, when that was read) — plus up to two pages of its website when YouTube shows no usable address. On the platform with the default 1,024 MB, 2026-10-07:

- 20 channels for one keyword with a subscriber filter (the prefilled input): **4 to 7 seconds** of run time in nine of ten runs, 11.5 in the tenth.
- While YouTube's feed is failing a run takes longer: every channel's feed is asked again, its uploads list is a far larger answer than the feed, and up to three video pages are opened for a channel that shows no address of its own. During the failure of 2026-10-08 the prefilled input took 11 seconds on the platform, and a 256 MB run of 30 channels 27 seconds with a memory peak of 187 MB (build 0.1.25, which did not open video pages yet). In two platform test runs with the feed made to fail (test builds 0.1.26 and 0.1.27, website look-ups off), 60 channels took 24 and 25 seconds — the runs that had found and read them with their feeds took 6 — with memory peaks of 170 and 152 MB; with 256 MB, 30 channels took 14 and 23 seconds (peaks 141 and 128 MB). For this build during a real failure, when every feed is first asked again over the network, we have no figure.
- YouTube limits video pages per address: our [YouTube Channel Videos Scraper](https://apify.com/foxlabs/youtube-channel-videos-scraper), which reads the same pages, measured roughly a thousand in a row before an address was refused for a while — some addresses much sooner — and about a page a second through Apify Proxy afterwards. Our largest test of this Actor opened 188 video pages in one run, and none was refused in any test; a run of a thousand channels during a failure would open about 1,500 (an estimate: about 800 of them show no address of their own, at 1.9 pages each), which we have not measured. The Actor moves a refused kind of request to Apify Proxy by itself; a page that still does not come is left out, and the run's status message says how many pages could not be read.
- 1,000 channels for eight keywords: **69 to 81 seconds** in three runs — 2,073 to 2,087 requests to YouTube, none refused or retried, plus 312 to 331 requests to creators' websites.
- One keyword followed to the end of YouTube's results ("drone review", 10,000+ subscribers): 301 channels in 91 seconds out of the 870 the searches brought, and in a second run 293 in 92 seconds out of 861.

A run the platform restarts or moves to another server continues from the rows it already delivered. Two test runs of 300 channels were restarted on purpose part-way (after 222 and after 230 rows) and both ended with 300 different channels; in an earlier test run of 1,000 channels the platform itself moved the run to another server three times and it still ended with 1,000 different channels.

### 💼 Use cases

**1. Influencer outreach** — creators you can actually write to.
*Input:* your niche keywords with `minSubscribers`, `uploadedWithinDays: 30` and `onlyWithEmail`. *Output:* active channels with a public e-mail, its source, and median views to rank them. *Use:* a ready outreach list; `latestVideo` gives you a personal first line.

**2. Sponsorship sales** — channels that already work with brands.
*Input:* topic keywords. *Output:* `sponsorMentionsRecent`, `promoCodes`, `hasAffiliateLinks` and the brand domains each channel links. *Use:* pitch the creators who run sponsored content, and see which competitors they promote.

**3. Creator CRMs and marketplaces** — enrich a list you already have.
*Input:* `channels` with your URLs, handles or IDs. *Output:* contacts, links and current activity per channel, keyed by `channelId`. *Use:* refresh your database on a schedule.

**4. Recurring prospecting** — new creators every week.
*Input:* the same keywords with `onlyNewChannels` and a `listName`, scheduled weekly. *Output:* only creators you have not received. *Use:* a steady feed into your pipeline.

**5. Market mapping** — who covers a topic.
*Input:* several keywords, no filters. *Output:* every channel with size, country, upload rate and links. *Use:* a picture of the niche before you plan a campaign.

### 🔗 Integration

**JavaScript / Node.js**

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('foxlabs/youtube-channel-email-scraper').call({"searchKeywords":["drone review"],"maxChannels":20,"minSubscribers":10000});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((row) => row.type === 'lead' && row.email));
```

**Python**

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('foxlabs/youtube-channel-email-scraper').call(run_input={"searchKeywords": ["drone review"], "maxChannels": 20, "minSubscribers": 10000})
for row in client.dataset(run.default_dataset_id).iterate_items():
    if row['type'] == 'lead':
        print(row['title'], row['subscribers'], row['email'])
```

Written for `apify-client` 3 for Python, where `call()` returns a run object; with version 2 it returns a dict — write `run['defaultDatasetId']` there.

**Automation (n8n / Zapier / Make):** schedule or webhook → run the Actor with your `searchKeywords` and `onlyNewChannels` → handle the JSON dataset → push new leads to a sheet, a CRM or an outreach tool.

### 📊 Pricing

Pay-per-event. Every delivered row carries exactly one of two events:

| Event | When | Price |
|---|---|---|
| `channel-lead-with-email` | the row has a public e-mail (`email` is filled) | $0.01 a row ($10 per 1,000) |
| `channel-lead` | the row has none | $0.003 a row ($3 per 1,000) |

Plus Apify's run-start event ($0.00005 a run, also for a run that delivers nothing; Apify counts this event per GB of run memory, and this Actor runs with 1 GB at most). A channel that has videos is delivered — and charged — only when the Actor could read its recent videos or found an e-mail for it: while YouTube's feed is failing, a channel that gave neither is left out, not charged, and not remembered by a list. (A channel without public videos is delivered, with `recentVideosAnalyzed: 0`.) [Current pricing](https://apify.com/foxlabs/youtube-channel-email-scraper).

**Not charged:** status rows, channels that did not pass your filters, channels that could not be read, channels left for a later run, and anything `onlyWithEmail` leaves out.

**Spending limit.** Set a maximum cost per run in the run options. Before every row the Actor checks that it still fits; when the next row would not, it stops delivering, keeps what it has delivered, and says so in its status message.

### ❓ FAQ

**Where do the e-mails come from?** From the channel description, the channel's link list, the descriptions of its latest uploads and — when none of those has a usable address — the channel's own website. Every address carries its source.

**Why does a channel have `hasHiddenBusinessEmail: true` but no `email`?** The creator stored an address behind YouTube's "View email address" button, and the Actor found no usable address in the public places it reads: the channel's description and links, its recent video descriptions, its own website (a link-in-bio page is not opened). The Actor does not open that button.

**How many channels does a keyword bring?** It depends on the keyword, and on YouTube: the same search returns a different part of its results every time. On 2026-10-07 "drone review", followed to the end, brought 870 different channels. The channel search added 412 on its first pass and 67, 72 and 12 on the next three; the video search 281, then 39 and 9 (the passes add up to 892: a channel both searches brought is one channel). Of the 870, 301 had 10,000 or more subscribers. Use several keywords for a larger list.

**Are the view counts exact?** In a row with `recentVideosSource: "feed"` they are not rounded: the channel's public feed carries views and likes to the last digit. The feed can run a little behind the live counter, mostly for videos published in the last few days. In a row with `recentVideosSource: "uploads list"` — read when YouTube's feed does not answer — they are YouTube's rounded figures ("9.8M views"). The subscriber count is always rounded by YouTube.

**What does `uploadsLast30Days: 15` mean?** At least 15: the feed holds the latest 15 uploads. `uploadsPerMonth` gives the rate for very active channels (it is `null` when all 15 uploads fall within one day — and, in a row read from the uploads list, when YouTube words the newest and the oldest of them alike).

**Why does a row say `recentVideosSource: "uploads list"`?** The recent-video figures come from the channel's public feed, and YouTube's feed service does not always answer. On 2026-10-07 it did: in the 1,000-channel run every one of the 993 channels that have videos came with its figures from the feed. In the night of 2026-10-08 it did not: the service answered "not found" or "server error" for channels that do have a feed — by 01:25 UTC, at 04:56 for every one of 40 channels we asked, and until some time between 06:47 and 09:48. How often that happens we cannot say from two days; others describe it as recurring (see *Data quality*). So the Actor asks a feed that does not come up to five more times (once more while the last channels of a run brought next to no feeds), and when it still does not come it reads the channel's uploads list: the same latest uploads, with rounded views and their ages as YouTube words them. Such a row has its view and activity figures (see the output table for how exact each one is); its like columns, `sponsorMentionsRecent` and `hasAffiliateLinks` are `null`, `promoCodes` and `domainsLinkedInRecentVideos` are empty lists, and an address in its video descriptions is looked for on the pages of two or three of its latest uploads instead of in fifteen descriptions. It is charged like any other row, and the run's status message counts those rows. For the feed's unrounded figures, the like and sponsor columns and all fifteen descriptions, give the channel in `channels` in a later run: that is a new run, charged like any other, and a list (`onlyNewChannels`) that has delivered the channel will not deliver it a second time.

**Why are the recent-video figures empty for a channel?** Either the channel has no public videos — it shows `recentVideosAnalyzed: 0` — or neither its feed nor its uploads list answered: `recentVideosAnalyzed` and `recentVideosSource` are `null` then. Such a row is delivered only when it carries an e-mail, and charged as a lead with e-mail; a list remembers it like any other row, so for its figures give the channel in `channels` in a later run without that list — a new run, charged like any other. A channel that has videos and gave neither its recent videos nor an e-mail is not delivered at all: it is left for a later run, not charged, and a list (`onlyNewChannels`) does not remember it, so the list's next run can deliver it whole. A channel line you gave gets a free status row that names it. Channels a keyword found are only counted in the status message: to get just those later, run the same keywords with `onlyNewChannels` and the same `listName` — without a list a later run delivers, and charges, the other channels again. While both sources fail, a keyword run therefore delivers only channels with an e-mail, each charged as a lead with e-mail, until it reaches `maxChannels` or its reading limit; set a spending limit if that is not what you want. We have not seen both fail: during the failure of 2026-10-08 the list answered for 40 of 40 channels in a probe and 19 of 19 in a test run, both made locally through a proxy, and in four platform runs of build 0.1.25 it was asked for 56 channels and 55 rows came with its figures (the 56th, a channel that uploads Shorts only, was turned down by that run's own filter).

**What do my filters do with a channel whose feed did not answer?** `minSubscribers`, `maxSubscribers` and `countries` work as always: they do not need the feed. `uploadedWithinDays` and `minMedianViews` judge such a channel by the figures of its uploads list. A figure that list does not carry — the age of the last upload for a channel that uploads Shorts only, or everything when the list did not answer either — cannot be judged: the channel is left for a later run whether it shows an e-mail or not — not delivered, not charged, and counted in the status message as "left for a later run", not under "no recent upload". If nothing could be delivered and, of the channels that passed your subscriber and country filters, more were left for that reason than were turned down, the run ends as failed and says so: run it again later. Otherwise it ends normally, and its message lists how many channels each reason removed. (A run that skipped channels as already delivered to its list does not end as failed for this.) `onlyWithEmail` judges by what could be read: while the feed is down an address that stands only in video descriptions is looked for on the pages of two or three of the latest uploads (when the uploads list came), and when none is found there or on the channel's website the channel is turned down as "no public e-mail found (YouTube's feed did not answer: its video descriptions could not all be read)" — counted apart from "no public e-mail" in the status message, so you can see how many channels another run may still bring once the feed answers.

**Can I run it on a schedule without getting the same creators again?** Yes. Turn on `onlyNewChannels` and give the campaign a `listName`. Delivered channels are remembered in a key-value store named `youtube-leads-<listName>` in your account. A run that is killed before it could save its list — a timeout, for instance — is made up for: the list's next run first reads back what that run delivered.

**Does it need a proxy or an API key?** Neither. Requests go out directly; a kind of request YouTube refuses is moved to Apify Proxy by the Actor itself, at no extra charge.

**Is a channel without a visible subscriber count delivered?** Yes, with `subscribers: null` — unless you set a subscriber filter, which it cannot pass.

**Can I export to CSV / Excel / JSON?** Yes — directly from the Apify dataset.

### 🐛 Troubleshooting

- **"No channels delivered … did not pass your filters"** — the run's status message, and the one status row in its dataset, list how many channels each filter removed. Loosen the one with the largest number.
- **"For N channels YouTube's feed did not answer: their upload and view figures come from the channel's uploads list instead …"** — those rows carry `recentVideosSource: "uploads list"`: rounded views and estimated activity, no likes, no sponsor signals, and an address from video descriptions only when one of the two or three uploads the Actor opened shows it. They are charged like any other row. Give the same channels in `channels` later for the feed's unrounded figures and all fifteen descriptions: that run is charged again, and a list that has delivered them skips them.
- **"… came without recent-video figures, because neither YouTube's feed nor the channel's uploads list answered …"** — those rows are delivered for their e-mail and charged as leads with e-mail; they carry everything the channel's About panel gives. A list remembers them: for their figures give them in `channels` later, without the list.
- **"N channels were left for a later run: YouTube's feed did not answer, and the uploads list did not give what is needed …"** — those channels are not delivered and not charged: neither source gave their recent videos and no e-mail was found — or one of your filters asks for a figure neither gave, which leaves a channel whether it shows an e-mail or not. A list does not remember them. A channel line you gave has a status row that names it: give those lines again later. For channels a keyword found, run the same keywords later with `onlyNewChannels` and the same `listName`; without a list the later run delivers — and charges — the other channels again.
- **"… no public e-mail found (YouTube's feed did not answer: its video descriptions could not all be read)"** (among the filter reasons) — you asked for `onlyWithEmail`, the feed did not answer, and neither the channel's own description and links, nor — when its uploads list came — the pages of two or three of its latest uploads, nor its website showed an address that counts. Those channels are not delivered and not charged; the status message says how many there were. Run the same input again later.
- **"Nothing delivered … YouTube's feed did not answer, and the uploads list did not give what is needed either"** — of the channels that passed your subscriber and country filters, more were left for a later run (see above) than were turned down. No row is charged; run it again later.
- **"N video pages could not be read: an address that stands only in their descriptions is missing from the rows"** — YouTube limits video pages per address; the Actor moved them to Apify Proxy and these still did not come. The rows went out with what could be read. Give those channels in `channels` again later.
- **"Stopped after reading N channels: too few of them could be delivered"** — the run read 50 channels for every row you asked for (300 at least) while channels were being left for a later run. Run it again later.
- **A channel line came back as a status row** — the `error` column says why: not a channel address, no channel at that handle, the channel was removed — or YouTube did not answer for it ("could not be looked up", "could not be read"): run those again later.
- **`websiteScan: "unreachable"`** — the channel's site refused or timed out. The row is still delivered with everything YouTube shows.
- **Few rows with an `email`** — small channels rarely publish one (18 of 264 channels under 1,000 subscribers in our run). Set `minSubscribers`, or `onlyWithEmail` to pay only for channels that have one.
- **"… another run of the list … is in progress, or was cut off in the last three minutes"** — two runs of the same `listName` would deliver the same channels; start the second when the first has finished.

### 🚧 What it does not do

- It does **not** reveal the address behind YouTube's "View email address" button. That needs a signed-in Google account and a CAPTCHA; this Actor never logs in. `hasHiddenBusinessEmail` tells you the address exists, so you can open it by hand for the few channels that matter most.
- No addresses that their owner hid from harvesting (spelled in words, protected by a script, encoded in the page source).
- No transcripts, no comments, no video downloads.
- Subscriber counts are YouTube's rounded figures; exact counts have not been public since 2019.
- YouTube's search decides which channels a keyword brings, and it does not return the same list on every request.
- YouTube can change its pages at any time; a count or figure that cannot be read is `null`. The one estimate the Actor makes is marked: in a row read from the uploads list (`recentVideosSource`), the activity columns rest on the ages YouTube words ("3 weeks ago").

### ⚖️ Is it legal to scrape YouTube channel e-mails?

The Actor reads only what YouTube and the creator show to every visitor who is not signed in: no login, no cookies, no CAPTCHA solving, no hidden addresses. E-mail addresses and channel names are personal data in many countries even when they are public. You are responsible for how you use them: anti-spam and data-protection rules (CAN-SPAM in the US, the GDPR and ePrivacy rules in the EU, and local equivalents) apply to your outreach, and your use should fit YouTube's terms. YouTube is a trademark of Google LLC; this Actor is not affiliated with, endorsed or sponsored by YouTube or Google.

### 🤝 Support & contact

- 🌐 **Website:** [data.foxlabs.com.tr](https://data.foxlabs.com.tr)
- 📧 **Email:** info@foxlabs.com.tr
- 🐛 **Issues:** open a ticket in the Actor's **Issues** tab
- 🧰 **More data actors:** [Fox Labs on Apify](https://apify.com/foxlabs)

### Changelog

#### 0.1.28 — 2026-10-08

- When YouTube's feed does not answer for a channel and the channel itself shows no address that counts, the Actor now opens two or three video pages — of the channel's latest fifteen uploads, the two newest that run longer than three minutes, and a third while an address was seen that does not count yet — and looks for one in their descriptions.
- A channel that gave neither its recent videos (feed and uploads list both silent) nor an e-mail is no longer delivered and charged with empty columns: it is left for a later run, and a list does not remember it. A channel line that is left gets a free status row; the status message counts such channels apart from the ones your filters turned down.
- An address on a domain without mail servers no longer stops the search: the channel's website — and, while the feed is down, its video pages — are looked at for another.
- A run that delivers nothing ends as failed when, of the channels that passed your subscriber and country filters, more were left for a later run than were turned down; what those two filters turned down no longer counts against it.
- The status message says how many video pages could not be read. Under `onlyWithEmail`, the reason a channel is turned down while the feed is down now reads "no public e-mail found (YouTube's feed did not answer: its video descriptions could not all be read)".

#### 0.1.25 — 2026-10-08

- When YouTube's feed does not answer for a channel, its upload and view figures are now read from the channel's uploads list instead of being left empty: rounded views, activity from the ages YouTube shows. The new column `recentVideosSource` says where a row's figures come from (`feed` or `uploads list`); `uploadedWithinDays` and `minMedianViews` judge such a channel by those figures. The like columns, `sponsorMentionsRecent` and `hasAffiliateLinks` stay `null` in such a row (`promoCodes` and `domainsLinkedInRecentVideos` are empty lists), and with `onlyWithEmail` a channel for which no address was found is turned down with a reason of its own, because its video descriptions could not be read.

#### 0.1.24 — 2026-10-08

- A run that delivers nothing now says "no rows charged" (it said "nothing charged"; the run-start event applies to every run). This README was corrected after an independent fact-check — the list is in the Changelog tab.

#### 0.1.23 — 2026-10-08

- While YouTube's feed service is down altogether, a feed that does not come is asked one more time instead of five, so an outage does not slow a run down for nothing.

#### 0.1.22 — 2026-10-08

- When YouTube's feed does not answer for a channel, it is asked up to five more times. A channel whose feed still does not come shows `recentVideosAnalyzed: null` (it was `0`) with `sponsorMentionsRecent` and `hasAffiliateLinks` `null`, the status message counts such rows, and a filter on recent videos no longer calls the channel inactive.

#### 0.1 — 2026-10-07 — first version

- Channel discovery by keyword (channel search and the creators behind the top videos, each search followed up to five times) or from a channel list.
- Public e-mail with source and confidence, a mail-server check, website and social links.
- Recent-video audience and activity figures; sponsorship signals; filters by size, country and activity.
- "Only new channels" lists; runs that continue after a restart; a spending limit that is exact. The full list is in the Changelog tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/foxlabs/youtube-channel-email-scraper/changelog.md

# Actor input Schema

## `searchKeywords` (type: `array`):

One niche or topic per line, the way you would type it into YouTube search: "drone review", "skincare routine", "budget mechanical keyboards". Each keyword brings the channels YouTube ranks for it. Specific keywords find more relevant creators than broad ones.

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

Already know the channels? One per line: a channel URL (https://www.youtube.com/@mkbhd), an @handle (@mkbhd) or a channel ID (UC…). A line that is not an existing channel comes back as a free status row that says why.

## `maxChannels` (type: `integer`):

How many channel rows to deliver in total. The run stops here; you are charged only for delivered rows.

## `discoverFrom` (type: `string`):

Channel search lists channels whose name or description matches the keyword (many are small). The creators behind the top videos are channels that actually rank for the keyword. Both gives the widest list.

## `maxChannelsPerKeyword` (type: `integer`):

Limits how many rows one keyword may deliver, so a list of keywords is covered evenly. Leave empty for no per-keyword limit.

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

Only channels with at least this many subscribers. Channel search returns many very small channels; 10000 keeps the ones with a real audience. Channels that hide their subscriber count are left out when this is above 0. Filtered channels are not delivered and not charged.

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

Only channels with at most this many subscribers, for example 100000 for micro-creators. Leave empty (or 0) for no upper limit.

## `countries` (type: `array`):

Only channels whose stated country is in this list. Write country names or two-letter codes: United States, US, Germany, DE. Channels that state no country are left out when the list is filled. Leave empty for all countries.

## `uploadedWithinDays` (type: `integer`):

Only channels whose latest upload is at most this many days old, for example 30 for active creators. Leave empty to include inactive channels.

## `minMedianViews` (type: `integer`):

Only channels whose recent uploads (the latest 15 at most) reach at least this median number of views. Leave empty for no limit.

## `onlyWithEmail` (type: `boolean`):

Deliver a channel only if a public e-mail was found for it. Channels without one are not delivered and not charged.

## `scanWebsites` (type: `boolean`):

When YouTube itself shows no usable address, open the website the channel links to (home page and one contact page) and take an address only if it is on that site's own domain, or is a free mailbox (Gmail and the like) shown there. Addresses of other companies found there are ignored.

## `checkEmailDomains` (type: `boolean`):

A DNS lookup per address (nothing is sent to the mailbox). An address whose domain cannot receive mail is listed but not used as the row's e-mail.

## `onlyNewChannels` (type: `boolean`):

Remember delivered channels under the list name below and skip them in later runs. Schedule the same keywords weekly and each run brings only creators you have not received yet.

## `listName` (type: `string`):

Name of the memory used by "Only channels not delivered before". Use a different name for each campaign. Letters, digits and hyphens (a hyphen not first or last), up to 40 characters; capitals are read as lower case.

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

Nothing to change for normal use: requests go out directly, and a kind of request YouTube refuses (or keeps leaving unanswered) is moved to Apify Proxy by the Actor itself. Turn a proxy on here only if you want every request to use it.

## Actor input object example

```json
{
  "searchKeywords": [
    "drone review"
  ],
  "maxChannels": 20,
  "discoverFrom": "channels-and-videos",
  "minSubscribers": 10000,
  "onlyWithEmail": false,
  "scanWebsites": true,
  "checkEmailDomains": true,
  "onlyNewChannels": false,
  "listName": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "searchKeywords": [
        "drone review"
    ],
    "maxChannels": 20,
    "minSubscribers": 10000,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("foxlabs/youtube-channel-email-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 = {
    "searchKeywords": ["drone review"],
    "maxChannels": 20,
    "minSubscribers": 10000,
    "proxyConfiguration": { "useApifyProxy": False },
}

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

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

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

```

## CLI example

```bash
echo '{
  "searchKeywords": [
    "drone review"
  ],
  "maxChannels": 20,
  "minSubscribers": 10000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call foxlabs/youtube-channel-email-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/3K6InhPDlo4uPfDGc/builds/eu28qWWNvzOMFczxK/openapi.json
