# Email List Cleaner: Bulk Email Verify, Keep Qualified Emails (`rich_minds/email-list-qualifier-ai`) Actor

First 25 qualified emails free. Email list cleaner: check email deliverability (MX, disposable, role, typo), score ICP fit with AI and bill only qualified emails (≈ $1.10 per 1,000 cleaned). Works on any plan; the optional deep SMTP check needs a plan that runs Store Actors.

- **URL**: https://apify.com/rich_minds/email-list-qualifier-ai.md
- **Developed by:** [Rich Minds](https://apify.com/rich_minds) (community)
- **Categories:** Lead generation, Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 qualified email (rules only)s

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Email List Cleaner: Bulk Email Verify, Keep Qualified Emails

**Every week, only the emails worth sending — domain-checked, on-ICP, billed per keeper.**

⚡ First **25 qualified emails free** · 💵 **$0.002** per qualified email · 🤖 **$0.0404** all in with the AI fit score and call · 🧮 **≈ $1.10 per 1,000 addresses cleaned, all in** (rules only; you pay for the keepers, not the junk) · ⏱️ demo in seconds

![One row per qualified email — fit score, label, send call and the reason, best first](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/shortlist.svg)

> **Try it in 30 seconds.** Click **Try it** — the form is already filled with 7 sample addresses (4 qualify, 3 are
> dropped for free). Then paste your own emails or a CSV / Google Sheet link: checked inside this Actor on any Apify
> plan, no second bill. The built-in check is domain-level; the **Deep mailbox check** preset adds SMTP.
> **Check one email:** [one-click example input](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/check-one-email.json) → a send / skip verdict in `OUTPUT.verdict`.
> → [What a run costs](#-pricing--what-a-run-really-costs) · [What the AI adds](#-what-the-ai-tier-adds) · [Run it weekly](#-run-it-weekly)

### ⚡ At a glance

| | |
|---|---|
| **What you get** | the emails of your list that are deliverable and on-ICP — each with a fit score 0-100, reasons, first name, seniority and a send / send-with-care / skip call — plus three cleaned CSV lists (sendable, risky, bad) |
| **You provide** | a list of emails (paste, a CSV / Google Sheet link, an Apify dataset, or scraper rows) and one sentence on who you want to reach |
| **Output** | a ranked sheet, best first (CSV, Excel, Google Sheets), the cleaned lists, the dropped rows with their reason, an e-mail digest |
| **How deep the check goes** | built-in: syntax, mail server (MX), disposable, role, typo. `sendable.csv` = lowest expected bounce, `risky.csv` = middle band, `bad.csv` = never send. Dead mailboxes need the **Deep mailbox check** preset (SMTP, ≈ $0.00089 per address extra) |
| **Typical run** | 100 emails → ≈ 55 qualified in seconds (built-in check: 100 addresses on 50 domains in 0.7 s, measured locally 2026-10-04; the 55 % keep rate is an estimate) |
| **Cost of that run** | $0.11 rules only · $2.22 with AI (55 × $0.002 / 55 × $0.0404 with tokens) — no second bill |
| **Free tier** | first 25 qualified emails per account |
| **Keys / setup** | 🔑 Apify plan with model access: no key · other plans: your own Gemini / Groq key (free = 1–3 live AI verdicts per run, paid = every qualified email) |
| **Which plan** | Free demo, the built-in check and your own rows ([template](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/list-template.json), exported from your CRM or Sheet): any plan · the optional deep SMTP check: a plan that runs Store Actors |
| **Agencies** | 10 clients × 1,000 addresses a month ≈ $11 rules only; one memory name and digest each (`clients` mode dated 2026-10-31) |
| **Works with** | Schedules, webhooks, Sheets, Slack, Instantly / Smartlead / Mailchimp, Make / n8n / Zapier, MCP |

### 🎯 What this Actor does

**Email list cleaner** and qualifier in one run: verify email list exports from a scraper, a webinar or a CRM and keep only what is safe and relevant to send to.

- **Only keepers are billed here.** Invalid, disposable, role (info@), catch-all, low-score, deny-listed and duplicate addresses are dropped for free and listed with the reason.
- **Checked inside the Actor.** Syntax, domain and mail server (MX), disposable domains, role mailboxes and typos, on any plan. It does not probe the mailbox, so it cannot see a catch-all domain or a dead mailbox; the **Deep mailbox check** preset adds BounceVerify's SMTP check.
- **An AI ICP-fit score with reasons** against *your* ideal recipient, citing only the fields you supplied — and a call per address: send, send with care or skip.
- **Never twice.** Charged addresses are remembered, not even re-verified.

### 🚀 How to use it

1. **Click `Try it`** — the form is pre-filled with the free demo on sample addresses; it works as is.
2. **Paste your emails — or a CSV / Google Sheet link — and describe who you want** ("Emails to qualify", "Or your list as a CSV link", "Who is your ideal recipient?"). They replace the sample and start the live check; leave "AI assessment" on (your first 5 qualified emails get live AI verdicts on any plan). Want the price first? Switch on "Estimate the bill only".
3. **Press Start and open the key-value store** — three CSV lists (sendable, risky, bad) are ready to import; ranked rows are in the dataset, dropped ones with their reason in `REJECTED`.
4. **Schedule it weekly** (Actions → Schedule) with your address in "E-mail me the new qualified emails" — only new addresses are charged.

![The outreach sheet — first name, seniority, company type, the call and the next step](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/outreach.svg)

📚 **Learn more:** [Step-by-step tutorial — how to clean an email list and keep only the sendable emails](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/tutorial.html)

### 🆚 Why this instead of running bounceverify/bounceverify-email-verifier yourself?

| | [BounceVerify (`bounceverify/bounceverify-email-verifier`)](https://apify.com/bounceverify/bounceverify-email-verifier) | `clearpath/email-validation-api` | `automation-lab/email-mx-verifier` | **This Actor** |
|---|---|---|---|---|
| **Price per email** | $0.00089 · 1,114 users / 30 days | $0.008 · 19 users | $0.00092 + $0.035 start · 5 users | from $0.002, first 25 free |
| **Billed for** | every row | every row | every row | qualified emails only |
| **100 qualified emails, all in** | ≈ $0.16 (182 raw rows), then you filter by hand | ≈ $1.46 (SMTP and catch-all checks) | ≈ $0.20, then you filter by hand | $0.20 rules only for 100 ranked, explained emails · $4.04 with AI |
| **Your time per 100 qualified emails** | ≈ 3 h by hand (≈ 2 min each, an estimate) | ≈ 3 h | ≈ 3 h | 0 h — filtered, ranked, explained |

<details><summary>Against ZeroBounce, NeverBounce and Hunter</summary>

**Against the hosted tools** (ZeroBounce, NeverBounce, Hunter — what searchers of "alternative" compare):

| | ZeroBounce | NeverBounce | Hunter | **This Actor** |
|---|---|---|---|---|
| **$ per 1,000** | see their pricing page — not quoted until read (dated 2026-11-01) | same | same | ≈ $1.10, rules only, keepers only |
| **Check depth** | mailbox (SMTP), catch-all, spam traps | mailbox (SMTP) | mailbox (SMTP) | domain-level built in; mailbox via the deep preset |
| **Plan** | their subscription or credits | their subscription or credits | their subscription | any Apify plan, pay per keeper |

</details>

<details><summary>Where the prices come from</summary>

Prices from the Store pages, 2026-10-04. Honest read: plain MX cleaners (e.g. `burly_bat/email-list-cleaner-deliverability-check`) are cheaper per row, and BounceVerify and `clearpath/email-validation-api` probe the mailbox where the built-in check does not. This Actor is for the buyer whose cost is hand-filtering and bounced sends.

</details>

### 💵 Pricing — what a run really costs

Pay per result: **one event per qualified email, no start fee.**

| Event | When it is charged | Price |
|---|---|---|
| `free-tier` | your first 25 qualified emails on this Actor | **$0.00** |
| `qualified-email-basic` | AI off — verifier fields, flags, rule score, call | **$0.002** |
| `qualified-email-ai` | AI on — plus fit score, reasons, first name, seniority, company type, the AI's note | **$0.04** |

**One free tier:** your first 25 qualified emails are free, whatever the tier. **Never charged:** invalid or unknown addresses, disposable, role, catch-all and free-mail addresses you excluded, deny-listed domains, rows below the score floors, duplicates and addresses you already received. The built-in check has **no second bill**; with `ownSource` off BounceVerify bills ≈ $0.00089 per address on your account.

**How that compares** — $0.002 is 2.7× one raw row of the cheapest verifier ($0.000736), but one qualified email replaces ≈ 1.8 raw rows ($0.0013–$0.0016 per raw-row equivalent); per 1,000 addresses ≈ $1.10 here against $0.74–$0.89 for raw verifiers that leave the filtering to you.

**Worked example** — 100 addresses → 55 qualified (estimate): 55 × $0.002 = **$0.11 rules only**; with AI 55 × $0.0404 (the $0.04 plus ≈ $0.0004 tokens each) = **$2.22 all in**. Deep check on: add $0.09 per 100 addresses on your account.

<details><summary>Agency volume and the AI tier all in</summary>

**At volume** — Agency, 10 clients × 1,000-address lists a month (5,500 qualified): ≈ $11 rules only, ≈ $222 with AI. **AI tier, all in:** ≈ $0.0404 per qualified email ($0.04 + ≈ $0.0004 tokens, estimated: ≈ 120 tokens in / 60 out) through Apify's model access or your own key.

</details>

### 🤖 What the AI tier adds

<details><summary>Rule-only row vs AI-enriched row</summary>

The same address, AI **off** — a verified address and a generic send call (abridged):

```json
{"email": "d.okafor@brightpath.co", "verifierScore": 91, "disposition": "send",
 "dispositionNote": "Send: verifier valid, score 91.", "suggestedAction": "Load into your sequencer now — verified and safe to send."}
```

AI **on** (`qualified-email-ai`, $0.04) — who it is and what to do, judged against *your* ICP (the row a real run of `INPUT.ai.json` wrote on 2026-10-16 with the own-key default model, ≈ 108 output tokens per email):

```json
{"email": "d.okafor@brightpath.co", "aiModel": "google:gemini-3.5-flash-lite", "fitScore": 90, "label": "hot",
 "seniority": "founder", "companyType": "B2B SaaS",
 "fitReasons": ["title: Founder & CEO", "company: BrightPath Studio", "verifierScore: 91"], "disposition": "send",
 "dispositionNote": "Valid personal address with a verifier score of 91 and direct founder context."}
```

</details>

By hand: ≈ 2 minutes per email to judge fit (an estimate).

**Try the AI on any plan:** without model access the demo shows labelled sample verdicts, and your first real run gets **5 live AI verdicts on us**; after that use your own key (`llmProvider: byok`): a free Gemini key ([aistudio.google.com/apikey](https://aistudio.google.com/apikey)) assesses 1–3 emails per run, a paid key every qualified email.

### ⚙️ Input

The fields that decide the bill: `emails` / `csvUrl` (your list), `icpDescription` (who you want), `minScore` (default 50 — the field that decides what you pay), `allowRoleEmails` / `allowCatchAll` (keep `info@`-style and catch-all addresses, flagged), `maxQualified` (default 100, the hard cap), `enableAi` (default on), `ownSource` (default on: built-in check, no second bill). Every field with its default: API reference.

More inputs: `estimateOnly`, `allowFreeProviders`, `notifyEmail`, `allowDomains` / `denyDomains`, `suppressionList`, `fixTypos`, `sendAlertsTo` (one field for Slack, Discord, Teams, Google Chat, Telegram or any webhook URL — the service is read from the address), `webhookHeaders`, `webhookSecret`; the rest: API reference.

`targetFlags` values (`personal_address`, `business_domain`, `has_context`, `typo_corrected`, `role_based`, `catch_all`, `free_provider`, `low_verifier_score`): API reference.

### 📤 Output

One dataset item per qualified email — export as JSON, CSV or Excel, or stream it to a webhook. The cleaned list is also three files in the key-value store: `sendable.csv` (call `send`), `risky.csv` (`send-with-care`, `skip`, role, catch-all, free-mail and low-score addresses, with the reason) and `bad.csv` (invalid, disposable, no mail server); your own columns come along.

<details><summary>Example output row</summary>

```json
{
  "email": "maya.chen@northwind-analytics.io", "contactRoute": "maya.chen@northwind-analytics.io", "verifierStatus": "valid",
  "verifierScore": 96, "verifierMethod": "smtp", "smtpValid": true, "isCatchAll": false, "riskType": null,
  "contextFields": {"company": "Northwind Analytics", "title": "Head of Growth"}, "ruleScore": 98, "score": 95,
  "assessedBy": "ai", "label": "hot", "fitScore": 95, "firstName": "Maya", "companyType": "analytics software",
  "disposition": "send", "aiModel": "google:gemini-3.5-flash-lite", "chargedEvent": "demo",
  "dedupeKey": "email:maya.chen@northwind-analytics.io"
}
```

</details>

This is the AI run's row (2026-10-16 run of `INPUT.ai.json`); a row the built-in check verified has `verifierMethod: "builtin"` and `smtpValid: null`. Sort one run by `score`; `assessedBy` says which scale it is on (`ai`, `sample` or `rules`) and the AI-assessed rows lead their label — trend across runs on `ruleScore`.

<details><summary>Every field of a row</summary>

| Field | Description |
|---|---|
| `itemId`, `name`, `email`, `contactRoute`, `url`, `domain` | Identity; `url` is `mailto:<email>`; stable across runs |
| `verifierStatus`, `verifierScore`, `verifierMethod`, `smtpValid`, `mxFound`, `domainExists`, `syntaxValid`, `verifiedAt` | Verifier findings, `null` when not reported; `verifierMethod` is `builtin` (domain-level) or `smtp` (mailbox) |
| `isCatchAll`, `isDisposable`, `isRoleBased`, `isFreeProvider`, `riskType`, `reason` | Risk flags and the verifier's explanation (`isCatchAll` is `null` with the built-in check) |
| `typoSuggestion`, `correctedFrom` | The correction; the mistyped original on a corrected row |
| `contextFields`, `flags`, `targetFlagsMatched` | Your row's company / title / website, rule flags, matched target flags |
| `ruleScore`, `fitScore`, `score`, `label` | Rule score, AI fit score, final score, hot / warm / cold |
| `fitReasons`, `firstName`, `seniority`, `companyType` | AI: up to 3 reasons, first name, seniority, company type (`null` when unsupported) |
| `disposition`, `dispositionNote`, `suggestedAction` | send / send-with-care / skip, a one-line note, the next step |
| `aiAssessed`, `assessedBy`, `aiModel`, `billedEvent`, `chargedEvent`, `billedAs` | Whether the AI assessed the row and which scale `score` is on (`ai` / `sample` / `rules`); pricing tier and what was charged (`already-charged`: paid for under another list of the same memory, never billed twice) |
| `dedupeKey`, `sourceActor`, `source`, `scrapedAt` | Provenance |
| **Canonical fields** | `url` and `score` are the fields to map; trend on `ruleScore` (same scale every run) |

</details>

⭐ **Found it useful? A review on the Store helps others find it.**

**Upsert key for a CRM or Sheet:** `dedupeKey` (`email:<address>`, lower case) — one address is one row however many runs find it.

Dataset **views**: `Shortlist`, `Outreach sheet` and `Verifier audit`. **`REJECTED`** lists every dropped address with `rejectReason`; a **`DIGEST`** and a client-ready **`REPORT`** follow every run that delivered.

### 🔁 Run it weekly

1. **Open the saved task and click Schedule** after your first real run (`OUTPUT.scheduleTaskUrl`). Put your address in `notifyEmail` ("E-mail me the new qualified emails") and every run with new addresses mails the digest with the trend vs the last run.
2. Point `csvUrl` at a Google Sheet that keeps growing and leave `dedupeAcrossRuns` on — charged addresses are remembered and **not even re-verified**, so a re-run of the same list costs $0. Several clients: one `dedupeStoreName` each.
3. Paste a Slack, Teams, Google Chat, Discord or Telegram link into "Send alerts to", or a Zapier / Make hook, or use Google Sheets with the [tracker sheet](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/tracker.csv) (`dedupeKey` first to upsert on); `historyDatasetName` keeps a dated log.

**Re-check monthly:** a list loses ≈ 2-3 % of its addresses a month; every run states the date to re-check (`OUTPUT.recheckBy`).

**Week 2 is honest:** a static list delivers nothing new (the memory skips every address you have; $0); a sheet gaining 500 rows a week costs ≈ 275 qualified × $0.002 ≈ $0.55 a week, rules only.

#### 🎯 Try it for your niche

Pick one in **Start from a ready niche search** (`preset`), then paste your emails.

Six ready searches in `preset`: `cold-email-agency`, `saas-sdr`, `scraped-leads-cleanup`, `newsletter-cleanup`, `event-attendee-list`, `deep-smtp-check` — yields not measured yet; table with what each looks for: API reference.

### 🔌 Integrations, automation and API

Branch on `OUTPUT.ok` and `OUTPUT.outcome` (`ok` · `empty` · `source_refused` · `source_failed`), never on the run status or an empty dataset; retry only on `source_failed`.

<details><summary>Run outcomes</summary>

| Outcome | Run status | `OUTPUT` | Charged? | What to do |
|---|---|---|---|---|
| Success | SUCCEEDED | `ok: true`, `outcome: ok`, funnel, `rejected`, `cleanFiles`, `aiCoverage` | per qualified email | — |
| Nothing matched | SUCCEEDED | `ok: true`, `outcome: empty`, `qualified: 0`, `rejected` | not by us | loosen `minVerifierScore` / `minScore` |
| Invalid input | FAILED | — | no | fix the field the status names |
| Your plan cannot run the source Store Actor (`ownSource` off) | SUCCEEDED — the same for the Console, the API and a schedule; check `OUTPUT.ok` | `ok: false`, `outcome: source_refused`, `sourceRefused: "plan"` | no | turn `ownSource` on, or paste verified rows in `list` mode |
| Source failed (unreadable CSV link, DNS unreachable twice, the source renamed its fields) | FAILED | `ok: false`, `outcome: source_failed`, `sourceError` | no | follow the reason in the status |
| Source partial (a chunk failed after others delivered) | SUCCEEDED | `sourceStatus` (`failedChunks`, `partial: true`) | per qualified email | re-run the failed part |
| AI unavailable | SUCCEEDED | `aiError`, `aiCoverage` | free while free units last, then basic price | `llmProvider: byok` |

</details>

- **Instantly** — Make / n8n / Zapier: trigger "Actor run succeeded" → "Get record" `sendable.csv` → "Add lead to campaign", upsert on `dedupeKey`.
- **Smartlead / Mailchimp** — the same trigger → "Add leads to campaign" / "Add/update subscriber" from the `sendable.csv` rows; `bad.csv` becomes the suppression list.
- **Webhook** — `webhookUrl` POSTs `{"event": "email.qualified", "email": {…}, "runId": "…"}` per keeper, retried once on 429 / 5xx; set `webhookSecret` and every POST carries `X-Signature: sha256=<HMAC of the raw body>`. A POST that still fails is listed in `OUTPUT.webhook.failedKeys` and not re-sent next run (it was delivered and charged).
- **Verify on form submit** — one address in, one verdict out: `OUTPUT.verdict`; recipes in the [API reference](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/api-reference.md).
- **Google Sheets / Slack** — one click from the **Integrations** tab, or the digest through "Send alerts to".
- **AI agents / MCP** — every input is a plain typed field; the free first call is `{}` (the demo).

#### Already scraping emails? Qualify your email scraper data

**Clean the output of your scraper** — you already ran `bounceverify/bounceverify-email-verifier` or an email scraper (`email_scraper/docker-email-scraper`, `vdrmota/contact-info-scraper`, Google Maps or LinkedIn / Apollo-style lead scrapers): add a webhook on its "run succeeded" → this Actor with `{"sourceMode": "dataset", "datasetId": "<the scraper's defaultDatasetId>", "emailField": "email", "enableAi": false}` — rules only by default, BounceVerify rows are used as they are, bare addresses get the built-in check. More: API reference.

**Minimal agent input** — 35 fields on the form, five matter; every other one has a default (API reference):

```json
{"sourceMode": "actor", "emails": ["a@b.io"], "icpDescription": "Heads of growth at B2B SaaS", "maxQualified": 50, "minScore": 50, "dedupeAcrossRuns": false}
```

<details><summary>Copy-paste: Python, JavaScript and MCP</summary>

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("rich_minds/email-list-qualifier-ai").call(
    run_input={
        # free call instead: {} or {"sourceMode": "list", "itemsList": [{"email": "a@b.io"}]}
        "sourceMode": "actor",
        "emails": ["maya.chen@northwind-analytics.io", "info@lumenlabs.com"],
        "icpDescription": "Founders and heads of growth at B2B SaaS companies",
        "maxQualified": 50,
        "maxDiscoveryChargeUsd": 0.5,
    },
    timeout_secs=3600,
    # build="0.1.6",  # pin the build number your pipeline was tested with (Changelog below); omit for the latest
)
out = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("OUTPUT")["value"]
if not out.get("ok", True) or out.get("sourceError"):  # a failed source is `ok: false`, never "nothing new"
    raise RuntimeError(out.get("sourceError") or "run not ok")
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["url"], item["score"], item["label"], item["disposition"])
```

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('rich_minds/email-list-qualifier-ai').call({
    // free call instead: {} or { sourceMode: 'list', itemsList: [{ email: 'a@b.io' }] }
    sourceMode: 'actor',
    emails: ['maya.chen@northwind-analytics.io', 'info@lumenlabs.com'],
    icpDescription: 'Founders and heads of growth at B2B SaaS companies',
    maxQualified: 50,
    maxDiscoveryChargeUsd: 0.5,
}, { timeout: 3600 /* , build: '0.1.6' — pin the build number your pipeline was tested with */ });
const out = (await client.keyValueStore(run.defaultKeyValueStoreId).getRecord('OUTPUT')).value;
if (out.ok === false || out.sourceError) throw new Error(out.sourceError || 'run not ok'); // never "nothing new"
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((i) => [i.url, i.score, i.label, i.disposition]));
```

**MCP client (Claude, ChatGPT …)** — add Apify's MCP server with this Actor as a tool:

```json
{"mcpServers": {"apify": {"url": "https://mcp.apify.com/?actors=rich_minds/email-list-qualifier-ai"}}}
```

</details>

<details><summary>Which call runs what and list-mode rows</summary>

| Input shape | `OUTPUT.mode` | Charges? |
|---|---|---|
| `{}` / untouched form | `demo` | no |
| your `emails` / `csvUrl`, or `sourceMode: "actor"` | `live` | per keeper |
| `estimateOnly: true` | `estimate` | no |
| `list` + `itemsList`, or `dataset` + `datasetId` | `list` / `dataset` | per keeper |

#### List-mode rows — what a pasted record needs

Start from the [template](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/list-template.json); a row needs only `email`:

| Field | Needed? | What it enables |
|---|---|---|
| `email` | required | the address (or the `emailField` column), identity and dedupe key |
| `status`, `score`, `smtp_valid` | optional | the qualification rules and score floor; rows without a result are checked first |
| `is_catch_all`, `is_disposable`, `is_role_based`, `is_free_provider`, `risk_type` | optional | the risk rules and `riskType` |
| `did_you_mean`, `syntax_valid`, `domain_exists`, `mx_found`, `reason`, `verified_at` | optional | the typo fix and audit fields |
| `company`, `title`, `website`, `name` | optional | `contextFields` and a better AI fit score |

</details>

### 👥 Who is it for?

<details><summary>Who runs it for what</summary>

| You are… | You run it to… | Start with |
|---|---|---|
| Cold-email agency | clear client lists of bounces and role mailboxes before sending | `preset: cold-email-agency`, a `dedupeStoreName` per client |
| SDR / sales team | keep only decision-makers that fit your ICP from a bought or exported list | `preset: saas-sdr` |
| Lead-gen builder | qualify the emails a Google Maps, LinkedIn or Apollo scraper produced | `sourceMode: dataset` + `datasetId` of the scraper run |
| Developer / no-code builder | verify one signup address | `emails: ["a@b.io"]` → `OUTPUT.verdict` |

</details>

### 🧠 How the AI works

One verified address at a time, judged against *your* ICP: every reason cites a field you supplied, no outside facts; default `anthropic/claude-haiku-4.5` via Apify's model access or your own key (`llmProvider: "byok"`). If the model fails, the row keeps its rule score.

### 🔒 Data, compliance and limits

- Only the addresses you supply are processed; nothing is stored outside your account. A lawful basis (GDPR, CAN-SPAM, CASL) for emailing them is yours.
- The built-in check asks public DNS about each distinct domain — never the mailbox. A CSV link must open without a login (25 MB / 200,000 rows).
- A catch-all domain or a dead mailbox needs the **Deep mailbox check** preset (`ownSource` off, BounceVerify, 5,000 addresses per chunk); a catch-all is then dropped unless you allow it.

### ❓ FAQ

**How much will one run cost me?** 100 addresses ≈ $0.11 rules only or ≈ $2.22 with AI, no second bill. The first 25 qualified emails are free; `estimateOnly` prints the number before anything runs.

**Is this a ZeroBounce alternative?** For list hygiene and ICP fit, yes — and a NeverBounce alternative too: it drops invalid, disposable and role addresses and adds a fit score and a send call, billed per keeper. It does not probe the mailbox like theirs; their prices are not quoted here yet.

**Is this a bulk email verifier validator?** Yes, in bulk: domain and mail-server checks, junk cleaning and a fit ranking.

**How do I clean scraped leads?** Point `datasetId` at your scraper's dataset or give a `csvUrl`; keep the company and title columns so the AI can judge fit.

**How do I check email deliverability for a whole list?** Paste it, give a `csvUrl` or a scraper's `datasetId`: every address gets a domain and mail-server check and a call. Need the mailbox itself? Use the **Deep mailbox check** preset.

**How do I verify one email address?** Send `emails: ["a@b.io"]` — the answer is `OUTPUT.verdict`; turn `dedupeAcrossRuns` off for signup checks, or a repeat address is "already delivered".

**How long does a run take?** Pass `timeoutSecs` of 600 + 2 s × qualified emails; `OUTPUT.timing` has the phases:

| Mode | Time | `timeoutSecs` |
|---|---|---|
| Demo, your own verified rows | under 1 s (local run) | 120 |
| Built-in check, rules only | 0.7 s per 100 addresses on 50 domains (local, 2026-10-04) | 300 |
| Deep check (`ownSource` off) | ≈ 1 min per 100 (estimate) | 600 |
| AI via Apify's model access | ≈ 1–2 s per qualified email (estimate) | 1800 |
| AI with a free own key | measured 2026-10-16: a free Gemini key (`google:gemini-3.5-flash-lite`) assessed 4 emails in 14.4 s, ≈ 3.6 s each; past its rate limit the rest keep their rule score after 60 s of waiting | 600 |
| AI with a paid own key | ≈ 1–2 s per qualified email (estimate) | 1800 |

**Which Apify plan do I need?** Any plan runs the demo, the built-in check, `list` mode and `estimateOnly`; the deep SMTP check and model access need a plan that runs Store Actors, else use `llmProvider: "byok"`.

**Will I be charged for the same email twice?** No, with `dedupeAcrossRuns` on: charged addresses are remembered on your account and not verified again. Adding emails keeps the memory; each `dedupeStoreName` and `targetFlags` set keeps its own; after `dedupeTtlDays` days an address may return (0 = never).

**Do rule-scored emails use my free ones?** Yes, while the AI is unavailable — except the last 5, kept so your first AI-assessed emails are free too (`OUTPUT.chargeSplit.freeKeptForAi`).

**What if nothing matches?** The run succeeds with 0 rows, nothing charged by us; `OUTPUT.rejected` counts the drops per reason.

### 🧩 More Actors from the same developer

- **[Local Business Leads](https://apify.com/rich_minds/local-business-lead)** — find local businesses with their contact data, then bring the emails here to keep only the ones worth sending.
- **[Link Building Prospects & Guest Post Finder (Google, AI)](https://apify.com/rich_minds/link-building-prospects-ai)** — Guest-post and resource-page sites for your keywords, with a checked e-mail and a first line.
- **[Twitter Lead Generation](https://apify.com/rich_minds/twitter-lead-generation-ai)** — People on X asking for a tool like yours — with a reply draft, every week.
- **[LinkedIn Buying Intent Leads](https://apify.com/rich_minds/linkedin-intent-leads)** — People on LinkedIn asking for a product like yours, ready for your outreach.

### 🆘 Support

Something missing? Open an issue on the Actor's page. Developer detail: [API reference](https://api.apify.com/v2/key-value-stores/7ax2l9RfLMNFVU7V2/records/api-reference.md).

### 📝 Changelog

- **0.1.6** (2026-10-16) — One "Send alerts to" field now takes your Slack, Discord, Teams, Google Chat, Telegram or webhook link, and webhook posts can be signed so your tool knows they came from this Actor; An address you already paid for under another list or filter set is delivered again but never charged twice, and AI-scored emails now rank ahead of rule-scored ones in the same label; Your own Gemini key works with the model field left empty, and a refused deep check now ends the same way on every run with a clear "source refused" result, so nothing is charged and no run is counted as a failure.
- **0.1.5** (2026-10-04) — Bring your own data: send rows in `itemsList` or a dataset ID in `datasetId` and the Actor picks the mode itself, starts no source Actor and charges only for the analysis. Fields it cannot read are listed in the run log instead of being dropped silently.
- **0.1.4** (2026-10-04) — The run status counts what was loaded, kept and removed (sendable, risky, bad) and links the dropped addresses with their reason; every run names the date to re-check your list.

<details><summary>Earlier builds</summary>

- **0.2.0** (2026-10-04) — basic price cut to $0.002; verifies inside the Actor on any plan with no second bill (BounceVerify's SMTP check is optional), CSV / Google Sheet link in, `sendable.csv` / `risky.csv` / `bad.csv` out, bill estimate, one verdict per address, `OUTPUT.ok`, five niche presets.
- **0.1.3** (2026-10-04) — New "Deep mailbox check" ready search in the form: one click runs the SMTP mailbox check, and the page now says plainly that the built-in check is domain-level; Shorter, easier-to-scan listing page, with the AI example showing what the AI adds (who they are and what to do) and a minimal five-field input to start from; New recipe for feeding a lead scraper's results straight into this Actor.
- **0.1.2** (2026-10-04) — a built-in e-mail check inside the Actor (no second bill, any plan), CSV / Google Sheet link in, `sendable.csv` / `risky.csv` / `bad.csv` out, `estimateOnly`, `contactRoute` and five niche presets; basic price cut from $0.003 to $0.002.
- **0.1.1** (2026-10-04) — initial release: BounceVerify on your account, free qualification rules, AI ICP fit, cross-run memory, `REJECTED` record.

</details>

# Actor input Schema

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

Paste the addresses to check, one per line (anything that looks like an address is picked up). They are checked inside this Actor on any Apify plan, with no second bill; junk is dropped for free, you pay only for qualified ones. One address = one verdict in OUTPUT.verdict. Empty = free demo on 7 sample rows.

## `csvUrl` (type: `string`):

A link to your list: a CSV file or a Google Sheet shared as 'anyone with the link'. The address column is found for you; other columns stay on the row. Runs on any Apify plan. Affects what you pay: each qualified email of the file is billed here. Cleaned lists come back as sendable.csv, risky.csv and bad.csv.

## `icpDescription` (type: `string`):

Describe the person you want to reach in plain words (role, company type, size). The AI scores every verified address against it (fit 0–100) and explains why from the fields you supplied. Empty = rank by the free rules only.

## `preset` (type: `string`):

Pick a niche and press Start: it fills the ICP and the qualification rules you left empty — then paste your emails. Your own values always win. <b>Custom</b> = fill the fields yourself.

## `minScore` (type: `integer`):

The field that decides what you pay: emails whose fit score (AI on) or rule score (AI off) is below this are dropped and <b>not charged</b>. Raise it to pay only for the strongest matches, lower it to see more.

## `maxQualified` (type: `integer`):

Hard cap on results — and the ceiling of what <b>this Actor</b> charges: at most this many × $0.04 with AI on (× $0.002 with AI off), e.g. 100 × $0.04. Affects what you pay. Best-scoring emails first.

## `maxDiscoveryChargeUsd` (type: `number`):

Only when <b>Built-in verification</b> is off: the hard cap on what BounceVerify may charge your Apify account (≈ $0.00089 per address) — $0.50 in the form and for an API call that leaves it out. Raise it for bigger lists. Ignored while the built-in check runs (no second bill).

## `enableAi` (type: `boolean`):

Score each qualified email against your goal, explain why, summarise it and generate text. <b>On = $0.04 per qualified email + AI tokens; off = $0.002</b> (rule-based flags and scores only). Your first 5 qualified emails get live AI verdicts on any plan; every one with Apify model access or a paid own key.

## `notifyEmail` (type: `string`):

After every run with at least one new qualified email, the digest (how many are new, the top 10, the trend) is e-mailed here through Apify's send-mail Actor. Schedule the run weekly and only new emails reach your inbox. Never sent for the free demo.

## `minVerifierScore` (type: `integer`):

The verifier's own deliverability score. Addresses below it are dropped for free. The built-in check scores a clean domain 80 (a role mailbox 70); BounceVerify's mailbox-confirmed rows score 85–95, so raise it to 85 only with the deep check on. 70 keeps clearly safe addresses.

## `allowRoleEmails` (type: `boolean`):

Off (default): shared mailboxes like info@ or sales@ are dropped for free. On: they are kept and flagged <code>role_based</code>.

## `allowCatchAll` (type: `boolean`):

Off (default): domains that accept every address (deliverability cannot be proven) are dropped for free. On: they are kept, flagged <code>catch_all</code> and never dispositioned as plain <i>send</i>.

## `allowFreeProviders` (type: `boolean`):

On (default): free-provider addresses are kept and flagged <code>free_provider</code>. Off: only company-domain addresses qualify.

## `allowDomains` (type: `array`):

Keep only addresses on these domains (subdomains match too), e.g. <code>acme.com</code>. Everything else is dropped for free. Empty = all domains.

## `denyDomains` (type: `array`):

Drop addresses on these domains (competitors, existing customers) for free — subdomains match too.

## `fixTypos` (type: `boolean`):

On (default): a probable typo (gmial.com, hotmial.com) is corrected and the corrected address verified once more, then kept with <code>correctedFrom</code> set (with the deep check this is a small extra source charge). Off: typos are dropped.

## `targetFlags` (type: `array`):

Only deliver emails that have at least one of these flags, and rank those with more of them higher. Leave empty to keep everything. An API call passes the flag names from the README's flag table, e.g. <code>personal_address</code> or <code>business_domain</code>; an unknown name stops the run at once with the list of valid ones.

## `suppressionList` (type: `array`):

Addresses or domains to never output (e.g. <code>ceo@acme.com</code>, <code>acme.com</code>). Skipped for free.

## `llmProvider` (type: `string`):

<b>Apify (no keys)</b> — the AI runs through Apify's built-in OpenRouter proxy; tokens are billed to your Apify account at OpenRouter's rates. <b>My own key</b> — use your OpenAI / Anthropic / Gemini / Groq key instead; a free-tier key assesses only a few qualified emails per run — expect 1–3 AI checks (2 calls at a time, one after a rate limit, then the 3 borderline qualified emails retried one by one) — and the rest keep their rule score; a paid Groq / Gemini key (billing on) assesses every qualified email.

## `llmModel` (type: `string`):

Leave empty for the default (<code>anthropic/claude-haiku-4.5</code> with Apify, <code>google:gemini-3.5-flash-lite</code> with your own key). Apify mode takes an OpenRouter slug such as <code>openai/gpt-4.1-mini</code>; own-key mode takes <code>provider:model</code>, e.g. <code>anthropic:claude-haiku-4-5-20251001</code>.

## `llmApiKey` (type: `string`):

Required when <b>AI model access</b> is <i>My own API key</i>. Stored encrypted by Apify, never logged.

## `sendAlertsTo` (type: `string`):

Paste one or more URLs, one per line — the service is detected from the address: a Slack or Discord channel webhook, a Teams Workflows or Google Chat space webhook, your Telegram bot's send link (<code>https://api.telegram.org/bot\<token>/sendMessage?chat_id=@yourchannel</code>) or any other https URL (Zapier, Make, n8n, your CRM), which gets the qualified emails as JSON. After every run with at least one new qualified email, the digest (how many are new, the top 10 with links) is posted there. Never sent for the free demo. Headers and signing: <i>Delivery details (advanced)</i>.

## `dedupeAcrossRuns` (type: `boolean`):

Remembers every qualified email you were charged for (in a named key-value store on your account) and skips it in future runs.

## `dedupeTtlDays` (type: `integer`):

How long a delivered qualified email stays remembered. 0 = never deliver or charge it again. A number of days lets a qualified email that comes back (a seasonal peak, a relisted item) reach you again after that many days — charged again as a new qualified email.

## `historyDatasetName` (type: `string`):

Optional. Type a name (e.g. <code>email-list-qualifier-ai-seen-log</code>) and every run appends its new qualified emails with the date to that named dataset on your account — one list across months to connect to Google Sheets or your BI tool once. Empty = off. Costs nothing extra.

## `sourceMode` (type: `string`):

<b>actor</b> — verify the pasted <b>Emails to qualify</b> (or the <b>CSV link</b>) inside this Actor by default, on any plan. <b>dataset</b> — an existing Apify dataset. <b>list</b> — rows you paste in. <b>Bring your own data:</b> rows or a dataset ID that already carry a verifier's result, and you pay only for the analysis. The mode you choose always runs as chosen; only the untouched sample records next to emails you typed switch to the live search. <code>{}</code> over the API runs the free demo — nothing charged.

## `ownSource` (type: `boolean`):

On (default): the addresses are verified inside this Actor — syntax, domain and mail server (MX), disposable domains, role mailboxes, typos — on any Apify plan, with no second bill. It does not probe the mailbox, so catch-all and "does this mailbox exist" stay unknown (<code>smtpValid</code> is null). Off: BounceVerify's deep SMTP check runs on your account (≈ $0.00089 per address on top of ours; needs a plan that runs Store Actors).

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

Only for <b>dataset</b> mode: a dataset of BounceVerify results, or of any scraper with an e-mail column (those addresses are verified first). Pick it so the Actor is granted read access.

## `itemsList` (type: `array`):

Only for <b>list</b> mode. JSON array of objects with an <code>email</code> (or the column named in <b>E-mail column</b>). Rows that carry BounceVerify's fields (<code>status</code>, <code>score</code>, <code>smtp_valid</code>, <code>is_catch_all</code>, <code>is_disposable</code>, <code>is_role_based</code>, <code>is_free_provider</code>) are used as they are — no source run, you pay only for the analysis; rows with just an address are verified first. Extra fields (company, title …) become context for the AI.

## `discoveryInput` (type: `object`):

Only for the deep check (advanced). Raw input for BounceVerify (<code>bounceverify/bounceverify-email-verifier</code>), merged under the Actor's own defaults (<code>skipInvalidSyntax</code>, <code>concurrency</code> 50). Setting it runs the deep check instead of the built-in one. Use <code>inputDatasetId</code> + <code>emailField</code> to verify a dataset in one source run. It runs a Store Actor on your Apify plan — if your plan cannot, use the built-in check, or paste rows BounceVerify already verified in <b>list</b> mode (any plan).

## `estimateOnly` (type: `boolean`):

On: the list is counted and the bill printed (worst case and typical, for AI on and off) in the status and <code>OUTPUT.estimate</code> — nothing is verified, charged or remembered. Use it before a big list. Off (default): the run verifies and delivers.

## `webhookUrl` (type: `string`):

Qualified emails are POSTed here as JSON (Zapier, Make, n8n, your CRM). For Google Sheets / Slack you can also use Apify's built-in Integrations tab. Stored encrypted — Zapier / Make hook URLs carry their secret in the path.

## `webhookHeaders` (type: `object`):

Extra HTTP headers for the webhook, e.g. <code>{"Authorization": "Bearer …"}</code>. Stored encrypted by Apify, never logged.

## `webhookSecret` (type: `string`):

Any random string. Every webhook POST then carries <code>X-Signature: sha256=\<HMAC-SHA256 of the raw body with this secret></code>, so your endpoint can verify it came from this Actor. Stored encrypted by Apify, never logged.

## `dedupeStoreName` (type: `string`):

Key-value store used for cross-run memory. Left at the default, the memory is kept per search (source and search terms): rewording <code>goalDescription</code> keeps it, so a scheduled run never re-delivers what you already have, and another search starts its own list. Running it for several clients on one account, type one name per client.

## `discoveryTimeoutSecs` (type: `integer`):

API only — hidden on the form. How long the deep-check source run (BounceVerify) may run before its partial results are used. Raise it when the status says the source stopped early.

## `slackWebhookUrl` (type: `string`):

API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Slack incoming-webhook URL for the digest.

## `discordWebhookUrl` (type: `string`):

API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Discord channel webhook URL for the digest.

## `teamsWebhookUrl` (type: `string`):

API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Teams Workflows webhook URL for the digest.

## `googleChatWebhookUrl` (type: `string`):

API only — hidden on the form, where <code>sendAlertsTo</code> takes it. Google Chat space webhook URL for the digest.

## `telegramUrl` (type: `string`):

API only — hidden on the form, where <code>sendAlertsTo</code> takes it. The bot's sendMessage link with its chat_id.

## `autoSwitchToLive` (type: `boolean`):

API only — hidden on the form. On (default): search terms typed next to the untouched sample records start the live search (spend-capped; <code>OUTPUT.switchedToLive</code> names them). Off: the mode you send is the mode that runs — the sample stays the free demo and the status names the terms it did not search. API pipelines that must never start a paid read on their own: send <code>false</code>.

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

API only — hidden on the form. Only for <b>dataset</b> / <b>list</b> mode: the field that holds the address in your rows (default <code>email</code>). Rows that already carry BounceVerify's result are used as they are; rows with only an address are verified first.

## `contextFields` (type: `array`):

API only — hidden on the form. Only for <b>dataset</b> / <b>list</b> mode: extra columns of your rows (company, title, website …) passed to the AI as context and returned in <code>contextFields</code>. Empty = name, firstName, lastName, fullName, company, title, website.

## Actor input object example

```json
{
  "icpDescription": "Founders and heads of growth at B2B SaaS companies with 10-200 employees that sell to marketing teams",
  "preset": "custom",
  "minScore": 50,
  "maxQualified": 100,
  "maxDiscoveryChargeUsd": 0.5,
  "enableAi": true,
  "minVerifierScore": 70,
  "allowRoleEmails": false,
  "allowCatchAll": false,
  "allowFreeProviders": true,
  "fixTypos": true,
  "llmProvider": "apify",
  "dedupeAcrossRuns": true,
  "dedupeTtlDays": 0,
  "historyDatasetName": "",
  "sourceMode": "list",
  "ownSource": true,
  "itemsList": [
    {
      "email": "maya.chen@northwind-analytics.io",
      "status": "valid",
      "score": 96,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:41Z",
      "name": "Maya Chen",
      "title": "Head of Growth",
      "company": "Northwind Analytics",
      "website": "https://northwind-analytics.io"
    },
    {
      "email": "d.okafor@brightpath.co",
      "status": "valid",
      "score": 91,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:42Z",
      "title": "Founder",
      "company": "BrightPath Studio"
    },
    {
      "email": "info@lumenlabs.com",
      "status": "valid",
      "score": 78,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": true,
      "is_free_provider": false,
      "risk_type": "role_based",
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Role-based mailbox",
      "verified_at": "2026-10-03T09:12:43Z",
      "company": "Lumen Labs"
    },
    {
      "email": "temp8841@mailinator.com",
      "status": "invalid",
      "score": 5,
      "smtp_valid": false,
      "is_catch_all": false,
      "is_disposable": true,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": "disposable",
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Disposable domain",
      "verified_at": "2026-10-03T09:12:44Z"
    },
    {
      "email": "sam@acme-widgets.com",
      "status": "valid",
      "score": 74,
      "smtp_valid": true,
      "is_catch_all": true,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": "catch_all",
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Domain accepts all addresses",
      "verified_at": "2026-10-03T09:12:45Z",
      "company": "Acme Widgets"
    },
    {
      "email": "jordan.lee@gmail.com",
      "status": "valid",
      "score": 88,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": true,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:46Z",
      "name": "Jordan Lee"
    },
    {
      "email": "priya.nair@tidewater.dev",
      "status": "valid",
      "score": 88,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:47Z",
      "name": "Priya Nair",
      "title": "VP Marketing",
      "company": "Tidewater",
      "website": "https://tidewater.dev"
    }
  ],
  "discoveryInput": {},
  "estimateOnly": false,
  "dedupeStoreName": "email-list-qualifier-ai-seen",
  "discoveryTimeoutSecs": 3600,
  "autoSwitchToLive": true,
  "emailField": "email"
}
```

# Actor output Schema

## `qualified` (type: `string`):

All qualified emails as JSON, best-scoring first.

## `sheet` (type: `string`):

The same qualified emails as a spreadsheet.

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

JSON record with the funnel (loaded -> filtered -> AI-assessed -> qualified), per-reason skip counts, charged events by type, free-tier qualified emails used and remaining, whether a budget limit was reached, webhook delivery counts and the dedupe store size.

# 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 = {
    "icpDescription": "Founders and heads of growth at B2B SaaS companies with 10-200 employees that sell to marketing teams",
    "maxDiscoveryChargeUsd": 0.5,
    "sourceMode": "list",
    "itemsList": [
        {
            "email": "maya.chen@northwind-analytics.io",
            "status": "valid",
            "score": 96,
            "smtp_valid": true,
            "is_catch_all": false,
            "is_disposable": false,
            "is_role_based": false,
            "is_free_provider": false,
            "risk_type": null,
            "syntax_valid": true,
            "domain_exists": true,
            "mx_found": true,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:41Z",
            "name": "Maya Chen",
            "title": "Head of Growth",
            "company": "Northwind Analytics",
            "website": "https://northwind-analytics.io"
        },
        {
            "email": "d.okafor@brightpath.co",
            "status": "valid",
            "score": 91,
            "smtp_valid": true,
            "is_catch_all": false,
            "is_disposable": false,
            "is_role_based": false,
            "is_free_provider": false,
            "risk_type": null,
            "syntax_valid": true,
            "domain_exists": true,
            "mx_found": true,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:42Z",
            "title": "Founder",
            "company": "BrightPath Studio"
        },
        {
            "email": "info@lumenlabs.com",
            "status": "valid",
            "score": 78,
            "smtp_valid": true,
            "is_catch_all": false,
            "is_disposable": false,
            "is_role_based": true,
            "is_free_provider": false,
            "risk_type": "role_based",
            "syntax_valid": true,
            "domain_exists": true,
            "mx_found": true,
            "reason": "Role-based mailbox",
            "verified_at": "2026-10-03T09:12:43Z",
            "company": "Lumen Labs"
        },
        {
            "email": "temp8841@mailinator.com",
            "status": "invalid",
            "score": 5,
            "smtp_valid": false,
            "is_catch_all": false,
            "is_disposable": true,
            "is_role_based": false,
            "is_free_provider": false,
            "risk_type": "disposable",
            "syntax_valid": true,
            "domain_exists": true,
            "mx_found": true,
            "reason": "Disposable domain",
            "verified_at": "2026-10-03T09:12:44Z"
        },
        {
            "email": "sam@acme-widgets.com",
            "status": "valid",
            "score": 74,
            "smtp_valid": true,
            "is_catch_all": true,
            "is_disposable": false,
            "is_role_based": false,
            "is_free_provider": false,
            "risk_type": "catch_all",
            "syntax_valid": true,
            "domain_exists": true,
            "mx_found": true,
            "reason": "Domain accepts all addresses",
            "verified_at": "2026-10-03T09:12:45Z",
            "company": "Acme Widgets"
        },
        {
            "email": "jordan.lee@gmail.com",
            "status": "valid",
            "score": 88,
            "smtp_valid": true,
            "is_catch_all": false,
            "is_disposable": false,
            "is_role_based": false,
            "is_free_provider": true,
            "risk_type": null,
            "syntax_valid": true,
            "domain_exists": true,
            "mx_found": true,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:46Z",
            "name": "Jordan Lee"
        },
        {
            "email": "priya.nair@tidewater.dev",
            "status": "valid",
            "score": 88,
            "smtp_valid": true,
            "is_catch_all": false,
            "is_disposable": false,
            "is_role_based": false,
            "is_free_provider": false,
            "risk_type": null,
            "syntax_valid": true,
            "domain_exists": true,
            "mx_found": true,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:47Z",
            "name": "Priya Nair",
            "title": "VP Marketing",
            "company": "Tidewater",
            "website": "https://tidewater.dev"
        }
    ],
    "discoveryInput": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("rich_minds/email-list-qualifier-ai").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 = {
    "icpDescription": "Founders and heads of growth at B2B SaaS companies with 10-200 employees that sell to marketing teams",
    "maxDiscoveryChargeUsd": 0.5,
    "sourceMode": "list",
    "itemsList": [
        {
            "email": "maya.chen@northwind-analytics.io",
            "status": "valid",
            "score": 96,
            "smtp_valid": True,
            "is_catch_all": False,
            "is_disposable": False,
            "is_role_based": False,
            "is_free_provider": False,
            "risk_type": None,
            "syntax_valid": True,
            "domain_exists": True,
            "mx_found": True,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:41Z",
            "name": "Maya Chen",
            "title": "Head of Growth",
            "company": "Northwind Analytics",
            "website": "https://northwind-analytics.io",
        },
        {
            "email": "d.okafor@brightpath.co",
            "status": "valid",
            "score": 91,
            "smtp_valid": True,
            "is_catch_all": False,
            "is_disposable": False,
            "is_role_based": False,
            "is_free_provider": False,
            "risk_type": None,
            "syntax_valid": True,
            "domain_exists": True,
            "mx_found": True,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:42Z",
            "title": "Founder",
            "company": "BrightPath Studio",
        },
        {
            "email": "info@lumenlabs.com",
            "status": "valid",
            "score": 78,
            "smtp_valid": True,
            "is_catch_all": False,
            "is_disposable": False,
            "is_role_based": True,
            "is_free_provider": False,
            "risk_type": "role_based",
            "syntax_valid": True,
            "domain_exists": True,
            "mx_found": True,
            "reason": "Role-based mailbox",
            "verified_at": "2026-10-03T09:12:43Z",
            "company": "Lumen Labs",
        },
        {
            "email": "temp8841@mailinator.com",
            "status": "invalid",
            "score": 5,
            "smtp_valid": False,
            "is_catch_all": False,
            "is_disposable": True,
            "is_role_based": False,
            "is_free_provider": False,
            "risk_type": "disposable",
            "syntax_valid": True,
            "domain_exists": True,
            "mx_found": True,
            "reason": "Disposable domain",
            "verified_at": "2026-10-03T09:12:44Z",
        },
        {
            "email": "sam@acme-widgets.com",
            "status": "valid",
            "score": 74,
            "smtp_valid": True,
            "is_catch_all": True,
            "is_disposable": False,
            "is_role_based": False,
            "is_free_provider": False,
            "risk_type": "catch_all",
            "syntax_valid": True,
            "domain_exists": True,
            "mx_found": True,
            "reason": "Domain accepts all addresses",
            "verified_at": "2026-10-03T09:12:45Z",
            "company": "Acme Widgets",
        },
        {
            "email": "jordan.lee@gmail.com",
            "status": "valid",
            "score": 88,
            "smtp_valid": True,
            "is_catch_all": False,
            "is_disposable": False,
            "is_role_based": False,
            "is_free_provider": True,
            "risk_type": None,
            "syntax_valid": True,
            "domain_exists": True,
            "mx_found": True,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:46Z",
            "name": "Jordan Lee",
        },
        {
            "email": "priya.nair@tidewater.dev",
            "status": "valid",
            "score": 88,
            "smtp_valid": True,
            "is_catch_all": False,
            "is_disposable": False,
            "is_role_based": False,
            "is_free_provider": False,
            "risk_type": None,
            "syntax_valid": True,
            "domain_exists": True,
            "mx_found": True,
            "reason": "Mailbox accepted",
            "verified_at": "2026-10-03T09:12:47Z",
            "name": "Priya Nair",
            "title": "VP Marketing",
            "company": "Tidewater",
            "website": "https://tidewater.dev",
        },
    ],
    "discoveryInput": {},
}

# Run the Actor and wait for it to finish
run = client.actor("rich_minds/email-list-qualifier-ai").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 '{
  "icpDescription": "Founders and heads of growth at B2B SaaS companies with 10-200 employees that sell to marketing teams",
  "maxDiscoveryChargeUsd": 0.5,
  "sourceMode": "list",
  "itemsList": [
    {
      "email": "maya.chen@northwind-analytics.io",
      "status": "valid",
      "score": 96,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:41Z",
      "name": "Maya Chen",
      "title": "Head of Growth",
      "company": "Northwind Analytics",
      "website": "https://northwind-analytics.io"
    },
    {
      "email": "d.okafor@brightpath.co",
      "status": "valid",
      "score": 91,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:42Z",
      "title": "Founder",
      "company": "BrightPath Studio"
    },
    {
      "email": "info@lumenlabs.com",
      "status": "valid",
      "score": 78,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": true,
      "is_free_provider": false,
      "risk_type": "role_based",
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Role-based mailbox",
      "verified_at": "2026-10-03T09:12:43Z",
      "company": "Lumen Labs"
    },
    {
      "email": "temp8841@mailinator.com",
      "status": "invalid",
      "score": 5,
      "smtp_valid": false,
      "is_catch_all": false,
      "is_disposable": true,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": "disposable",
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Disposable domain",
      "verified_at": "2026-10-03T09:12:44Z"
    },
    {
      "email": "sam@acme-widgets.com",
      "status": "valid",
      "score": 74,
      "smtp_valid": true,
      "is_catch_all": true,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": "catch_all",
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Domain accepts all addresses",
      "verified_at": "2026-10-03T09:12:45Z",
      "company": "Acme Widgets"
    },
    {
      "email": "jordan.lee@gmail.com",
      "status": "valid",
      "score": 88,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": true,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:46Z",
      "name": "Jordan Lee"
    },
    {
      "email": "priya.nair@tidewater.dev",
      "status": "valid",
      "score": 88,
      "smtp_valid": true,
      "is_catch_all": false,
      "is_disposable": false,
      "is_role_based": false,
      "is_free_provider": false,
      "risk_type": null,
      "syntax_valid": true,
      "domain_exists": true,
      "mx_found": true,
      "reason": "Mailbox accepted",
      "verified_at": "2026-10-03T09:12:47Z",
      "name": "Priya Nair",
      "title": "VP Marketing",
      "company": "Tidewater",
      "website": "https://tidewater.dev"
    }
  ],
  "discoveryInput": {}
}' |
apify call rich_minds/email-list-qualifier-ai --silent --output-dataset

```

## MCP server setup

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

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/VGHpGXOjS6BRAYySF/builds/VwgcffFHZennTxUvV/openapi.json
