# YouTube Channel Contact Scraper (`muhammad_noman_riaz/youtube-channel-contact-scraper`) Actor

Find public business emails, websites and social links of YouTube creators by channel URL or keyword, with the source of every email. You pay only for channels where an email is found. No login.

- **URL**: https://apify.com/muhammad_noman_riaz/youtube-channel-contact-scraper.md
- **Developed by:** [Muhammad Noman Riaz](https://apify.com/muhammad_noman_riaz) (community)
- **Categories:** Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 channel with emails

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 Contact Scraper

### What does YouTube Channel Contact Scraper do?

YouTube Channel Contact Scraper finds the business contact details that YouTube creators publish openly. Give it channel URLs or search keywords and it returns, for each channel, the emails written in the channel description, the website and social links from the About section, and emails listed on the creator's own website. Every email comes with the page it was found on, so you can check the source yourself. It also returns channel stats such as subscribers, videos, total views, country and join date. The scraper only reads public pages. It never logs in, never uses cookies and never touches YouTube's hidden "View email address" button, which requires a signed-in account and a captcha. It does not guess or generate emails. Agencies and brands use it to build influencer outreach lists. You pay $50 per 1,000 channels, and only for channels where an email was found.

### Why use it?

- **Source for every email.** Each email has `sourceType` (`channel_description`, `channel_links` or `website`) and `sourceUrl`.
- **Creator websites included.** Reads the creator's own linked site (homepage plus one contact or about page) for published emails.
- **Social profiles sorted.** Instagram, X, TikTok, Facebook, LinkedIn, Twitch and Discord links in their own fields, other link pages in `other`.
- **Pay only when you get an email.** By default only channels with at least one public email are returned and charged: $50 per 1,000, or $0.05 per channel with an email.
- **Keyword discovery.** Finds channels with YouTube search using the Channels filter.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `channelUrls` | array of strings | | Channel URLs: `/@handle`, `/channel/UC...`, `/c/name` or `/user/name` |
| `searchKeywords` | array of strings | | Keywords to find channels with YouTube search (Channels filter) |
| `maxChannelsPerKeyword` | integer | 20 | Channels taken from the results of each keyword |
| `includeWebsiteEmails` | boolean | true | Also read the creator's own website for published emails |
| `includeChannelsWithoutEmail` | boolean | false | Also return (and charge for) channels that only have a website or social links |
| `maxConcurrency` | integer | 5 | Channels processed at the same time |
| `maxRequestRetries` | integer | 5 | Attempts per channel, each on a new proxy IP |
| `proxy` | object | Apify residential | Proxy for YouTube and website requests |

#### Input example

```json
{
  "channelUrls": [
    "https://www.youtube.com/@mkbhd",
    "https://www.youtube.com/channel/UCsTcErHg8oDvUnTzoqsYeNw"
  ],
  "searchKeywords": ["small business tips"],
  "maxChannelsPerKeyword": 5,
  "includeWebsiteEmails": true,
  "includeChannelsWithoutEmail": false,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

### Output

One item per channel. This is a real item from a test run; the email's local part is masked here and the description is shortened.

```json
{
  "channelId": "UCG-KntY7aVnIGXYEBQvmBAQ",
  "channelUrl": "https://www.youtube.com/channel/UCG-KntY7aVnIGXYEBQvmBAQ",
  "handle": "@Thomasfrank",
  "title": "Thomas Frank",
  "description": "I make videos that help you be more productive - whether you're in high school, college, university, or the real world. ...",
  "subscriberCount": 3010000,
  "videoCount": 281,
  "viewCount": 187084258,
  "country": "United States",
  "joinedDate": "2006-12-02",
  "avatar": "https://yt3.googleusercontent.com/ytc/AIdro_nIr5PB90HA3Uoa6w6PNkbMDZVVINovFpKb5hbHd7JwG0nP=s900-c-k-c0x00ffffff-no-rj",
  "isVerified": true,
  "emails": [
    {
      "email": "t***@collegeinfogeek.com",
      "sourceType": "website",
      "sourceUrl": "https://thomasjfrank.com/about/"
    }
  ],
  "websites": [
    "https://thomasjfrank.com/templates/",
    "https://standard.tv/collections/college-info-geek",
    "https://collegeinfogeek.com",
    "http://collegeinfogeek.com/faq/",
    "https://thomasjfrank.com/gear/"
  ],
  "socialLinks": {
    "instagram": "https://instagram.com/tomfrankly/",
    "x": "https://www.twitter.com/tomfrankly",
    "tiktok": null,
    "facebook": "https://www.facebook.com/thomas",
    "linkedin": null,
    "twitch": null,
    "discord": null,
    "other": []
  },
  "scrapedAt": "2026-10-17T16:11:14.095Z"
}
```

| Field | Description |
|---|---|
| `subscriberCount` | Number parsed from YouTube's rounded text, so "3.01M" becomes 3010000 |
| `videoCount`, `viewCount` | Exact counts from the About section |
| `country`, `joinedDate` | From the About section when the creator set them, else `null` |
| `emails` | Valid, lowercased, deduplicated emails with `sourceType` and `sourceUrl` |
| `websites` | Non-social links from the About section and description |
| `socialLinks` | Named networks plus `other` (link pages, Patreon, Spotify and similar) |

Inputs that fail (not a YouTube URL, channel not found, blocked after all retries) are listed in the `FAILED_ITEMS` record of the run's key-value store.

### How it works

1. Resolves each channel URL to its channel id.
2. Loads the channel header and the About panel through YouTube's own web API, the same data the "...more" button on a channel page loads.
3. Pulls emails from the description and sorts the About links into websites and social profiles. YouTube's `youtube.com/redirect` wrappers are removed.
4. If enabled, reads the first linked website: its robots.txt, the homepage and at most one contact or about page on the same domain. Never more than 3 requests per site, and social networks are never crawled.
5. Drops junk such as image file names (`logo@2x.png`), placeholder domains (`example.com`) and tracking addresses (Sentry, Wix).

### FAQ

#### Why are some channels missing an email?

Many creators only offer their email through YouTube's "View email address" button, which needs a signed-in account and a captcha. This scraper does not use that button, so those emails are not returned. Others list only social profiles or a contact form. In a test on 50 channels from keyword searches, about 1 in 4 had a public email. By default only those are returned; turn on `includeChannelsWithoutEmail` to also get channels with just a website or social links.

#### Is this legal and GDPR compliant?

The scraper only collects contact details that creators chose to publish for business enquiries. Whether you may use them depends on your use and your country. Under GDPR and similar laws, business emails of individuals are still personal data, so you need a lawful basis such as legitimate interest, you must tell people where you got their data and you must honor opt-outs. This is not legal advice.

#### How are results charged?

$50 per 1,000 channels returned, which is $0.05 per channel. By default a channel is only returned, and only charged, when at least one public email was found, so you never pay for a channel without an email. If you turn on `includeChannelsWithoutEmail`, channels with only a website or social links are returned and charged too. Searched channels with no contact and failed inputs are never charged.

#### Can it find hidden YouTube emails?

No. The email behind YouTube's "View email address" button is not collected. Getting it needs a logged-in account and solving a captcha, and this scraper does neither.

#### Does it work with handles and old channel URLs?

Yes. `/@handle`, `/channel/UC...`, `/c/name` and `/user/name` all work. Duplicate channels are returned once.

### Known limits

- Only the first linked website is checked, and only its homepage plus one contact or about page.
- Emails hidden in images, behind contact forms or written as "name at domain dot com" are not found.
- Websites that block the proxy, need JavaScript to show the email, or disallow it in robots.txt return no website emails.
- `subscriberCount` is as precise as YouTube's rounded display (for example 21.4M).
- Search results come from YouTube's search order and can change between runs.
- Social links found on a website only fill empty fields; links in the YouTube About section take priority.

### Responsible use

Use the results for genuine business outreach such as sponsorship and collaboration offers. Follow anti-spam and data protection laws (CAN-SPAM, GDPR, CASL and local equivalents), identify yourself, include a way to opt out and honor every opt-out request. Do not resell contact lists or send bulk unsolicited mail.

# Actor input Schema

## `channelUrls` (type: `array`):

YouTube channel URLs in any form: https://www.youtube.com/@handle, /channel/UC..., /c/name or /user/name.

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

Find channels with YouTube search using the Channels filter, for example "tech review" or "home bakery".

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

How many channels to take from the search results of each keyword.

## `includeWebsiteEmails` (type: `boolean`):

Also read the creator's own linked website (homepage plus one contact or about page, at most 3 requests, robots.txt respected) for published emails and social links.

## `includeChannelsWithoutEmail` (type: `boolean`):

Off (default): only channels with at least one public email are returned and charged. On: channels that only have a website or social links are also returned and charged. Channels with no contact at all are never returned or charged.

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

Maximum number of channels processed at the same time.

## `maxRequestRetries` (type: `integer`):

How many times a channel is tried, each time on a new proxy IP, before it is skipped.

## `proxy` (type: `object`):

Proxy used for YouTube and website requests. Residential proxies are recommended to avoid YouTube blocks.

## Actor input object example

```json
{
  "channelUrls": [
    "https://www.youtube.com/@mkbhd"
  ],
  "maxChannelsPerKeyword": 20,
  "includeWebsiteEmails": true,
  "includeChannelsWithoutEmail": false,
  "maxConcurrency": 5,
  "maxRequestRetries": 5,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `channels` (type: `string`):

One item per YouTube channel with its publicly listed emails, websites and social links

# 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 = {
    "channelUrls": [
        "https://www.youtube.com/@mkbhd"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammad_noman_riaz/youtube-channel-contact-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 = {
    "channelUrls": ["https://www.youtube.com/@mkbhd"],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("muhammad_noman_riaz/youtube-channel-contact-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 '{
  "channelUrls": [
    "https://www.youtube.com/@mkbhd"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call muhammad_noman_riaz/youtube-channel-contact-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammad_noman_riaz/youtube-channel-contact-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/ohCXIS4ZdgfC1Y0KV/builds/iE9OYqpwcUoMgpFA2/openapi.json
