# Email List Cleaner — Remove Bounces, Disposables & Typos (`pabloalcolea02/email-list-cleaner`) Actor

Clean an email list before a campaign. Removes malformed addresses, dead domains, disposable inboxes and typos, and flags role and free-provider mailboxes. Every address returns a drop / review / keep recommendation with the reason behind it.

- **URL**: https://apify.com/pabloalcolea02/email-list-cleaner.md
- **Developed by:** [PABLO ALCOLEA AGUILAR](https://apify.com/pabloalcolea02) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 email checkeds

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/platform/actors/running/actors-in-store#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

## Email List Cleaner — Remove Bounces, Disposables & Typos Before You Send

Paste a list of email addresses. Get every one back with a **drop / review / keep**
recommendation and the exact reason behind it.

Built for the pass you run *before* a campaign: the one that strips out the addresses
guaranteed to bounce and flags the ones not worth your sender reputation.

***

### What it actually checks

| Check | Catches | Verdict |
|---|---|---|
| Syntax (RFC-pragmatic + full IANA TLD list) | `user@localhost`, `a@b.zzzzz`, `bad@@addr.com` | `drop` |
| MX / A record resolution | Domains that cannot receive mail at all | `drop` |
| Disposable domains (~70) | `mailinator.com`, `yopmail.com`, `temp-mail.org` | `drop` |
| Typo correction | `pablo@gmial.com` → *did you mean `pablo@gmail.com`?* | flagged |
| Role accounts | `info@`, `sales@`, `support@` — low reply rates | `review` |
| No-reply mailboxes | `no-reply@`, `bounce@`, `postmaster@` | `review` |
| Free providers | `gmail.com`, `hotmail.es`, `gmx.de` and ~90 more | `review` |
| Mail provider detection | Google, Microsoft, Zoho, Proton, OVH, IONOS… | info |

On a typical scraped list this removes a meaningful chunk of guaranteed bounces before
you ever load it into a sending tool.

***

### What it does NOT do — read this before you buy

**It does not confirm that an individual mailbox exists.**

Confirming a specific mailbox requires an SMTP conversation on port 25, and **Apify's
network blocks outbound port 25** — every connection times out. That is a platform
limit, not something any Actor here can work around, so this one does not claim to.

The consequence is concrete: an address on a live domain with clean syntax comes back as
`keep` / `unknown` with the reason `domain_ok_mailbox_unconfirmed`. That means *"the
domain accepts mail and nothing is wrong with this address"* — **not** *"this mailbox
exists"*.

If you need mailbox-level confirmation, use a dedicated verification service. If you
need to strip the obvious garbage out of a list cheaply, that is exactly what this does.

> The SMTP code ships in the Actor and works where port 25 is open, so a self-hosted run
> gets full mailbox verification. On Apify it stays off, and the option says so.

***

### Output

One record per address. Real, unedited results:

```json
{ "email": "x@mailinator.com",     "recommendation": "drop",   "status": "undeliverable",
  "score": 0,  "reason": "disposable_domain",         "isDisposable": true }

{ "email": "no-existe@este-dominio-fantasma-zz9.com", "recommendation": "drop",
  "status": "undeliverable", "score": 0, "reason": "domain_cannot_receive_email", "hasMx": false }

{ "email": "info@hetzner.com",     "recommendation": "review", "status": "risky",
  "score": 60, "reason": "role_account",              "isRoleAccount": true,
  "hasMx": true, "mxHost": "mail.hetzner.company" }

{ "email": "pablo@gmial.com",      "recommendation": "keep",   "status": "unknown",
  "score": 70, "reason": "domain_ok_mailbox_unconfirmed", "didYouMean": "pablo@gmail.com" }

{ "email": "maria.lopez@hetzner.com", "recommendation": "keep", "status": "unknown",
  "score": 70, "reason": "domain_ok_mailbox_unconfirmed", "mailProvider": "other" }
```

#### How to use the output

- Filter `recommendation = "drop"` and delete those. They will bounce.
- Review `recommendation = "review"` against your own policy — role accounts are fine
  for some campaigns and useless for others.
- Send to `recommendation = "keep"`.
- Check `didYouMean` before deleting anything: a typo is a recoverable contact, not a
  dead one.

`reason` always names the rule that produced the verdict, so you can apply your own
thresholds instead of trusting a single opaque score.

***

### Pricing

**$0.31 per 1,000 addresses.**

| Charge | Price |
|---|---|
| Email checked | $0.30 / 1,000 |
| Dataset record | $0.01 / 1,000 |
| Actor start | $0.00005 per GB, once per run |

Priced deliberately below mailbox-verification services, because this is a cleaning
pass, not mailbox verification. No monthly rental, no subscription. Duplicates are
removed (case-insensitive) before anything is charged.

> The last two lines are Apify platform events that every Actor on the Store carries and
> that cannot be switched off. They are listed here rather than left for you to discover
> on the invoice.

***

### Pairs with

**[Website Contact Scraper](https://apify.com/pabloalcolea02/website-contact-scraper)** —
turns a list of domains into contacts (emails, phones, socials, company data). Run that
first to build the list, then this one to clean it before you send.

Same author, same billing model: you only pay for what actually comes back.

### Lawful use

This Actor only inspects addresses **you already hold**. It does not discover, guess or
generate addresses, and it sends no mail. You remain the data controller for your list:
in the EU/UK you still need a lawful basis under GDPR to contact those people and must
honour opt-outs. Cleaning a list does not make it lawful to mail it.

# Actor input Schema

## `emails` (type: `array`):

One address per line. Duplicates are removed automatically (case-insensitive), so you are never billed twice for the same address in one run.

## `verifySmtp` (type: `boolean`):

Off by default and intentionally so: Apify blocks outbound port 25, so mailbox-level SMTP checks time out on this platform. The code is kept for self-hosted runs where port 25 is open. Leaving this on will only make runs slower, not more accurate.

## `detectCatchAll` (type: `boolean`):

Only has an effect when the SMTP check above is possible, which it is not on Apify. Left here for self-hosted runs.

## `smtpTimeoutSecs` (type: `integer`):

Seconds to wait for the mail server.

## `domainConcurrency` (type: `integer`):

Addresses are grouped by domain and each domain gets a single SMTP connection. Keep this moderate to avoid rate limiting from large providers.

## Actor input object example

```json
{
  "emails": [
    "info@hetzner.com",
    "someone@mailinator.com",
    "pablo@gmial.com",
    "no-reply@github.com",
    "broken@@address.com",
    "hello@este-dominio-fantasma-zz9.com"
  ],
  "verifySmtp": false,
  "detectCatchAll": true,
  "smtpTimeoutSecs": 10,
  "domainConcurrency": 8
}
```

# 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 = {
    "emails": [
        "info@hetzner.com",
        "someone@mailinator.com",
        "pablo@gmial.com",
        "no-reply@github.com",
        "broken@@address.com",
        "hello@este-dominio-fantasma-zz9.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pabloalcolea02/email-list-cleaner").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 = { "emails": [
        "info@hetzner.com",
        "someone@mailinator.com",
        "pablo@gmial.com",
        "no-reply@github.com",
        "broken@@address.com",
        "hello@este-dominio-fantasma-zz9.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("pabloalcolea02/email-list-cleaner").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 '{
  "emails": [
    "info@hetzner.com",
    "someone@mailinator.com",
    "pablo@gmial.com",
    "no-reply@github.com",
    "broken@@address.com",
    "hello@este-dominio-fantasma-zz9.com"
  ]
}' |
apify call pabloalcolea02/email-list-cleaner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pabloalcolea02/email-list-cleaner"
        }
    }
}

```

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/Kwokwx3f3ADgd0p1y/builds/QLlBkitVEdO8wztn6/openapi.json
