Find email addresses and contact information from YouTube channels. Search by keyword or provide channel URLs directly. Useful for influencer outreach, sponsorship, and lead generation.
Actor: futurizerush/youtube-email-scraper
Set APIFY_API_TOKEN in environment. Get a token at console.apify.com/account/integrations.
Apify runs are asynchronous. Every request follows 3 steps:
SUCCEEDEDTypical run time: 30-120 seconds depending on channel count.
| Parameter | Type | Required | Description |
|---|---|---|---|
| ----------- | ------ | ---------- | ------------- |
inputMode | string | Yes | "directUrls" or "keywordSearch" |
channelUrls | array of strings | For directUrls mode | YouTube channel URLs |
searchKeywords | array of strings | For keywordSearch mode | Keywords to search channels |
maxChannelsPerKeyword | integer | No | Max channels per keyword. Default: 50 |
maxResults | integer | No | Max total results. Default: 100 |
locale | string | No | Locale for search. Default: "US" |
directUrls -- Scrape specific channels. Requires channelUrls.keywordSearch -- Find channels by keyword, then extract contacts. Requires searchKeywords.import requests, os, time
TOKEN = os.environ["APIFY_API_TOKEN"]
BASE = "https://api.apify.com/v2"
# Step 1: Start the run
response = requests.post(
f"{BASE}/acts/futurizerush~youtube-email-scraper/runs?token={TOKEN}",
json={
"inputMode": "keywordSearch",
"searchKeywords": ["AI automation"],
"maxChannelsPerKeyword": 10,
"maxResults": 10,
"locale": "US",
},
)
response.raise_for_status()
run = response.json()["data"]
run_id = run["id"]
dataset_id = run["defaultDatasetId"]
# Step 2: Poll until done
while True:
status = requests.get(
f"{BASE}/actor-runs/{run_id}?token={TOKEN}"
).json()["data"]["status"]
if status == "SUCCEEDED":
break
if status in ("FAILED", "ABORTED", "TIMED-OUT"):
raise RuntimeError(f"Run failed: {status}")
time.sleep(5)
# Step 3: Fetch results (JSON array)
items = requests.get(
f"{BASE}/datasets/{dataset_id}/items?token={TOKEN}"
).json()
# Filter channels with emails
for channel in items:
if channel["hasEmails"]:
print(f"{channel['channelName']} ({channel['subscriberCount']} subs)")
print(f" Emails: {', '.join(channel['emails'])}")
print(f" URL: {channel['channelUrl']}")
requests.post(
f"{BASE}/acts/futurizerush~youtube-email-scraper/runs?token={TOKEN}",
json={
"inputMode": "directUrls",
"channelUrls": [
"https://www.youtube.com/@mkbhd",
"https://www.youtube.com/@nateherk",
],
},
)
# Step 1: Start the run
RUN_RESPONSE=$(curl -s -X POST \
"https://api.apify.com/v2/acts/futurizerush~youtube-email-scraper/runs?token=$APIFY_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"inputMode": "keywordSearch", "searchKeywords": ["AI automation"], "maxChannelsPerKeyword": 10, "maxResults": 10}')
RUN_ID=$(echo "$RUN_RESPONSE" | jq -r '.data.id')
DATASET_ID=$(echo "$RUN_RESPONSE" | jq -r '.data.defaultDatasetId')
# Step 2: Poll until done
while true; do
STATUS=$(curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?token=$APIFY_API_TOKEN" \
| jq -r '.data.status')
[ "$STATUS" = "SUCCEEDED" ] && break
[ "$STATUS" = "FAILED" ] || [ "$STATUS" = "ABORTED" ] && echo "Failed: $STATUS" && exit 1
sleep 5
done
# Step 3: Fetch results
curl -s "https://api.apify.com/v2/datasets/$DATASET_ID/items?token=$APIFY_API_TOKEN" | jq '.'
Each item in the results array (field names verified from real API output on 2026-04-11):
{
"channelName": "Nate Herk | AI Automation",
"subscriberCount": "645.0K",
"channelUrl": "https://www.youtube.com/@nateherk",
"channelDescription": "Hi, I'm Nate Herk. I'm passionate about...",
"joinDate": "Nov 17, 2013",
"viewCount": 405328,
"videoCount": 383,
"location": null,
"contactInfo": {
"emails": ["sponsorships@nateherk.com"],
"websites": ["https://nateherk.com/"],
"socialMedia": {}
},
"emails": ["sponsorships@nateherk.com"],
"websites": ["https://nateherk.com/"],
"socialMedia": {},
"emailCount": 1,
"hasEmails": true,
"descriptionUrls": [],
"extractedAt": "2026-04-11T06:07:13.204Z",
"processingSuccess": true,
"searchKeyword": "AI automation"
}
Note: Field names use camelCase. The emails array is a flat convenience field; the same data is also in contactInfo.emails. Use hasEmails to quickly filter channels that have contact emails.
| Error | Cause | Fix |
|---|---|---|
| ------- | ------- | ----- |
| 401 Unauthorized | Invalid or missing API token | Check APIFY_API_TOKEN |
invalid-input: "must be equal to one of the allowed values" | Wrong inputMode value | Use "directUrls" or "keywordSearch" (exact strings) |
| No emails found | Channel doesn't list email publicly | Try more channels or different keywords |
hasEmails: true to quickly filter channels with public emails.subscriberCount is a string (e.g. "645.0K") not a number.contactInfo contains structured data; emails and websites at root level are flat convenience arrays.searchKeyword in output tells you which query found each channel (useful with multiple keywords).共 1 个版本