# YouTube Channel Email Finder - Creator Contacts & Socials (`ntriqpro/youtube-channel-email-finder`) Actor

YouTube channel email finder, no login: the business e-mail each channel shows in public (About text, linked pages), socials and numbers. Free: a masked preview of 5 channels. Paid: full contacts. E-mails shown only after sign-in are not opened. Plus an optional AI fit review (beta, paid plans).

- **URL**: https://apify.com/ntriqpro/youtube-channel-email-finder.md
- **Developed by:** [daehwan kim](https://apify.com/ntriqpro) (community)
- **Categories:** Lead generation, Social media, AI
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.80 / 1,000 e-mail founds

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

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## YouTube Channel Email Finder - Creator Contacts & Socials

Give YouTube channels (or a search) and get one row per channel: the business e-mail addresses the channel shows in public, its social profiles and links, and its channel numbers. No login, no cookies, no CAPTCHA solving. Optional: an AI fit review (beta, paid plans).

**What you pay for** (details under [Pricing](#pricing)): each row with a **full public e-mail** ($0.032 on BRONZE) on top of its **channel looked up** ($0.008 on BRONZE, also one YouTube says does not exist) · each **search made** ($0.008) · a **run-start fee** once per run ($0.004) · each **AI fit verdict** if you ask for one ($0.024) · each lookup or search **YouTube refused** (HTTP 403/429, $0.008, instead of the lookup or search event). A paid run of 100 entered channels where 40 show a public e-mail is about $2.08 on BRONZE (an estimate: the e-mail share is different for every list).

**E-mails YouTube shows only after sign-in are not opened on any plan.** The Actor tells you which channels have one (`hiddenBusinessEmail`), and that is all.

| | Free plan (preview) | Paid Apify plan (Starter and up) |
|---|---|---|
| Channels per run | 5 (with searches: the first search only) | up to `maxChannels` (default 100, 1,000 per run), up to 100 per search |
| Each channel row | the channel only: `channelId`, `handle`, `channelUrl`, `title`, `exists`, what you entered | the full row: numbers, country, join date, About text, e-mails, links, social profiles |
| Public e-mail addresses | not per channel: the run's total (how many channels show one, how many addresses) in a `free-plan-preview` row | full addresses, the page each was found on, own domain or free mail, whether the domain takes mail |
| "E-mail shown only after sign-in" | the run's total | per channel (the address itself is not opened on any plan) |
| Social profiles and links | the run's total | every address |
| AI fit review (beta) | locked — a free run asks no model | optional, for up to `aiMaxItems` channels (1,000 a run) |
| What a paid run of the same input costs | an estimate in the `free-plan-preview` row (BRONZE prices, from this run's sample) | — |
| Price | `run-started`, `search-query`, `channel-checked` and `source-access-checked` | all six events, with plan discounts |

A free run ends as SUCCEEDED with a status message that says what it found, what was withheld and what a paid run of the same input would cost. To get the full rows, switch to a paid Apify plan and run the same input again. Nothing in a free run's dataset, log or key-value store holds an address, a link or the About text.

### What it is for

- **Sponsorship and influencer outreach**: turn a list of channels, or a search like `indie game review`, into contacts you can sort.
- **Creator lead lists**: public business e-mail, website, Instagram, X, TikTok, LinkedIn, Discord and link-page addresses in one row.
- **Shortlisting (optional AI fit review, beta, paid plans)**: describe your campaign once in `brief`; channels the review is made for come back as `strong`, `possible`, `weak` or `unclear`, with the sentences of the channel's own About text the verdict rests on.

### Where the addresses come from

Only from what the channel itself publishes to visitors who are not logged in:

1. the channel's **About text**, including addresses written as `name [at] domain [dot] com`;
2. when the About text shows none: **one page** behind each of up to three links the channel lists (its website or link page — not social profiles or shops). Nothing beyond that page is opened.

YouTube also has a "View email address" button that works only for signed-in users. This Actor does **not** open it, on the free plan or on any paid plan. On a paid plan it tells you that the button is there (`hiddenBusinessEmail: true`), so you know which channels have a business address that is not shown in public; a free run gives only the number of such channels.

Addresses that are not a contact are left out: file names (`logo@2x.png`), placeholders (`you@example.com`), `no-reply` addresses, and the addresses of site builders, trackers and the platforms themselves.

### How to use it

1. Enter channels (`@handle`, channel address or channel ID) and/or searches.
2. Optional (beta, paid plans): turn on **AI fit review** and describe your campaign in **Campaign brief**.
3. Run. Each channel is one row, and each search made is one row (`recordType: "search"`). A channel YouTube says does not exist is a row too (`exists: false`), charged as a lookup. A channel or a search YouTube refused (HTTP 403 or 429) is a row too (`accessRefused: true`), charged once as `source-access-checked`. A channel or a search we could not read on our side (a time-out, an answer that could not be read) is a notice row and is not charged.

### Input

| Field | Type | Description |
|---|---|---|
| `channels` | array | `@handle`, `youtube.com/@name`, `/channel/UC…`, `/c/name`, `/user/name` or a channel ID. The same channel entered twice is read and charged once. A handle or ID YouTube does not know is still looked up and charged: its row says so. A lookup YouTube refuses (HTTP 403/429) is a row charged as `source-access-checked` instead of `channel-checked` |
| `searchQueries` | array | Search words, 2–100 characters each. Each search made is charged as `search-query`, also one that returns no channels; the channels YouTube returns are checked and charged like entered ones. A search YouTube refuses (HTTP 403/429) is a search row charged as `source-access-checked` instead of `search-query` |
| `maxChannelsPerSearch` | integer | Channels taken from each search, 1–100 (default 20) |
| `maxChannels` | integer | The most channels a run looks at, 1–1,000 (default 100; free plan: 5) |
| `followLinks` | boolean | Read one page behind up to 3 of the channel's links when the About text shows no address (default `true`) |
| `aiFit` | boolean | Optional AI fit review (beta, paid plans, default `false`). Needs `brief` |
| `brief` | string | Your campaign in your own words, 20–2,000 characters |
| `aiMaxItems` | integer | The most channels sent to the model in a run, 1–1,000 (default 100) |
| `proxyConfiguration` | object | Off by default. Turn it on if a run reports refused requests: every request to YouTube then leaves through it (the `limits` row counts them in `requests.viaProxy`). Pages a channel links to are always read directly |

```json
{
  "channels": ["@mkbhd", "https://www.youtube.com/@veritasium"],
  "searchQueries": ["mechanical keyboard review"],
  "maxChannels": 50,
  "aiFit": true,
  "brief": "Mechanical keyboard brand looking for English-speaking tech reviewers who cover desk setups; not gaming-only channels."
}
```

### Output

One row per channel (`recordType: "channel"`):

| Field | What it holds |
|---|---|
| `exists` | `true` for a channel that was read; `false` when YouTube answered that there is no such channel (see below) |
| `input`, `searchQuery` | What you entered, or the search that found the channel |
| `channelId`, `handle`, `channelUrl`, `title`, `description` | The channel |
| `subscribers`, `views`, `videos` (+ `subscribersText`, `viewsText`, `videosText`) | Numbers, and the text YouTube shows them as |
| `joined`, `country` | Join date (`YYYY-MM-DD`) and country, when the channel shows them |
| `emails[]` | `address`, `source` (`description` or `link`), `sourceUrl`, `domainType` (`own` or `free`), `domainHasMx` (`true`, `false`, or `null` when it could not be found out) |
| `emailFound`, `emailCount` | Whether and how many public addresses were found |
| `hiddenBusinessEmail` | `true` when YouTube shows a business e-mail only after sign-in |
| `website`, `links[]`, `linkCount` | The channel's first own site, and all links it lists (`title`, `url`) |
| `socials`, `socialPlatforms` | `{ "instagram": "https://…", "x": "https://…" }` and the platform names |
| `fit` | Optional AI fit review (beta): `status` (`reviewed` or `not-reviewed` with a `reason`), `fit`, `reasons[]` (`quote`, `why`), `audience`, `audienceEvidence`, `flags[]`, `model`, `modelRequests` (how many requests the review took, on rows the model was asked about) |
| `plan`, `locked[]` | `free` or `paid`, and which parts of the row are withheld or masked |
| `billedEvents[]` | The events this row is charged for |
| `checkedAt`, `disclaimer` | When it was read, and the notice below |
| `rowId` | A fixed id of the row within the run (every row has one, notices and the `limits` row too). A run that is restarted reads these ids from its dataset and writes no row a second time |

```json
{
  "recordType": "channel",
  "input": "@examplechannel",
  "channelId": "UCxxxxxxxxxxxxxxxxxxxxxx",
  "handle": "@examplechannel",
  "channelUrl": "https://www.youtube.com/@examplechannel",
  "title": "Example Channel",
  "subscribers": 123000,
  "subscribersText": "123K subscribers",
  "country": "United States",
  "emails": [
    { "address": "business@example-channel.tv", "source": "description", "sourceUrl": "https://www.youtube.com/@examplechannel/about", "domainType": "own", "domainHasMx": true }
  ],
  "emailFound": true,
  "hiddenBusinessEmail": true,
  "website": "https://example-channel.tv/",
  "socials": { "instagram": "https://www.instagram.com/examplechannel", "x": "https://x.com/examplechannel" },
  "socialPlatforms": ["instagram", "x"],
  "fit": {
    "status": "reviewed",
    "fit": "strong",
    "reasons": [{ "quote": "Weekly reviews of mechanical keyboards and desk setups", "why": "The channel covers the product category of the brief." }],
    "audience": "not stated",
    "flags": ["business-contact-stated"],
    "model": "claude-haiku-5-5"
  },
  "plan": "paid",
  "billedEvents": ["channel-checked", "email-found", "fit-reviewed"]
}
```

**A channel that does not exist.** When YouTube itself answers that there is no channel for what you entered (a misspelled, renamed or removed handle, or a mistyped ID), you get a row with `exists: false`, `input`, `lookedUp` (the address or channel ID that was looked up), `notFoundReason` (one sentence: what YouTube answered and the likely causes), `checkedAt`, and empty e-mail, link and social fields. The lookup was made, so the row is charged as `channel-checked`. No e-mail is looked for and no AI review is made for it.

```json
{
  "recordType": "channel",
  "exists": false,
  "input": "@examplechanel",
  "lookedUp": "https://www.youtube.com/@examplechanel",
  "notFoundReason": "YouTube answered that there is no channel at this address. The handle may be misspelled, or the channel was renamed or removed.",
  "emails": [],
  "emailFound": false,
  "billedEvents": ["channel-checked"]
}
```

A lookup that **YouTube refused** (HTTP 403 or 429 — the site's answer, not a fault in this Actor) is a row of its own: `exists: null`, `accessRefused: true`, `refusedStatus`, `lookedUp`, `refusalReason` and `billedEvents: ["source-access-checked"]`, with nothing about the channel in it; it is charged once and the channel is not asked again in the same run. A lookup that failed **on our side** (it timed out, or its answer could not be read) is never reported as "does not exist": it is a `channel-unreadable` notice row and is not charged.

**A search** is one row (`recordType: "search"`): `searchQuery`, `channelsFound`, `channelsTaken` (checked in this run, each its own channel row), `channelsLeftOut` (past `maxChannels`), `lastPageFailed`, a `message` and `billedEvents: ["search-query"]` — also when YouTube returns no channels for it. A search YouTube refused (HTTP 403/429) is a search row with `accessRefused: true` and `billedEvents: ["source-access-checked"]`. A search whose answer could not be read on our side is a `search-unreadable` notice and is not charged.

**On the free plan** a channel row holds only `recordType`, `exists`, `input`, `searchQuery`, `channelId`, `handle`, `channelUrl`, `title`, `plan`, `locked`, `billedEvents`, `rowId`, `checkedAt` and `disclaimer` (a channel that does not exist also `lookedUp` and `notFoundReason`). Whether a channel shows an e-mail is not on its row: the `free-plan-preview` row gives the run's totals — channels with a public e-mail, addresses, channels with an e-mail shown only after sign-in, links and social profiles — and an estimate of a paid run of the same input.

Other rows, never charged: **notice rows** (`noticeCode`: `channel-unreadable`, `search-unreadable`, `duplicate-input`, `invalid-input`, `input-limit`, `charge-limit-reached`, `free-plan-preview`, `ai-off`, `pricing-unverified` and others, each with a `message`) and one **`limits` row** that adds the run up: what was read, what was left out and why, each search, and what was charged.

### AI fit review (optional, beta, paid plans)

The review is an extra you can ask for; the contacts are the product. It is made only when all of these hold — otherwise the rows go out without a verdict, nothing is charged for reviews, and an `ai-off` notice (or the row's `fit.reason`) says why:

- the run is on a paid Apify plan, `aiFit` is on and `brief` has 20–2,000 characters;

- the run's price list has the `fit-reviewed` event;

- the review is available in this run (the model service answers and the Actor's model key is set);

- the channel has an About text, and fewer than `aiMaxItems` channels were reviewed so far;

- your run's maximum charge still pays for the whole row (channel, e-mail and review);

- the run's own spending cap for model calls is not used up.

- **What is sent**: the channel's public About text (first 6,000 characters), its name, subscriber text, country and the names of the platforms it links to, together with your brief. E-mail addresses and web addresses are removed before sending. When `aiFit` is on, this text goes to **Anthropic's API (United States)**; the model is `claude-haiku-5-5`.

- **What comes back**: `strong`, `possible`, `weak` or `unclear`, up to three reasons each quoting the channel's own words, the audience the channel states (or `not stated`), and flags from a fixed list.

- **What we check before it is delivered and charged**: every quote must be in the channel's text letter for letter, every number the answer writes must be in the channel's text or your brief, and the answer may hold no address. An answer that fails is thrown away — you get the row without a verdict and are not charged for the review.

- The verdict is a reading of what the channel says about itself. It is an aid for sorting a list, not a statement about the channel's audience or performance.

- A channel with no About text is not sent to the model (`fit.reason: "no-material"`, not charged), nor is a channel that does not exist (`fit.reason: "channel-not-found"`).

### Pricing

Pay per event, six events. The main one is `email-found`; a channel with a full public e-mail is `email-found` + `channel-checked` (FREE $0.05, BRONZE $0.04, GOLD $0.026):

| Event | FREE | BRONZE | SILVER | GOLD and up | When |
|---|---|---|---|---|---|
| `email-found` | $0.04 | $0.032 | $0.026 | $0.0208 | The row carries at least one full public e-mail address (once per channel, however many addresses), on top of the row's `channel-checked` |
| `channel-checked` | $0.01 | $0.008 | $0.0065 | $0.0052 | A channel was looked up and delivered as a row — also when no e-mail was found, and also when YouTube answered that the channel does not exist (`exists: false`) |
| `search-query` | $0.01 | $0.008 | $0.0065 | $0.0052 | A search was made (once per search, however many result pages; also when it returns no channels) |
| `run-started` | $0.005 | $0.004 | $0.00325 | $0.0026 | Once per run, right before the run starts looking anything up |
| `fit-reviewed` | $0.03 | $0.024 | $0.0195 | $0.0156 | Optional (beta): an AI verdict that passed our checks is on the row |
| `source-access-checked` | $0.01 | $0.008 | $0.0065 | $0.0052 | YouTube refused a channel lookup or a search (HTTP 403 or 429): the request was made and the row says so (`accessRefused: true`). Charged once, instead of `channel-checked` or `search-query`: an entry or a search gets one of them in a run, also when the run is restarted |

A lookup is charged because it was made: a channel that turns out to be misspelled or removed costs the same lookup as one that exists, and its row tells you so. The same holds for a search that finds nothing, and for a lookup or search YouTube refuses (403/429): that refusal is the site's answer, not a fault in this Actor.

What is not charged: entries that are not a YouTube channel (nothing is looked up for them, and a run whose input has no valid channel or search pays no run-start fee either), lookups and searches that failed on our side (time-outs, answers that could not be read, our errors), the same channel or search entered twice, masked addresses, e-mails shown only after sign-in (they are not opened), reviews that failed or were thrown away, notice rows and the `limits` row.

**Your maximum charge is respected.** The run-start fee is counted against your run's limit before the first lookup: when the limit does not cover it, the run looks nothing up, charges nothing and ends as SUCCEEDED with a `charge-limit-reached` notice. A search is counted the same way before it is made. Before a row is written the Actor counts whether your run's limit still pays for it. When the limit pays for the channel but not for its e-mail event, the row is delivered with the addresses masked and without that charge; when it does not pay for the channel, the run ends as SUCCEEDED with a `charge-limit-reached` notice. A run that is restarted (a migration, or a resurrected run) first charges the rows it had delivered and not yet charged — only as far as your limit pays for them; what does not fit is named in a `restart-unsettled` notice and nothing further is delivered until the limit is raised.

### Good to know

- **The same channel in two runs is charged in both.** The Actor keeps nothing between runs.
- **A restarted run** (migration or resurrect) continues where it stopped: channels and searches already in the dataset are not charged again, the run-start fee is not charged a second time, and a row that was delivered right before the restart is charged then. A run stopped between its run-start fee and its first lookup has paid only that fee; resurrect it and it goes on.
- **A typo is a lookup.** Check handles before a large run: `@examplechanel` instead of `@examplechannel` is looked up, comes back with `exists: false` and is charged as `channel-checked`.
- **An address found on a linked page** (`source: "link"`) is the address that page shows; it may be a general contact of the site rather than the channel owner's own. `sourceUrl` tells you where it was read.
- **Requests**: up to 4 channels at a time, with a pause between requests. If YouTube refuses five requests in a row the run stops asking and says so; the refused lookups are rows charged as `source-access-checked`, and nothing is charged for channels that were not asked (the run-start fee was charged when the run began looking up).
- Counts (`subscribers`, `views`, `videos`) are read from YouTube's English wording and rounded as YouTube rounds them (`21.4M` → 21,400,000).

### Responsible use

This is an unofficial, independent tool. It is not affiliated with, endorsed by, or sponsored by YouTube or Google. It reads only what YouTube and the pages a channel links to show to visitors who are not logged in. It does not use accounts or login cookies, and it sends no messages.

The output contains contact details that channels publish. You are responsible for how you run this Actor and how you use its output: having a lawful basis for processing personal data (for example under GDPR, CCPA or PIPA), complying with anti-spam law (for example CAN-SPAM) and any consent a message requires, and respecting YouTube's terms. You are the data controller for the output of your runs. Do not use it to send unsolicited bulk messages or to harass anyone.

# Actor input Schema

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

Channels to check. Accepts @handles, channel addresses (youtube.com/@name, /channel/UC..., /c/name, /user/name) and channel IDs. The same channel entered twice is read and charged once. A handle or ID YouTube answers does not exist is still a lookup: it comes back as a row with exists: false and is charged as one channel checked. A lookup YouTube refuses (HTTP 403 or 429) comes back as a row with accessRefused: true and is charged once as source-access-checked ($0.008 BRONZE) instead. Free plan: the first 5 channels per run, each row with the channel only (the run's e-mail totals and a paid-run estimate come in a free-plan-preview row). Paid plans: up to 1,000 per run, full public e-mail addresses ($0.032 per channel with one, plus $0.008 per channel, BRONZE). E-mails YouTube shows only after sign-in are not opened on any plan.

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

Search words (2-100 characters each). Each search made is charged once as search-query ($0.008 BRONZE), also one that returns no channels; every channel it returns is checked and charged like one you entered. A search YouTube refuses (HTTP 403 or 429) is a search row charged once as source-access-checked ($0.008 BRONZE) instead of search-query. Free plan: only the first search is run, and only as many channels as fit in the 5 of the run; paid plans: every search, up to 100 channels each.

## `maxChannelsPerSearch` (type: `integer`):

How many channels to take from each search (1-100). YouTube returns about 20 a page; more means more requests.

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

The most channels this run looks at (1-1,000), entered channels first, then search results. Every channel looked up uses a place: one that YouTube answers does not exist is a charged row (exists: false); one that could not be read on our side is not charged. Free plan: 5, whatever is set here.

## `followLinks` (type: `boolean`):

When a channel's About text shows no e-mail address, open up to 3 of the links the channel lists (its website or link page; not social profiles or shops) and read that one page each for an address. Nothing beyond that page is opened.

## `aiFit` (type: `boolean`):

Optional AI fit review (beta, paid plans): adds to channel rows a verdict on whether the channel fits your campaign (strong / possible / weak / unclear), with the passages of the channel's own text the verdict rests on. Needs `brief`. It is not made (and not charged) when the run's price list has no fit-reviewed event, the review is not available in the run, `aiMaxItems` is reached, the run's maximum charge does not pay for the whole row, or the run's spending cap for model calls is used up. When this is on, the channel's public About text, name, subscriber text, country and the names of the platforms it links to, together with your brief, are sent to Anthropic's API (United States). E-mail addresses are removed before sending. Free plan: locked — no model is asked.

## `brief` (type: `string`):

What you are looking for, in your own words (20-2,000 characters): the product, the audience you want to reach, what kind of channel suits it and what does not. Example: "Mechanical keyboard brand looking for English-speaking tech reviewers who cover desk setups; not gaming-only channels." Used only with `aiFit`.

## `aiMaxItems` (type: `integer`):

The most channels sent to the model in one run (1-1,000). Channels past it are delivered without a verdict and without that charge.

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

Off by default: YouTube answers these requests without one. Turn it on if your runs report refused requests: every request to YouTube then leaves through it. Proxy usage is billed by Apify as platform usage. Pages a channel links to are always read directly.

## Actor input object example

```json
{
  "channels": [
    "@mkbhd"
  ],
  "searchQueries": [
    "indie game review"
  ],
  "maxChannelsPerSearch": 20,
  "maxChannels": 100,
  "followLinks": true,
  "aiFit": false,
  "aiMaxItems": 100
}
```

# 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 = {
    "channels": [
        "@mkbhd",
        "@LinusTechTips",
        "@veritasium"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ntriqpro/youtube-channel-email-finder").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "channels": [
        "@mkbhd",
        "@LinusTechTips",
        "@veritasium",
    ] }

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

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

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

```

## CLI example

```bash
echo '{
  "channels": [
    "@mkbhd",
    "@LinusTechTips",
    "@veritasium"
  ]
}' |
apify call ntriqpro/youtube-channel-email-finder --silent --output-dataset

```

## MCP server setup

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

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/K51J6a7ijeCE0NDQJ/builds/iq9VgCI7bugmaVgl6/openapi.json
