How to check if an email address exists without sending an email

Get a free API key on RapidAPI →

The problem: syntax checks don't catch dead mailboxes

Most signup forms validate email addresses with a regex, and most of them stop there. But a syntactically perfect address like ceo@totally-real-company.com can still point at a domain with no mail server, a deleted employee account, or a disposable inbox that expires in ten minutes. You discover this only when the message hard-bounces — and mailbox providers track your bounce rate. Sustain a few percent of hard bounces and Gmail and Outlook quietly start routing everything you send, including mail to real customers, to spam.

The obvious fix — send a confirmation email and see what happens — is itself a send, so it still generates the bounce you were trying to avoid. What you actually want is to ask the receiving mail server whether the mailbox exists without delivering anything.

How SMTP mailbox verification works

Mail servers already have a mechanism for this. When one server delivers mail to another, it looks up the recipient domain's MX records in DNS, connects to the listed host, and issues a sequence of SMTP commands: HELO, MAIL FROM, then RCPT TO:<jane.doe@gmail.com>. The receiving server answers the RCPT TO with an accept or reject before any message content is transmitted. A verifier simply performs this handshake and disconnects before sending data — the mailbox owner never sees anything, and no bounce is generated.

Doing this yourself is harder than it sounds. Nearly every cloud provider blocks outbound port 25, and even if yours doesn't, major providers reject or greylist SMTP connections from datacenter IP ranges on sight — which is why DIY verifiers and many hosted ones return a wall of unknowns for Gmail and Outlook addresses. This API routes its handshakes through residential IPs, so the receiving server treats the connection like ordinary mail traffic and gives a real answer.

Step 1: verify a single address

One call does the whole pipeline — syntax parse, MX resolution, and the live handshake:

GET /verify?email=jane.doe@gmail.com

The response gives you a deliverability statusdeliverable, undeliverable, risky, unknown, or invalid — alongside a 0–100 risk score, a catch-all flag, the parsed local part and domain, the MX hosts found, and risk flags for disposable domains, role accounts (info@, sales@), missing MX, and free webmail. If you only need a fast pre-check without the network round trip, /syntax validates the address format alone and /mx returns just the domain's mail hosts.

Step 2: act on the status, not just the score

Edge cases to watch for

Catch-all domains are the big one. Some domains accept RCPT TO for every address, so "accepted" proves nothing. The response flags these explicitly — treat a catch-all accept as unverified rather than deliverable, and lean on the risk score. Role accounts like support@ usually exist but rarely belong to the person you think you're emailing, which matters for cold outreach. And for list cleaning, don't loop over /verify one address at a time — POST /verify-batch handles up to 100 addresses per call.

FAQ

Will the person know their address was verified? No. The handshake ends before any message data is sent; nothing arrives in the inbox and no bounce is created.

Why do other checkers return "unknown" for addresses this API resolves? Their handshakes originate from datacenter IPs that Gmail, Outlook, and Yahoo block or greylist. Residential routing gets a genuine SMTP response.

Should I block signups on "risky"? Usually not outright — route them to email confirmation instead. Reserve hard rejection for undeliverable, invalid, and disposable-domain flags.

Copy-paste code

Calling email-mailbox-verification-api-smtp-handshake.p.rapidapi.com — replace YOUR_RAPIDAPI_KEY with your key.

cURL

curl --request GET \
  --url 'https://email-mailbox-verification-api-smtp-handshake.p.rapidapi.com/verify?email=jane.doe%40gmail.com' \
  --header 'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY' \
  --header 'X-RapidAPI-Host: email-mailbox-verification-api-smtp-handshake.p.rapidapi.com'

Python

import requests

url = "https://email-mailbox-verification-api-smtp-handshake.p.rapidapi.com/verify"
querystring = {"email": "jane.doe@gmail.com"}
headers = {
    "X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
    "X-RapidAPI-Host": "email-mailbox-verification-api-smtp-handshake.p.rapidapi.com",
}

response = requests.get(url, headers=headers, params=querystring)
print(response.json())

JavaScript (fetch)

const url = 'https://email-mailbox-verification-api-smtp-handshake.p.rapidapi.com/verify?email=jane.doe%40gmail.com';
const options = {
  method: 'GET',
  headers: {
    'X-RapidAPI-Key': 'YOUR_RAPIDAPI_KEY',
    'X-RapidAPI-Host': 'email-mailbox-verification-api-smtp-handshake.p.rapidapi.com',
  },
};

const response = await fetch(url, options);
console.log(await response.json());

Node.js (axios)

import axios from 'axios';

const options = {
  method: 'GET',
  url: 'https://email-mailbox-verification-api-smtp-handshake.p.rapidapi.com/verify',
  params: {email: 'jane.doe@gmail.com'},
  headers: {
    'X-RapidAPI-Key': 'YOUR_RAPIDAPI_KEY',
    'X-RapidAPI-Host': 'email-mailbox-verification-api-smtp-handshake.p.rapidapi.com',
  },
};

const { data } = await axios.request(options);
console.log(data);

Powered by the Email Mailbox Verification API (SMTP Handshake) API on RapidAPI.