# Email Verification API - Deliverability and Spam Traps (`nabeelbaghoor/email-verification-deliverability-api`) Actor

Verify email addresses at the mailbox rather than by syntax: does it exist, is it a role or catch-all address, is it disposable, is it a spam trap, is it on a block list, has it appeared in a breach. Returns a trust score and a send recommendation. Pay per result.

- **URL**: https://apify.com/nabeelbaghoor/email-verification-deliverability-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% 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

## Email Verification API - Deliverability and Spam Traps

Does this mailbox actually exist, and should you send to it. Verified at the receiving server rather than guessed from the syntax, with the risk picture attached. One flat row per address.

### What this actor does

- **Verifies at the mailbox, not at the regex.** The check connects to the receiving mail server and asks whether the address exists. That is a different question from whether it is well formed and whether the domain has an MX record, and it is the only one that predicts a bounce.
- **Tells you why, not just what.** A failure comes back with a specific reason: the mailbox does not exist, the domain is inexistent, the mailbox is full, the server is catch-all, the server grey-listed the check, the address is a known disposable domain, or a possible spam trap was detected. Sixteen distinct reasons, each its own value.
- **Distinguishes a bad address from an uncooperative server.** Grey-listing is a spam defence that refuses a first contact from an unfamiliar sender and accepts the retry. Treating that as a bad address throws away good subscribers, so it comes back as retry later rather than bad, and the run tells you how many to check again in an hour.
- **Flags spam traps.** A spam trap is an address that exists only to catch senders who do not verify, and hitting one damages your sending reputation far out of proportion to the single message. It gets its own column and its own filter.
- **Separates role, free mail and catch-all.** These are three different reasons to be careful and they need three different decisions, so they are three columns rather than one lumped quality flag.
- **Gives you a verdict as well as the evidence.** A trust score, an inbox quality score, an overall risk score with the domain, format and profanity components broken out, and a plain send recommendation of safe, risky or do not send. Route on the recommendation, or build your own rule from the parts.
- **Checks block lists by name.** Which lists the address or its domain appears on, and the reason each one gives, rather than a single yes or no.
- **Looks up the domain behind the address.** A separate WHOIS mode returns registration date, domain age, time to expiry, registrar, name servers and DNSSEC status. Domain age is one of the strongest single signals that a sending domain was created last week for one campaign.
- **Normalises the provider's enumerations.** These fields arrive sometimes as a name and sometimes as a number, and a column that is sometimes `1` and sometimes `"Ok"` cannot be filtered. Every one is resolved to its name here, with the numeric code kept alongside for sorting.
- **Uses only your own key.** This provider also serves a keyless endpoint on its free tier. It is deliberately not used here, because reselling a free tier through a paid actor is exactly what its terms forbid. Every mode uses the credential you supply.
- **Never puts your key in a log.** The credential travels in the URL path with this provider, which makes every request URL a secret. Nothing here logs one.
- **Never charges for a miss.** Only rows carrying a resolved result are billed.

### Input

| Field | What it does |
| --- | --- |
| `mode` | Verify, assess, block lists, or WHOIS. |
| `queries` | Addresses to check, one per line. Domains in WHOIS mode. |
| `assessIpAddress` | The IP the signup came from, in assess mode. |
| `assessFirstName` | A first name to check the address against. |
| `assessLastName` | A last name to check the address against. |
| `results` | Keep only Ok, Bad, RetryLater or Unverifiable rows. |
| `deliverableOnly` | Keep only addresses confirmed to exist. |
| `sendRecommendations` | Keep only these send recommendations. |
| `minTrustScore` | Keep only rows at or above this trust score. |
| `maxRiskScore` | Keep only rows at or below this risk score. |
| `excludeRole` | Drop shared mailboxes such as info and sales. |
| `excludeFreeMail` | Drop free consumer mail providers. |
| `excludeDisposable` | Drop throwaway domains. |
| `excludeCatchAll` | Drop domains that accept everything. |
| `excludeSpamTraps` | Drop identified spam traps. |
| `excludeDarkWeb` | Drop addresses seen in breached data. |
| `excludeGibberish` | Drop machine-looking addresses. |
| `excludeBlockListed` | Drop addresses on a block list. |
| `reportQuota` | Log your remaining provider allowance first. |
| `requestsPerMinute` | Pace the run under your plan's rate limit. |
| `skipNotFound` | Leave misses and refusals out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `apiKey` | Your own license key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "mode": "verify",
  "query": "user@example.com",
  "email": "user@example.com",
  "result": "Ok",
  "resultCode": 1,
  "reason": "Success",
  "reasonCode": 11,
  "isDeliverable": true,
  "trustScore": 8.4,
  "trustLevel": "High",
  "sendRecommendation": "SafeToSend",
  "inboxQualityScore": 9.1,
  "isSyntaxValid": true,
  "syntaxReason": "Success",
  "hasDnsRecord": true,
  "hasMxRecords": true,
  "mxRecords": ["mx1.example.com", "mx2.example.com"],
  "isRole": false,
  "isFreeMail": false,
  "isCatchAll": false,
  "isDisposable": false,
  "isDarkWebListed": false,
  "isGibberishUser": false,
  "isGibberishDomain": false,
  "spamRecommendation": "Allow",
  "overallRiskScore": 0.8,
  "domainRiskScore": 0.4,
  "formatRiskScore": 0.2,
  "profanityRiskScore": 0,
  "blockLists": [],
  "isSpamTrap": false,
  "spamTrapDescriptor": null,
  "mailServerLocation": "US",
  "smtpBanner": "220 mx1.example.com ESMTP",
  "domain": "example.com",
  "tld": "com",
  "user": "user",
  "emailHashMd5": "b58996c504c5638798eb6b511e6f49af",
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### How is this different from a syntax and MX check?

A syntax check tells you the address is well formed. An MX check tells you the domain can receive mail at all. Neither tells you whether that particular mailbox exists, which is the thing that decides whether your message bounces. This verification connects to the receiving server and asks about the specific address, which is why it is slower than a syntax check and why it is the only one worth running before a send.

#### What does each verification result mean?

`Ok` means the mailbox exists. `Bad` means it does not, and the reason column says why: no such mailbox, the domain does not exist, no MX servers, a mailbox that is full. `RetryLater` means the receiving server declined to answer right now, almost always grey-listing, and those are usually good addresses. `Unverifiable` means the answer cannot be obtained at all, most often because the server is catch-all and will accept anything. Four results, and a separate reason column carrying sixteen possible explanations.

#### Why do I get RetryLater and what should I do about it?

Grey-listing is a spam defence: the receiving server refuses the first contact from any sender it does not recognise and accepts the same contact on a retry a few minutes later. It is a statement about your verification attempt, not about the address. Treating those as bad addresses is one of the most common ways to throw away good subscribers. Run them again an hour or two later and most resolve. The run log tells you how many came back that way.

#### What is a spam trap and why does it matter so much?

A spam trap is an address that exists for the sole purpose of catching senders who mail without permission or without verifying. Some are addresses that were abandoned and later recycled as traps; others were never real. Sending to one can affect your domain's sending reputation with the major mailbox providers out of all proportion to the single message, which is why it gets its own column here rather than being folded into a general risk score.

#### Should I drop catch-all domains?

It depends on how much bounce risk you can carry. A catch-all domain accepts mail addressed to every possible mailbox, so the server cannot tell you whether a particular address is real. That makes the result neither a pass nor a fail. Many business domains are configured this way, so dropping them all removes real people; keeping them means accepting that some will bounce. The flag is there so you can decide rather than have the decision made for you.

#### What is the difference between a role address and a free mail address?

A role address belongs to a function rather than a person: info, sales, support, postmaster. It is real and deliverable, but marketing sent to it tends to be seen by several people and is a common source of complaints. A free mail address is on a consumer provider such as a webmail service. It is a person, just not one at a company domain. They need different decisions, so they are two columns.

#### What does the assess mode add?

A trust score for the address, and the ability to check it against context you already hold: the IP address the signup came from, checked for data centre and Tor exit node origin, and a first and last name to test against the address. Be aware that those three are positional path segments with this provider, so leaving the IP empty means the names are ignored too. The input descriptions say so at each field.

#### Why would I run a WHOIS lookup on a domain?

Domain age is one of the strongest single signals in email risk. A domain registered eleven days ago that is sending marketing is a very different proposition from one registered in 2003, and no amount of mailbox verification will tell you that. The WHOIS mode returns registration date, age, time to expiry, registrar, name servers and DNSSEC status for a domain, and it accepts whole email addresses and reduces them to the domain, so you can run it over the same list.

#### Does this actor use the provider's free endpoint?

No, deliberately. This provider does serve a keyless endpoint as its free tier, and it would be technically easy to call it. Reselling a free tier through a paid actor is exactly what those terms forbid, so every mode here uses the licence key you supply and nothing else.

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

Yes. This actor does not include data access. You use your own license key from Email Hippo, which is the provider whose email verification API this actor calls, taken from your provider account and pasted into the `apiKey` field, where it is stored as an Apify secret. Your own plan, allowance and terms apply. Turn on the balance report if you want the run to log how much of your allowance is left before it starts.

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

A negative verdict is a result, not a miss: the provider telling you a mailbox does not exist is exactly the question you asked, so that row is written and billed like any other. A miss is a value the provider would not answer for at all, and a refusal is an input that never left this actor. Both are written with `found: false` and never billed. Set `skipNotFound` to true to leave them out entirely.

#### How much does a run cost?

Pricing is pay per result: you are charged for each value resolved into the dataset, and never for misses, for locally refused inputs, for rows dropped by the filters, or for duplicates. Apify platform usage is included in the per-result price. Your own provider allowance is separate and billed by them.

### Keyword map

email verification API, email validation API, bulk email verifier, email list cleaning, mailbox verification, SMTP verification, deliverability check, bounce rate reduction, spam trap detection, disposable email detection, throwaway email check, catch all domain detection, role account detection, free mail detection, gibberish email detection, dark web email check, email block list check, DNSBL check, email risk score, email trust score, send recommendation, domain WHOIS API, domain age lookup, domain expiry check, signup fraud prevention, email hygiene API

# Actor input Schema

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

Which product to call. These live on three separate services behind one licence, so the mode selects the service as well as the check. Verification is the one most runs want.

## `queries` (type: `array`):

Email addresses to check, one per line. In WHOIS mode give domain names instead; a whole email address is accepted there too and reduced to its domain, since a list of addresses is usually what you have. Duplicates are checked once.

## `assessIpAddress` (type: `string`):

In assess mode, the IP address the signup came from, checked for data centre and Tor exit node origin. Note that this and the two name fields are positional: leaving this empty means the names below are ignored as well, because the provider reads them by position.

## `assessFirstName` (type: `string`):

In assess mode, a first name to check the address against. Only used when an IP address is also given, because the provider reads these by position.

## `assessLastName` (type: `string`):

In assess mode, a last name to check the address against. Only used when an IP address and a first name are also given, because the provider reads these by position.

## `results` (type: `array`):

Only keep rows whose verification result is one of these. Ok means the mailbox exists, Bad means it does not, RetryLater means the receiving server would not answer right now, and Unverifiable means it cannot be determined at all. Leave empty to keep every result.

## `deliverableOnly` (type: `boolean`):

Drop everything except addresses confirmed to exist. The one-tick way to turn a raw list into a sending list. Be aware this also drops the retry-later rows, which are usually good addresses behind a server that was being cautious.

## `sendRecommendations` (type: `array`):

Only keep rows the provider recommended this way. This is the provider's own verdict combining deliverability and risk, and it is what to route on if you do not want to build your own rule from the individual flags.

## `minTrustScore` (type: `number`):

Only keep rows scoring at or above this on the provider's trust scale, where higher is better. Leave at zero to keep every score.

## `maxRiskScore` (type: `number`):

Only keep rows whose overall risk score is at or below this, where higher is worse. The mirror of the trust score and a finer instrument than the send recommendation. Leave at zero to keep every score.

## `excludeRole` (type: `boolean`):

Leave out shared mailboxes such as info, sales, support and postmaster. These are real and deliverable but they belong to a function rather than a person, and sending marketing to them is a common source of complaints.

## `excludeFreeMail` (type: `boolean`):

Leave out addresses on free consumer mail providers, keeping only company domains. Useful when the list is meant to be a business one.

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

Leave out throwaway domains, which are the usual signature of a signup that was never meant to be reachable.

## `excludeCatchAll` (type: `boolean`):

Leave out addresses on domains that accept mail for every possible mailbox. A catch-all cannot be confirmed either way, so it is neither a pass nor a fail, and whether you keep them depends on how much bounce risk you can carry.

## `excludeSpamTraps` (type: `boolean`):

Leave out addresses the provider identified as spam traps. Worth thinking about rather than skipping: a spam trap is an address that exists purely to catch senders who do not verify, and hitting one can affect your sending reputation far out of proportion to the single message.

## `excludeDarkWeb` (type: `boolean`):

Leave out addresses the provider has seen in breached data circulating online.

## `excludeGibberish` (type: `boolean`):

Leave out addresses whose local part or domain looks machine-generated rather than chosen by a person.

## `excludeBlockListed` (type: `boolean`):

In block list mode, leave out addresses that appear on at least one list.

## `reportQuota` (type: `boolean`):

Log how much of your provider allowance is left before the run starts. Informational only: this check answers for any key at all, including a wrong one, so it can report a balance but it can never confirm that your credential works.

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

Pace the run so it stays under whatever rate your plan allows. Mailbox verification is slower than most lookups because it actually connects to the receiving server, so a low rate here costs less time than it looks.

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

Leave values the provider returned nothing for, and inputs refused before they were sent, out of the dataset. Off by default, because knowing which of your addresses were unusable is usually the point. 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 license key for the email verification API, taken from your provider account. Note that this provider carries the key in the request URL rather than in a header, so nothing in this actor ever logs a request URL. Stored as a secret.

## Actor input object example

```json
{
  "mode": "verify",
  "queries": [
    "user@example.com"
  ],
  "results": [],
  "deliverableOnly": false,
  "sendRecommendations": [],
  "minTrustScore": 0,
  "maxRiskScore": 0,
  "excludeRole": false,
  "excludeFreeMail": false,
  "excludeDisposable": false,
  "excludeCatchAll": false,
  "excludeSpamTraps": false,
  "excludeDarkWeb": false,
  "excludeGibberish": false,
  "excludeBlockListed": false,
  "reportQuota": false,
  "requestsPerMinute": 120,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

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

One row per address, carrying the mailbox verification result and its reason, the trust score and send recommendation, the role, free mail, catch-all, disposable, spam trap and block list flags, and the mail infrastructure behind the domain.

# 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 = {
    "queries": [
        "user@example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/email-verification-deliverability-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 = { "queries": ["user@example.com"] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/email-verification-deliverability-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/Su6Rl92UjwGZHm08z/builds/dcra0IfI7bC1ZaoxI/openapi.json
