Private betaMailbeam is in private beta — the public API isn't live yet.Join the waitlist

Mailbeam
GuideBy The Mailbeam Team7 min read1 June 2025

What Is Email Verification? A Developer's Guide

When a user types an email address into your signup form, what do you actually know about it? If you're only checking syntax — does it look like an email? — you know almost nothing. Email verification is the process of determining whether an address is real, reachable, and worth contacting.

This guide explains what verification actually checks, how each layer works, and where it belongs in your stack.

What email verification is not

Syntax validation is not email verification. Checking that an address matches /^[^\s@]+@[^\s@]+\.[^\s@]+$/ tells you only that the string has an @ sign and a dot. It won't catch:

  • test@mailinator.comdisposable address, deleted in 10 minutes
  • noreply@example.com — role address that nobody reads
  • user@nonexistent-domain.com — domain with no mail server
  • ghost@real-company.com — address where the mailbox was deleted

A full verification pipeline has five distinct layers. Each one eliminates a different class of bad addresses.

Layer 1: Syntax validation

The first check normalises and validates the address format per RFC 5321 and RFC 5322. Beyond the basic regex, this catches edge cases like:

  • Double dots (user..name@domain.com)
  • Leading or trailing dots in the local part
  • Invalid characters in the local part
  • Overly long addresses (RFC limit: 254 characters total, 64 in the local part)

Most addresses fail here only due to user typos, and this step completes in microseconds.

Layer 2: Domain and MX record lookup

A valid syntax doesn't mean the domain exists or accepts mail. The second layer performs DNS lookups:

  1. MX record lookup — does the domain have mail exchange records pointing to an SMTP server?
  2. A record fallback — if no MX record exists, does the domain resolve at all?
  3. Domain existence — is the domain registered and not expired?

A domain like emai.com (common typo for email.com) passes syntax validation but has no MX record — and never will. This check eliminates entire categories of typos and recently-expired domains.

# Manual check: look up MX records for a domain
dig MX gmail.com
# ;; ANSWER SECTION:
# gmail.com. 3600 IN MX 5 gmail-smtp-in.l.google.com.

Layer 3: SMTP verification

The most valuable check is an SMTP dialogue with the mail server. The verifier connects to the MX server, starts a conversation, and asks whether the mailbox exists — without actually sending an email.

The exchange looks like this:

→ EHLO verifier.mailbeam.dev ← 250-smtp.gmail.com Hello → MAIL FROM:<probe@mailbeam.dev> ← 250 2.1.0 OK → RCPT TO:<user@gmail.com> ← 250 2.1.5 OK ← mailbox exists

If the server responds with a 550 or 551 to the RCPT TO command, the mailbox doesn't exist. The verifier then issues a RSET to abort before any message is sent.

This catches addresses that pass syntax and MX checks but have no actual inbox — common after employee churn, account closures, or bulk-created fake accounts.

SMTP limitations

Some mail servers don't play along. They respond 250 OK to every address regardless (catch-all behaviour) to avoid revealing which mailboxes exist. Others rate-limit SMTP verification probes. This is where layer four matters.

Layer 4: Scoring the ambiguous cases

When SMTP gives an ambiguous result — typically a catch-all domain — you need something other than a yes or no. Some services apply a machine learning model trained on historical sending data. Mailbeam uses an additive score instead: a fixed set of signals with published weights and caps, where a catch-all address can never exceed 70 because no probe can confirm it.

The trade-off is deliberate. A rubric will not out-predict a good model on genuinely ambiguous addresses, but every point is traceable to a check, so when the number surprises you it is possible to find out why. The output is 0–100 rather than a binary result, which lets you apply different thresholds per use case:

const result = await verifyEmail(email);

if (result.catchAll) {
  // Corporate email — accept high-confidence, flag medium, reject low
  if (result.score >= 70) return "accept";
  if (result.score >= 40) return "flag_for_review";
  return "reject";
}

Layer 5: Metadata checks

Beyond deliverability, verification APIs surface useful metadata:

  • disposable — is this a known temporary email domain (Mailinator, 10MinuteMail, etc.)?
  • role — is this a role address like info@, noreply@, admin@?
  • suggestion — did the user probably mean gmail.com instead of gmai.com?
  • free — is this a free provider (Gmail, Outlook)?

These flags let you apply different rules for different address types — for example, accepting role addresses for billing contacts but rejecting them for marketing lists.

When to use email verification

At signup (real-time)

The highest-value integration point. Verify the address before the account is created, while the user is still on the form.

// Next.js Server Action
async function createAccount(formData: FormData) {
  const email = formData.get("email") as string;

  const result = await verifyEmail(email);
  if (!result.valid) {
    return { error: result.suggestion
      ? `Did you mean ${result.suggestion}?`
      : "That email address doesn't appear to be valid." };
  }
  if (result.disposable) {
    return { error: "Temporary email addresses are not allowed." };
  }

  // proceed with account creation
}

Real-time verification requires a sub-100ms API — anything slower and you're blocking the user's form submission noticeably.

Before bulk sends

Before importing a CSV or sending to a cold list, run a batch verification job. This protects your sender reputation before any damage is done.

const auth = { Authorization: `Bearer ${process.env.MAILBEAM_KEY}` };

// Submit the list; the response is a job id, not the results.
const job = await fetch("https://api.mailbeam.dev/v1/verify/batch", {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({ emails }),
}).then((r) => r.json());

// Wait for the batch.completed webhook, or poll /v1/jobs/:id until it is done.
const { results } = await fetch(
  `https://api.mailbeam.dev/v1/jobs/${job.job_id}/results?format=json`,
  { headers: auth }
).then((r) => r.json());

const deliverable = results
  .filter((r) => r.status === "deliverable" && r.score >= 60)
  .map((r) => r.email);

Periodic list cleaning

Email addresses decay at roughly 20–30% per year. Addresses that were valid 18 months ago may now bounce. Running list cleaning every 6 months keeps bounce rates in check without requiring frontend changes.

What to do with the result

A common mistake is treating verification as a binary gate. The better pattern is tiered handling:

ResultAction
valid: true, score >= 70Accept immediately
valid: true, score 40–69Accept but monitor engagement
valid: true, score < 40, catchAll: trueAccept for high-intent flows; reject for cold outreach
valid: false, suggestionShow suggestion to user
disposable: trueBlock with a message
valid: false, no suggestionShow generic validation error

Summary

Email verification is a pipeline, not a single check:

  1. Syntax — is the format valid?
  2. DNS/MX — does the domain accept mail?
  3. SMTP — does the specific mailbox exist?
  4. Scoring — for catch-all domains, how much confidence is left?
  5. Metadata — is it disposable, a role address, or a typo?

Running all five layers — either via an API or by building your own pipeline — is what separates a real address from one that looks real.

Next steps