Validate emails on sign-up forms

Validating addresses at sign-up catches typos and throwaway inboxes before they reach your database, but a check must never stop a real person from signing up. The rule of thumb: block only what is clearly wrong, accept everything else, and re-check what was unclear later.

Call the API from your server

Validate in your sign-up handler on the server, not in browser code: your API key stays secret, and bots cannot spend your credits by calling the API directly. If you also want feedback while the person types, call your own endpoint, add a bot check (such as a CAPTCHA or Turnstile) and rate-limit it.

Validate once, when the form is submitted, rather than on every keystroke: each completed check costs a credit.

Keep it fast

Send "timeout": 6 (or anything from 5 to 15) so slow mail servers do not hold up the form. A check that runs out of time is unknown and free, and you can re-check it later.

Decide on the result

ResultSuggested handling
deliverableAccept.
undeliverableAsk the person to fix the address. Offer did_you_mean when it is set.
risky with disposableAsk for a permanent address, if your product needs one.
risky with another reasonAccept and flag (for example for a confirmation email or review).
unknownAccept and re-check later.
Error 402, 429 or 503, or no answerTreat like unknown: accept and re-check later.

JavaScript

// Returns { ok, message?, suggestion? } for your sign-up handler.
export async function screenSignupEmail(email, ip) {
  let res;
  try {
    res = await fetch('https://api.email-validation.org/v1/validate', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.EV_API_KEY}`,
        'Content-Type': 'application/json',
      },
      // A short budget keeps the form fast; a check that runs out of time is unknown and free.
      body: JSON.stringify({ email, ip_address: ip, timeout: 6 }),
      signal: AbortSignal.timeout(10_000),
    });
  } catch {
    return { ok: true, recheck: true }; // network problem: accept, check later
  }
  if (!res.ok) return { ok: true, recheck: true }; // 402, 429, 503 …: treat like unknown

  const r = await res.json();
  if (r.status === 'undeliverable') {
    return {
      ok: false,
      message: r.did_you_mean ? `Did you mean ${r.did_you_mean}?` : 'Please check this email address.',
      suggestion: r.did_you_mean,
    };
  }
  if (r.status === 'risky' && r.reason === 'disposable') {
    return { ok: false, message: 'Please use a permanent email address.' };
  }
  // deliverable, other risky reasons, unknown: accept (flag risky and unknown for review or a later re-check)
  return { ok: true, flag: r.status === 'risky' || r.status === 'unknown' };
}

Python

import os

import requests

API = "https://api.email-validation.org/v1/validate"


def screen_signup_email(email: str, ip: str | None = None) -> dict:
    """Returns {"ok": bool, "message": str | None}. Never blocks on our errors."""
    payload = {"email": email, "timeout": 6}
    if ip:
        payload["ip_address"] = ip
    try:
        resp = requests.post(
            API,
            headers={"Authorization": f"Bearer {os.environ['EV_API_KEY']}"},
            json=payload,
            timeout=10,
        )
    except requests.RequestException:
        return {"ok": True, "recheck": True}
    if not resp.ok:  # 402, 429, 503 …: treat like unknown
        return {"ok": True, "recheck": True}

    r = resp.json()
    if r["status"] == "undeliverable":
        hint = f"Did you mean {r['did_you_mean']}?" if r["did_you_mean"] else "Please check this email address."
        return {"ok": False, "message": hint}
    if r["status"] == "risky" and r["reason"] == "disposable":
        return {"ok": False, "message": "Please use a permanent email address."}
    return {"ok": True, "flag": r["status"] in ("risky", "unknown")}

Words that help

  • Likely typo: "Did you mean name@gmail.com?" with a button that applies the suggestion.
  • Undeliverable: "We could not deliver to this address. Please check it for typos."
  • Disposable: "Please use an email address you will keep: we send your receipts and sign-in codes there."

Avoid messages that reveal how your screening works in detail, and never tell the person that "our provider" is down: just let them through.

Afterwards

  • Re-check addresses flagged unknown with a bulk job or later single checks.
  • A confirmation email (double opt-in) remains the only way to prove the person controls the mailbox; validation makes sure the message can arrive.
  • Watch your bounce and complaint rates per sign-up source to tune what you accept.

Updated · Maintained by the email-validation.org team