# Email List Cleaner & Deliverability Pre-Check (`burly_bat/email-list-cleaner-deliverability-check`) Actor

Clean email lists in bulk: syntax, MX/DNS, disposable, role and typo detection, dedup, plus per-domain mail provider, SPF and DMARC intelligence. $0.50/1000 decisive results; temporary failures and duplicates are never charged. Input: JSON, raw text, a file URL or any scraper's Apify dataset.

- **URL**: https://apify.com/burly\_bat/email-list-cleaner-deliverability-check.md
- **Developed by:** [Burly Bat](https://apify.com/burly_bat) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 & Deliverability Pre-Check

Clean any email list before you import it into a CRM or a sending tool. Point this Actor at a
scraped lead list, a CSV, a pasted column or **another Actor's dataset**, and get back a
send-safe list plus a per-domain deliverability profile.

**You pay $0.50 per 1,000 decisive results.** Duplicates, temporary DNS failures and run
errors are delivered free of charge.

### Quick start

1. Paste addresses into **Email addresses**, or set **Apify dataset ID** to the dataset of a
   scraper run (for example a Google Maps or lead-generation Actor) and name the column in
   **Email field**.
2. Run the Actor.
3. Download **CLEAN\_LIST.csv** (send-safe addresses) or the full dataset.

```json
{ "datasetId": "YOUR_SCRAPER_DATASET_ID", "emailField": "email" }
```

### What you get for every address

| Result | Meaning |
|---|---|
| `accepts_mail` | The domain provably receives mail. The individual mailbox was not confirmed. |
| `valid` | The mailbox itself was confirmed over SMTP. Requires outbound port 25 — see below. |
| `invalid` | Broken syntax, or the domain has no mail infrastructure at all (no MX, no A fallback). |
| `risky` | Disposable/throwaway domain, or a catch-all server that accepts every address. |
| `unknown` | A temporary DNS or SMTP failure. **Never charged.** |

Every record also carries:

- `isRole` — shared mailbox (`info@`, `sales@`, `billing@`, `biuro@`, …)
- `isDisposable` — throwaway provider, from the CC0 `disposable-email-domains` blocklist
- `isFreeProvider` — consumer mailbox (Gmail, Outlook, WP.pl, …)
- `didYouMean` — typo correction, e.g. `jane@gamil.com` → `jane@gmail.com`
- `mailProvider` — who actually hosts the mailboxes: `google_workspace`, `microsoft_365`,
  `zoho`, `proofpoint`, `mimecast`, `self_hosted_or_other`
- `spf` / `dmarcPolicy` — the domain's published email-authentication posture
- `confidence` and `verificationDepth` — how far the check actually got

Duplicates are removed before any work is done, so you are never charged twice for the same address.

### Honest note on SMTP and the `valid` status

Mailbox-level verification requires an outbound connection on port 25. **Apify's platform blocks
outbound port 25**, and so do most cloud providers. This Actor therefore does **not** claim to
confirm individual mailboxes when it runs here: a healthy address is reported as `accepts_mail`,
never as `valid`. `smtpCheck` is off by default because leaving it on only slows the run down.

If you run this Actor on your own infrastructure with port 25 open, enable `smtpCheck` (and
optionally `catchAllCheck`). The probe performs a standard `RCPT TO` handshake and disconnects —
**no message is ever sent** — and results then reach `valid` / `mailbox_not_found` depth.

We would rather tell you this than sell you a `valid` we cannot prove.

### What this removes from a scraped list

- addresses that cannot exist (malformed syntax)
- domains with no mail server at all — the single biggest source of hard bounces
- disposable and throwaway addresses
- duplicates, including the same mailbox written in different letter cases
- typo'd provider domains such as `gamil.com`, which are parked, accept mail and act as spam traps
- role accounts, separated out so you can decide whether to keep them

### Deliverability intelligence

With `domainIntel` enabled (default), each unique domain is resolved once for its mail provider,
SPF record and DMARC policy. Two cached DNS lookups per **domain**, not per address.

Useful for segmenting B2B lists (`google_workspace` vs `microsoft_365`), and for spotting domains
with no SPF or no DMARC before you send to them.

### Output

| Key | Contents |
|---|---|
| dataset | one record per unique address, all fields above |
| `CLEAN_LIST.csv` | send-safe addresses only: deliverable, non-role, no suspected typo |
| `RESULTS.csv` | every address with every flag, for spreadsheets and CRM import |
| `REPORT` | self-contained HTML report |
| `SUMMARY` | Markdown summary |
| `OUTPUT` | JSON summary with counts, provider breakdown and the billable ratio |

### Billing

One `email-checked` event per address that reaches a decisive result: `accepts_mail`, `valid`,
`invalid` or `risky`. Not charged: `unknown` (temporary DNS/SMTP failure), duplicates removed
before the check, and addresses skipped by your run charge limit. The Actor caps its own input to
your run charge limit before doing any work.

### Data processing

Addresses you submit are processed only inside your own run and stored in your own dataset and
key-value store. Nothing is retained, shared or resold by this Actor. Use it for list hygiene on
data you are allowed to process — it is not a tool for unsolicited mass email, which Apify's
Acceptable Use Policy prohibits.

### Sources and limits

- Disposable domain blocklist: [`disposable-email-domains`](https://github.com/disposable-email-domains/disposable-email-domains) (CC0), vendored and refreshed with each release.
- Syntax follows practical RFC 5321/5322 rules for unquoted local parts. Quoted local parts
  (`"a b"@example.com`) are rejected; they are legal but effectively never deliverable in practice.
- MX resolution honours the RFC 5321 implicit A/AAAA fallback and RFC 7505 "null MX".
- `accepts_mail` is a domain-level guarantee, not a mailbox-level one. Treat it as "safe to send,
  bounce still possible", which is exactly what a DNS-level check can honestly promise.

# Actor input Schema

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

List of email addresses to verify. Entries may contain surrounding text (for example "Jane Doe <jane@example.com>") — addresses are extracted automatically.

## `rawText` (type: `string`):

Paste a CSV column, an exported list or any text. Every email-shaped token is extracted and deduplicated.

## `fileUrl` (type: `string`):

Public HTTP(S) URL of a plain-text or CSV file with email addresses. Must be a direct link — redirects are not followed, and private, local and metadata addresses are blocked.

## `datasetId` (type: `string`):

Verify emails straight from another Actor's dataset (for example a Google Maps or lead scraper run). Set the column name in "Email field".

## `emailField` (type: `string`):

Field name holding the email address inside dataset items.

## `domainIntel` (type: `boolean`):

Resolve each domain's mail provider (Google Workspace, Microsoft 365, Zoho, ...) plus its SPF and DMARC records. Adds two cached DNS lookups per domain, not per address.

## `smtpCheck` (type: `boolean`):

Attempt a mailbox-level SMTP probe (RCPT TO, no message is sent). Apify blocks outbound port 25, so this stays off by default: leaving it on only slows the run down. Turn it on when self-hosting the Actor on infrastructure with port 25 open — results then reach `valid` / `mailbox_not_found` depth.

## `catchAllCheck` (type: `boolean`):

Also probe a random non-existent mailbox on the same domain to detect catch-all servers. Requires the SMTP check and therefore outbound port 25.

## `maxEmails` (type: `integer`):

Hard cap of addresses processed in one run.

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

Parallel verification workers. SMTP probes are serialized per mail server regardless of this value.

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

Timeout for connecting to a mail server and for each SMTP reply.

## Actor input object example

```json
{
  "emails": [
    "support@apify.com",
    "jane@gamil.com",
    "test@mailinator.com",
    "x@nonexistent-domain-abc987xyz.com"
  ],
  "emailField": "email",
  "domainIntel": true,
  "smtpCheck": false,
  "catchAllCheck": false,
  "maxEmails": 10000,
  "maxConcurrency": 20,
  "smtpTimeoutSecs": 8
}
```

# Actor output Schema

## `results` (type: `string`):

One record per unique address with status, reason, confidence, depth and deliverability intelligence.

## `cleanList` (type: `string`):

Send-safe addresses only: deliverable domains (accepts\_mail, or SMTP-confirmed valid), with role accounts and suspected typos excluded.

## `resultsCsv` (type: `string`):

All verified addresses with every flag, ready for spreadsheets or CRM import.

## `summary` (type: `string`):

Counts per status, depth breakdown, duplicates removed and billable ratio.

## `htmlReport` (type: `string`):

Self-contained, safely escaped report for browser review.

## `markdownSummary` (type: `string`):

Portable summary for tickets, docs or pull requests.

# 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": [
        "support@apify.com",
        "jane@gamil.com",
        "test@mailinator.com",
        "x@nonexistent-domain-abc987xyz.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("burly_bat/email-list-cleaner-deliverability-check").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": [
        "support@apify.com",
        "jane@gamil.com",
        "test@mailinator.com",
        "x@nonexistent-domain-abc987xyz.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("burly_bat/email-list-cleaner-deliverability-check").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "support@apify.com",
    "jane@gamil.com",
    "test@mailinator.com",
    "x@nonexistent-domain-abc987xyz.com"
  ]
}' |
apify call burly_bat/email-list-cleaner-deliverability-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=burly_bat/email-list-cleaner-deliverability-check",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ZlrSrHRcDugToAymQ/builds/nyubJTGhS99wVqqbS/openapi.json
