# Email List Cleaner (`arched_friend/email-list-cleaner`) Actor

Clean an email list before you import it. Removes malformed addresses and dead domains, flags disposable inboxes and shared role addresses, fixes obvious typos, and drops duplicates, so you stop paying to send to addresses that were never going to land.

- **URL**: https://apify.com/arched\_friend/email-list-cleaner.md
- **Developed by:** [Peach O](https://apify.com/arched_friend) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 address 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/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

## Email List Cleaner: Cut the Dead Weight Before You Send

**Strip the addresses that were never going to land, before they cost you money and reputation.**

Removes malformed addresses and dead domains, flags disposable inboxes and shared team addresses, fixes obvious typos, and drops duplicates. The cheap first pass every list should go through before you import it anywhere.

Built for sales teams, marketers and anyone about to load a list into a sending tool or a paid verifier.

### How it works

```mermaid
flowchart LR
    A[Your list] --> B[Drop duplicates]
    B --> C{Syntax}
    C -->|malformed| X[invalid]
    C -->|ok| D{Domain and mail server}
    D -->|no mail server| X
    D -->|ok| E{Disposable or role?}
    E -->|yes| Y[risky]
    E -->|no| Z[looks deliverable]
    C -->|typo| S[suggest a fix]
```

Every check here runs off the address and its domain, so it works anywhere and costs almost nothing. No mail is ever sent.

### What each verdict means

```mermaid
flowchart TD
    A[Every address] --> B[invalid]
    A --> C[risky]
    A --> D[unknown]
    B --> B1[Malformed or the domain cannot receive mail. Remove it.]
    C --> C1[Deliverable but a bad idea. Disposable or a shared role inbox.]
    D --> D1[Looks fine at the domain level. Not individually confirmed.]
```

`unknown` is the honest verdict for an address that passes every check we can run without contacting the mailbox. It is a clean address to the best of what a list level pass can tell, which is exactly what this tool is for.

### What it catches

| Problem | Example | Verdict |
| --- | --- | --- |
| Malformed address | `bad syntax@x.com` | invalid |
| Domain does not exist | `a@this-is-not-real-xyz.com` | invalid |
| Domain cannot receive mail | a parked domain with no mail server | invalid |
| Disposable inbox | `x@mailinator.com` | risky |
| Shared role address | `info@acme.com` | risky |
| Obvious typo | `someone@gmial.com` | unknown, with a fix |
| Duplicate | the same address twice | removed |

### Input

```json
{
  "emails": ["ruth@acme.com", "info@acme.com", "someone@gmial.com", "x@mailinator.com"],
  "onlyDeliverable": false,
  "dropDuplicates": true
}
```

| Option | Does |
| --- | --- |
| `emails` | Required. Paste a list or feed it from another Actor. |
| `dropDuplicates` | Remove repeats first, so you never pay twice for one address. |
| `onlyDeliverable` | Return only the addresses worth keeping. |
| `includeRisky` | Keep role and disposable addresses in the deliverable set. Off by default. |
| `checkSmtp` | Also ask the mail server whether the mailbox exists. Needs port 25, which Apify blocks, so off by default. |

### Output

One row per address, with the reason behind every verdict.

```json
{
  "email": "info@acme.com",
  "status": "risky",
  "reason": "role_address",
  "score": 50,
  "syntaxValid": true,
  "hasMailServer": true,
  "mailServer": "aspmx.l.google.com",
  "isDisposable": false,
  "isRoleAddress": true,
  "suggestedEmail": null,
  "verifiedAt": "2026-09-02T16:04:11.882Z"
}
```

A typo gets a correction rather than a shrug:

```json
{
  "email": "someone@gmial.com",
  "status": "unknown",
  "suggestion": "gmail.com",
  "suggestedEmail": "someone@gmail.com"
}
```

Export to JSON, CSV or Excel, or send it straight back into your sending tool.

### Clean a list in one run

```json
{
  "emails": ["..."],
  "onlyDeliverable": true,
  "includeRisky": false
}
```

What comes back is the list minus the addresses that would bounce, the throwaway inboxes and the shared role addresses. Import that instead of the raw file.

### Run it as an API

```bash
curl -X POST "https://api.apify.com/v2/acts/arched_friend~email-list-cleaner/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"emails": ["ruth@acme.com"], "onlyDeliverable": true}'
```

Wire it into a signup form to reject junk at the door, or run it over your CRM monthly to strip addresses that have gone dead.

### Where it fits

This is the fast, cheap first pass. It removes the addresses that are provably bad from the shape of the address and its domain, which is a large share of most lists. What it does not do is knock on each mailbox to confirm a specific person exists, because that needs outbound port 25 and Apify blocks it.

So the sensible flow is to run this first, then send whatever survives to a full mailbox verifier if you want that last layer. You will be paying that verifier for a much smaller, already clean list.

### Pricing

You pay per address checked, duplicates removed first.

| Option | Cost per 1,000 |
| --- | --- |
| Email List Cleaner | $3 |
| Full mailbox verifiers such as ZeroBounce | About $8 to $10 |
| Sending to a list you never cleaned | Bounces, spam traps and a damaged sending domain |

New Apify accounts get free monthly credits, so the first runs cost nothing.

### Common questions

**Does it confirm every mailbox exists?**
No, and it does not claim to. It removes what is provably bad at the address and domain level, which is the cheap majority of the work. Confirming an individual mailbox needs port 25, which Apify and most cloud platforms block. Run a full verifier after this on the smaller surviving list.

**Then why run this at all?**
Because most of a dirty list is not subtle. Typos, dead domains, disposable inboxes and duplicates make up a large slice, and clearing them here for a few dollars means you are not paying a pricier verifier, or your sending tool, to handle them.

**What is a role address and why is it risky?**
Addresses like info@ or support@ reach a shared inbox rather than a person. They are usually deliverable but get poor replies, and some become spam traps. Set `includeRisky` if you want them kept.

**Can it talk to the mail server if I run it somewhere port 25 is open?**
Yes. Turn on `checkSmtp` and, where the network allows it, the mailbox check runs and upgrades verdicts to a confirmed valid or invalid. On Apify it stays off because the port is blocked.

**Is this legal?**
It reads the addresses you supply and looks up public DNS records. Nothing is sent and no personal data is collected beyond your own list.

### Related products

- **Website Lead Extractor** to find the addresses in the first place
- **Lead Enrichment Pipeline** to build the whole list, then clean it here
- **Tech Stack Checker** to qualify the companies behind the addresses

# Actor input Schema

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

The addresses to verify. Paste a list, or feed it straight from another Actor's output.

## `checkSmtp` (type: `boolean`):

Opens a conversation with the mail server to confirm the mailbox exists, without sending anything. Off by default because it needs outbound port 25, which Apify and most cloud platforms block. Turn it on only where you know port 25 is open.

## `checkCatchAll` (type: `boolean`):

Also asks about an address nobody owns. A domain that accepts that accepts everything, so a positive answer there means nothing and is reported as risky rather than valid.

## `dropDuplicates` (type: `boolean`):

Removes repeated addresses before verifying, so you are not charged twice for the same one.

## `onlyDeliverable` (type: `boolean`):

Keeps only the addresses worth sending to and drops the rest from the output.

## `includeUnknown` (type: `boolean`):

Large providers refuse to confirm a mailbox. Those come back as unknown, and most senders still mail them.

## `includeRisky` (type: `boolean`):

Includes role addresses, disposable inboxes and catch all domains. Off by default, because these are what damage a sending reputation.

## `concurrency` (type: `integer`):

How many domains to work through in parallel. Lower this if a mail server starts rate limiting.

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

How long to wait for a mail server before giving up and reporting unknown.

## `fromEmail` (type: `string`):

The address the probe identifies itself with. Empty uses the null sender, which is what a bounce message uses. Supplying an address on a domain you own can improve answers from strict servers.

## `heloName` (type: `string`):

The hostname the probe introduces itself as. Some strict servers prefer a real one.

## Actor input object example

```json
{
  "emails": [
    "team@huel.com"
  ],
  "checkSmtp": false,
  "checkCatchAll": true,
  "dropDuplicates": true,
  "onlyDeliverable": false,
  "includeUnknown": true,
  "includeRisky": false,
  "concurrency": 5,
  "smtpTimeoutSecs": 10,
  "heloName": "verifier.local"
}
```

# Actor output Schema

## `verifiedEmails` (type: `string`):

One row per address, with its status, the reason behind it and every underlying signal.

## `runSummary` (type: `string`):

How many addresses came back valid, invalid, risky or unknown, plus duplicates dropped and mail servers reached.

# 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": [
        "team@huel.com",
        "hello@octopus.energy",
        "someone@gmial.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arched_friend/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": [
        "team@huel.com",
        "hello@octopus.energy",
        "someone@gmial.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("arched_friend/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": [
    "team@huel.com",
    "hello@octopus.energy",
    "someone@gmial.com"
  ]
}' |
apify call arched_friend/email-list-cleaner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arched_friend/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/vXhFTz7p5EVcHOpZQ/builds/zW2ec8yA2DgAfeKTQ/openapi.json
