# X Profile Emails Scraper (`automation-lab/x-profile-public-email-scraper`) Actor

Extract emails explicitly displayed in public X profile bios with identity, audience context, source evidence, and canonical URLs.

- **URL**: https://apify.com/automation-lab/x-profile-public-email-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.92 / 1,000 item extracteds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## X Profile Emails Scraper

Extract **x profile emails** that people explicitly display in their public X bios.
Supply handles or profile URLs and receive one clean record per displayed email,
together with the account identity, complete source bio, canonical profile URL,
audience counts, verification context, and extraction timestamp.

The Actor is designed for teams that refresh creator and prospect outreach lists.
It does not infer addresses, reveal private contact data, search followers, or use
an X login.

### What does X Profile Emails Scraper do?

The Actor:

1. accepts public X handles and `x.com` or `twitter.com` profile URLs;
2. deduplicates handles across both input routes;
3. retrieves each anonymously visible public profile;
4. finds syntactically complete email addresses displayed in the bio;
5. applies optional follower and exact email-domain filters;
6. saves one typed dataset item per displayed email.

Profiles without displayed emails are processed but omitted. This keeps the
result set useful as an outreach or enrichment table rather than a list of empty
profiles.

### Who is it for?

- **Creator partnerships teams** refreshing contact lists before a campaign.
- **Influencer agencies** collecting public business inquiry addresses with
  source evidence.
- **Sales and prospecting teams** enriching a known list of X handles.
- **Researchers and compliance teams** auditing where a contact address was
  publicly displayed.
- **Automation developers** sending typed profile-email records to a CRM,
  spreadsheet, warehouse, or review queue.

This Actor starts from profiles you already know. It does not discover accounts
by keyword, follower graph, or post search.

### Why use this Actor?

A bare email list quickly becomes hard to verify. Each result preserves the
public bio that contained the address and the canonical account URL, so a human
or downstream workflow can review the source.

Other practical differences include:

- both handles and profile URLs in one run;
- exact domain filtering without post-processing;
- minimum-follower filtering for campaign qualification;
- one output row per email when a bio contains several addresses;
- no paid proxy or browser fallback enabled by default;
- no inferred, guessed, or website-crawled email addresses.

### Getting started

1. Open the Actor input page.
2. Add one or more values under **X handles**, or add public profile URLs.
3. Optionally set **Minimum followers** or **Email domains**.
4. Set the maximum number of email records.
5. Click **Start**.
6. Open the dataset's **X profile emails** view.
7. Export results as JSON, CSV, Excel, XML, or RSS.

A working starter input is:

```json
{
  "usernames": ["ZachKing", "Valkyrae", "Sykkuno"],
  "maxItems": 100
}
```

Public bios change. A starter profile can stop displaying an email later; that
is expected source behavior rather than an inferred replacement opportunity.

### Input parameters

| Field | Type | Default | Description |
| --- | --- | ---: | --- |
| `usernames` | string\[] | starter list | X handles, with or without `@`. |
| `startUrls` | request\[] | none | Public `x.com` or `twitter.com` profile URLs. |
| `maxItems` | integer | 100 | Maximum displayed-email records to save, from 1 to 1,000. |
| `minFollowers` | integer | 0 | Only retain emails from profiles at or above this follower count. |
| `emailDomains` | string\[] | all domains | Optional exact lowercase-insensitive domains, such as `iso-ent.com`. |

At least one handle or profile URL is required. Inputs are deduplicated
case-insensitively. Unsupported hosts, malformed handles, invalid domains, and
out-of-range limits fail before profile extraction begins.

### Filter a creator outreach list

Use a minimum audience and selected agency domains when a campaign has clear
qualification rules:

```json
{
  "usernames": ["Sykkuno", "GeorgeNotFound", "ZachKing"],
  "minFollowers": 1000000,
  "emailDomains": ["iso-ent.com", "georgenotfound.com"],
  "maxItems": 100
}
```

The same filters apply whether a profile came from `usernames` or `startUrls`.
A domain filter is an exact match: `agency.com` does not automatically include
`mail.agency.com`.

### Output data

Each dataset item represents one email displayed in one public profile bio.

| Field | Meaning |
| --- | --- |
| `email` | Normalized lowercase displayed email address. |
| `emailDomain` | Domain parsed from the address. |
| `emailSource` | Always `bio` in this version. |
| `accountId` | Stable X account identifier. |
| `username` | Current X handle. |
| `name` | Public display name. |
| `profileUrl` | Canonical `https://x.com/<handle>` URL. |
| `bio` | Complete source bio containing the displayed address. |
| `followersCount` | Public follower count at extraction time. |
| `followingCount` | Public following count at extraction time. |
| `postsCount` | Public post count at extraction time. |
| `isVerified` | X verification flag. |
| `isBlueVerified` | X blue-verification flag. |
| `location` | Public location text, which can be empty. |
| `website` | Expanded public profile website, or `null`. |
| `scrapedAt` | ISO 8601 extraction timestamp. |

### Example output

A current result has this shape (the values below mirror the typed output while
using a neutral sample identity):

```json
{
  "email": "contact@samplecreator.test",
  "emailDomain": "samplecreator.test",
  "emailSource": "bio",
  "accountId": "123456789012345678",
  "username": "samplecreator",
  "name": "Sample Creator",
  "profileUrl": "https://x.com/samplecreator",
  "bio": "Videos and tutorials. Business: contact@samplecreator.test",
  "followersCount": 125000,
  "followingCount": 320,
  "postsCount": 4100,
  "isVerified": false,
  "isBlueVerified": true,
  "location": "New York, NY",
  "website": "https://samplecreator.test",
  "scrapedAt": "2026-01-15T12:00:00.000Z"
}
```

### How much does it cost to extract public X profile emails?

Pay-per-event pricing has one small run-start charge and one `item` charge for
each email record saved. Profiles with no displayed email, unavailable profiles,
duplicates, and records rejected by filters do not create an item charge.

The active six-tier price appears in Apify Console before a run. At the BRONZE
reference tier, one displayed email costs **$0.003192**, plus a **$0.0005** run
start. Approximate examples are:

| Saved emails | Approximate BRONZE charge |
| ---: | ---: |
| 1 | 0.003692 USD |
| 10 | 0.03242 USD |
| 100 | 0.3197 USD |
| 1,000 | 3.1925 USD |

Higher subscription tiers receive lower per-item prices. Runtime usage is
covered by the PPE model; actual platform billing is determined by the active
pricing shown for your account.

### Scheduling recurring refreshes

Create an Apify Schedule with the same profile list to refresh public contact
records daily, weekly, or before each campaign. Export the latest dataset to a
stable destination and compare on `accountId + email` to identify additions,
removals, or changed profile context.

The Actor itself does not maintain history or send change alerts. Use datasets,
webhooks, Zapier, Make, n8n, or your warehouse for those workflow steps.

### Integration ideas

- Send displayed business emails to a manual review queue before outreach.
- Upsert contacts into a CRM by `accountId` and `email`.
- Route agency domains to the relevant partnership owner.
- Keep source bio and scrape time as consent/provenance context.
- Filter a supplied creator roster by audience size.
- Export a campaign-ready CSV from the default dataset.

### Run with the Apify API using cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~x-profile-public-email-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"usernames":["ZachKing","Valkyrae"],"maxItems":20}'
```

Poll the returned run or use the synchronous dataset endpoint when your client
can wait for completion.

### Run with JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/x-profile-public-email-scraper').call({
  usernames: ['ZachKing', 'Valkyrae'],
  maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Run with Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/x-profile-public-email-scraper').call(
    run_input={'usernames': ['ZachKing', 'Valkyrae'], 'maxItems': 20}
)
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use with MCP and AI assistants

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/x-profile-public-email-scraper"
```

**Claude Desktop, Cursor, and VS Code setup:** add the following HTTP MCP server configuration in the client's MCP settings:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/x-profile-public-email-scraper"
    }
  }
}
```

Example prompts:

- “Extract displayed emails from these X handles and keep source bios.”
- “Check these X profile URLs and retain contacts with at least one million followers.”
- “Return only displayed addresses on `iso-ent.com` from this creator list.”

### Reliability and retries

The Actor uses an anonymous X guest session and a structured profile endpoint.
It retries bounded network errors, rate limits, and temporary server failures.
Malformed input and stable access errors are not retried blindly.

The implementation deliberately does not switch to an unmeasured residential
proxy or browser. If X changes its public endpoint, a run can fail until the
Actor is updated rather than silently returning false empty output.

### Limitations

- Only supplied public profiles are checked; there is no account discovery.
- Only complete email addresses displayed directly in the bio are extracted.
- Obfuscated text such as `name [at] domain` is not guessed.
- Emails on linked websites, posts, images, or private fields are not crawled.
- Protected, suspended, deleted, withheld, or unavailable profiles may be skipped.
- X can change public profile data and endpoint behavior without notice.
- A successful zero-item run can mean the supplied public profiles display no
  email or were excluded by filters.
- `maxItems` limits email records, not the number of profile requests.

### Responsible use and legality

Use only public information you are authorized to process. A publicly displayed
address is not automatic permission for unsolicited or unlawful marketing.
Follow X's terms, anti-spam rules, privacy and data-protection law, CAN-SPAM,
GDPR/UK GDPR, and industry-specific obligations that apply to your workflow.

Keep source evidence, provide required notices, honor opt-outs and deletion
requests, secure exports, and apply appropriate retention limits. Do not use the
Actor for harassment, sensitive-person targeting, identity abuse, or attempts to
obtain private contact details.

### Troubleshooting

#### “Provide at least one handle”

Add at least one `usernames` value or one `startUrls` entry. A post URL, list URL,
or unsupported host is not a profile input.

#### The run succeeded with zero items

Inspect your filters and check whether the current public bios visibly contain
complete email addresses. Remove `emailDomains` or reduce `minFollowers` if they
exclude the expected record.

#### A profile was unavailable

Confirm the handle in a logged-out browser. Renamed, suspended, deleted,
protected, withheld, or temporarily unavailable accounts may not resolve.

#### The run failed on every profile

Check the Actor status and logs. A broad upstream X endpoint change should fail
explicitly rather than be misreported as an empty contact list. Retry later only
for a documented temporary source incident.

### Related Automation Lab Actors

- [X Profiles Scraper](https://apify.com/automation-lab/x-profile-scraper) — use
  it when you need public profile metadata whether or not a bio has an email.
- [TikTok Profile Email Scraper](https://apify.com/automation-lab/tiktok-profile-email-scraper)
  — use it for displayed contact addresses on supplied public TikTok profiles.
- [Website Email Extractor](https://apify.com/automation-lab/website-email-extractor)
  — use it when the public contact is displayed on a website rather than an X bio.

### FAQ

#### Does this Actor need my X login or cookies?

No. The supported workflow uses anonymously visible public profile data.

#### Does it verify email deliverability?

No. It verifies public display and syntax, not mailbox existence or delivery.
Use a dedicated verifier and lawful outreach process when deliverability matters.

#### Does it guess missing emails?

No. Inference would weaken provenance and can create incorrect personal data.
Only addresses present in the source bio are emitted.

#### Can one profile create multiple records?

Yes. If its bio displays multiple distinct complete addresses, the Actor emits
one record per address with the same profile evidence.

#### Are profiles without email charged as items?

No. The run-start event is charged once, while the per-item event applies only
to saved displayed-email records.

#### Can I export to CSV or Excel?

Yes. Use the dataset export menu or API format parameters. The primary result
schema remains stable across JSON, CSV, Excel, XML, and RSS exports.

# Actor input Schema

## `usernames` (type: `array`):

Public X handles, with or without the @ prefix.

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

Public x.com or twitter.com profile URLs. Combined and deduplicated with X handles.

## `maxItems` (type: `integer`):

Stop after saving this many displayed email records.

## `minFollowers` (type: `integer`):

Only save emails from profiles with at least this many followers.

## `emailDomains` (type: `array`):

Optional exact domains such as gmail.com or iso-ent.com. Leave empty to accept every displayed email domain.

## Actor input object example

```json
{
  "usernames": [
    "ZachKing",
    "Valkyrae",
    "Sykkuno"
  ],
  "maxItems": 20,
  "minFollowers": 0
}
```

# Actor output Schema

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

All displayed public email records saved by this run.

# 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 = {
    "usernames": [
        "ZachKing",
        "Valkyrae",
        "Sykkuno"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/x-profile-public-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 = {
    "usernames": [
        "ZachKing",
        "Valkyrae",
        "Sykkuno",
    ],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/x-profile-public-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 '{
  "usernames": [
    "ZachKing",
    "Valkyrae",
    "Sykkuno"
  ],
  "maxItems": 20
}' |
apify call automation-lab/x-profile-public-email-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/x-profile-public-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/A7UXnjXEb2yWMjj79/builds/EiuFt674RVkA473tS/openapi.json
