# Bulk Email Verifier API - Deliverability and Toxicity (`nabeelbaghoor/bulk-email-verifier-api`) Actor

Verify up to ten thousand email addresses in one request with syntax, DNS, MX and SMTP checks, and get the verdict, the reason code, a 0-100 deliverability score, a 0-5 toxicity score plus catch-all, disposable, free, role and full mailbox flags. Domain checks cost one credit. Pay per result.

- **URL**: https://apify.com/nabeelbaghoor/bulk-email-verifier-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 71.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$8.00 / 1,000 results

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

## Bulk Email Verifier API - Deliverability and Toxicity

Clean a mailing list before you send to it. Verify up to ten thousand addresses in a single request, get the verdict and the reason behind it, and score the whole list for the addresses that quietly damage a sender reputation. One flat row per address or domain.

### What this actor does

- **Verifies in real bulk, not a loop dressed up as one.** Addresses go to a synchronous bulk endpoint that verifies the entire batch before it answers, up to ten thousand in one request. A hundred thousand address list is a hundred requests rather than a hundred thousand. Results are matched back to your input by address, not by position, so an address the provider drops never shifts the rest of the list onto the wrong rows.
- **Returns the reason, not just the verdict.** Every row carries one of four verdicts and the reason code underneath it, across eleven documented codes. That is what separates a receiving server that timed out and will verify on a retry from a mailbox that genuinely does not exist. Both are offered as filters.
- **Scores deliverability from zero to one hundred.** The verdict alone is blunt. The score lets you keep the stronger risky addresses and drop the weak ones instead of throwing away a whole verdict class.
- **Scores toxicity from zero to five.** Toxicity is the reputation question rather than the delivery question: spam traps, known complainers and addresses that have burned senders before. An address can be perfectly deliverable and still be the one that gets your domain blocklisted.
- **Flags catch-all, disposable, free, role, disabled and full mailboxes.** These arrive nested under domain and account objects and are lifted onto the row, because a spreadsheet filter cannot reach into a nested object and every one of these is something people filter on.
- **Checks a domain without touching a mailbox.** The domain route answers whether a domain is catch-all, disposable or free webmail, which provider runs it, its mail exchanger record and whether it is toxic, at one credit per domain rather than per address. Checking one domain is far cheaper than checking the two hundred addresses on it.
- **Waits out an asynchronous job properly.** The toxicity list is created, polled and downloaded. The download answers HTTP 400 while the job is still running, which reads like an error and is not one, so the actor treats it as a wait and keeps polling, with the whole wait bounded so a stuck job ends the run rather than hanging it.
- **Never charges for a miss.** Only rows carrying a resolved result are billed.

### Input

| Field | What it does |
| --- | --- |
| `mode` | Verify, domain check or toxicity. |
| `emails` | Addresses to verify or score. |
| `domains` | Domains to check. |
| `statuses` | Keep only these verdicts. |
| `reasons` | Keep only these reason codes. |
| `excludeFreeEmail` | Drop free consumer mailboxes. |
| `excludeRoleAccounts` | Drop shared inboxes such as info and support. |
| `excludeDisposable` | Drop throwaway addresses. |
| `excludeAcceptAll` | Drop addresses on catch-all domains. |
| `minScore` | Keep only addresses at or above this deliverability score. |
| `maxToxicity` | Keep only addresses at or below this toxicity score. |
| `batchSize` | Addresses per bulk request, up to ten thousand. |
| `timeout` | Seconds allowed per address on the single address route. |
| `requestsPerMinute` | Pace the run under your plan's rate limit. |
| `skipNotFound` | Leave unresolved inputs out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `apiKey` | Your own API key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "mode": "verify",
  "query": "jane.doe@example.com",
  "email": "jane.doe@example.com",
  "status": "deliverable",
  "reason": "accepted_email",
  "domain": "example.com",
  "acceptAll": "no",
  "disposable": "no",
  "free": "no",
  "role": "no",
  "disabled": "no",
  "fullMailbox": "no",
  "provider": "google.com",
  "dnsType": "MX",
  "dnsRecord": "aspmx.l.google.com",
  "score": 100,
  "toxic": "unknown",
  "toxicity": 0,
  "retryAfter": null,
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### How does bulk email verification actually work?

It runs in layers. The address is checked for syntax, the domain is looked up for mail exchanger records, and then the receiving mail server is contacted over SMTP and asked whether that specific mailbox exists. On top of that the provider applies its own intelligence about disposable providers, role addresses, catch-all configurations and mailboxes that are disabled or full. Each row returns the verdict of that whole process, the reason code that decided it, a zero to one hundred score and the individual flags.

#### What is the difference between the verdict and the reason code?

The verdict is one of deliverable, risky, undeliverable or unknown. The reason code is the detail underneath it, across eleven documented values, and it is where the useful decisions live. Two addresses can both come back as unknown when one hit a receiving server that timed out and will verify perfectly on a retry, while the other sits behind a DNS error that will never resolve. Filtering on the reason code lets you retry the first group and drop the second.

#### What does the toxicity score mean, and how is it different from the verdict?

The verdict answers whether mail will arrive. Toxicity answers whether sending it is a good idea. The scale runs zero to five and covers spam traps, known complainers and addresses with a history of damaging senders. A spam trap is a live, perfectly deliverable mailbox whose only purpose is to catch people mailing lists they should not have; it will verify as deliverable and it will still get your sending domain blocklisted. Score a list for toxicity before a first send to a list you did not build yourself.

#### Should I use the domain check or verify every address?

Use the domain check when the question is about the domain rather than the person. It tells you whether a domain is catch-all, disposable or free webmail, which mailbox provider runs it and whether it is toxic, at one credit per domain instead of one per address. If you hold two hundred addresses at one company, checking that domain once is two hundred times cheaper than verifying them all, and it will often tell you immediately that verifying them individually is pointless because the domain accepts everything.

#### What is a catch-all domain and why does it matter?

A catch-all domain is configured to accept mail for every possible mailbox, whether or not it exists. That means an SMTP check on any address there comes back positive and tells you nothing about the individual mailbox. The provider marks these, and the verdict comes back as risky rather than deliverable. Decide per domain rather than per address: some catch-all domains belong to well run companies and mail fine, while others are parked and will silently discard everything.

#### How large can one request be?

Ten thousand addresses, which is the provider's own ceiling on the synchronous bulk route. The actor defaults to a thousand per request, which keeps each response arriving reasonably quickly while still cutting a large list down to a manageable number of requests. Raise it toward ten thousand for very large lists, and remember that each request then takes correspondingly longer to answer because the whole batch is verified before it returns.

#### Do I need my own API key?

Yes. This actor does not include data access. You use your own API key from Bouncer, which is the provider whose email verification API this actor calls, created on their dashboard. Your own plan, credit balance and terms apply. Paste the key into the `apiKey` field, where it is stored as an Apify secret. The key travels in a request header rather than on the URL.

#### How does this handle rate limits and spent credits?

Requests are spaced evenly across the minute so a long list does not bunch up against the limiter, and the pace is yours to set. A rate limit or server error is retried with backoff, because it clears on its own. An empty credit balance is treated differently: the provider reports it with its own status code, the actor recognises it and stops with what it has rather than burning retries on a balance that waiting will not refill.

#### What happens when an address returns nothing?

The row is written with `found: false` and an `error` explaining that no verdict came back, and the run continues to the next input. Misses are not charged for. Set `skipNotFound` to true to leave them out of the dataset entirely. Note that a greylisting response is different: the provider returns a retry time on the row rather than a miss, so you can re-run just those addresses later.

#### How much does a run cost?

Pricing is pay per result: you are charged for each address or domain resolved into the dataset, and never for misses, for rows dropped by the filters, or for duplicates. Apify platform usage is included in the per-result price. Your own API provider's credit balance is separate and billed by them.

### Keyword map

bulk email verifier API, email verification API, email validation API, email list cleaning, SMTP verification, MX record check, DNS check, catch-all detection, disposable email detection, role account detection, spam trap detection, email toxicity score, deliverability score, bounce rate reduction, sender reputation, list hygiene, domain verification API, mailbox provider lookup, full mailbox detection, greylisting retry, B2B email cleaning, cold outreach list cleaning

# Actor input Schema

## `mode` (type: `string`):

Verify runs the full check on a list of addresses: syntax, domain, mail exchanger records and an SMTP conversation with the receiving server. Domain check answers what can be known about a domain without touching any mailbox, and costs one credit per domain rather than per address. Toxicity scores a list for spam traps, complainers and other addresses that damage a sender reputation.

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

Email addresses, one per line. Used by verification and by the toxicity list. An address pasted inside a longer string, such as a mail header or a name and address pair, is extracted before the request.

## `domains` (type: `array`):

Domains to check, one per line. A pasted URL is reduced to the bare hostname and an address is reduced to the part after the at sign, so a messy list still works. Domain check only.

## `statuses` (type: `array`):

Only keep addresses with these verdicts. Restricting to deliverable is how you turn a raw list into one that is safe to send to. Leave empty to keep every verdict. Verification only, and applied after the provider answers, so it reduces dataset noise and Apify spend rather than provider credits.

## `reasons` (type: `array`):

Only keep addresses whose reason code is one of these. The reason code is the detail under the verdict, and it is what separates a server that timed out and will verify on a retry from a mailbox that genuinely does not exist. Leave empty to keep every reason. Verification only.

## `excludeFreeEmail` (type: `boolean`):

Leave out addresses on free consumer providers such as the large webmail services, keeping only company addresses. Useful when the list is meant for B2B outreach. Verification only.

## `excludeRoleAccounts` (type: `boolean`):

Leave out shared inboxes such as info, sales and support, keeping only addresses that belong to an individual. Verification only.

## `excludeDisposable` (type: `boolean`):

Leave out addresses on throwaway providers, which are created to expire and are worth nothing in a list. Verification only.

## `excludeAcceptAll` (type: `boolean`):

Leave out addresses on domains that accept everything sent to them. A catch-all domain answers yes to every mailbox, so a positive result there says nothing about the individual address. Verification only.

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

Only keep addresses scoring at least this on the provider's zero to one hundred deliverability scale. A useful middle ground when the verdict alone is too blunt: it keeps the stronger risky addresses and drops the weak ones. Leave at zero to keep every score. Verification only.

## `maxToxicity` (type: `integer`):

Only keep addresses at or below this on the provider's zero to five toxicity scale, where zero is clean and five should never be mailed. Applies to both verification and the toxicity list. Leave at five to keep every address.

## `batchSize` (type: `integer`):

How many addresses to verify in one request against the provider's synchronous bulk endpoint, up to its own ceiling of ten thousand. Larger batches mean far fewer requests for a big list, at the cost of waiting longer for each response. Verification only.

## `timeout` (type: `integer`):

How long the provider may spend on one address before answering unknown. This is only honoured on the single address route, which the actor uses when exactly one address is given, because the bulk route sets its own pace.

## `requestsPerMinute` (type: `integer`):

Pace the run so it stays under whatever rate your plan allows. The provider's defaults are one hundred requests per minute on verification and one thousand on domain checks. Lower this if it starts returning rate limit responses.

## `skipNotFound` (type: `boolean`):

Leave inputs the provider had nothing for out of the dataset. Off by default, because keeping the misses is what lets you see which inputs resolved and which did not. Misses are never charged for either way.

## `maxResults` (type: `integer`):

Stop after this many rows. This is the cap on both spend and run time.

## `apiKey` (type: `string`):

Your own API key for the email verification API. Sent as a request header, never on the URL. Required for every lookup. Stored as a secret.

## Actor input object example

```json
{
  "mode": "verify",
  "emails": [
    "jane.doe@example.com"
  ],
  "statuses": [],
  "reasons": [],
  "excludeFreeEmail": false,
  "excludeRoleAccounts": false,
  "excludeDisposable": false,
  "excludeAcceptAll": false,
  "minScore": 0,
  "maxToxicity": 5,
  "batchSize": 1000,
  "timeout": 10,
  "requestsPerMinute": 100,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

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

One row per address or domain checked, with the verdict, the reason code behind it, the deliverability and toxicity scores and the domain and account flags.

# 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": [
        "jane.doe@example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/bulk-email-verifier-api").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": ["jane.doe@example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/bulk-email-verifier-api").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": [
    "jane.doe@example.com"
  ]
}' |
apify call nabeelbaghoor/bulk-email-verifier-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/bulk-email-verifier-api"
        }
    }
}

```

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/sLHNWcW9l57bPzION/builds/IdAg9MeHpy97QZL34/openapi.json
