Email Verification API: Integrate in 10 Minutes (Code Examples)

Email Verification API: Integrate in 10 Minutes (Code Examples)

You've decided to add email verification to your application. Good call — it's the single highest-impact change you can make to improve signup quality, reduce bounces, and prevent fraud.

This guide gives you production-ready code for integrating email verification into any stack. Python, JavaScript, PHP, cURL — pick your language and copy the code.


Why Server-Side Verification Matters

Before we write any code, let's be clear about one thing: client-side email validation is not enough.

// This is NOT verification — it's just syntax checking
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
// "thisdoesnotexist8472@gmail.com" → true (syntax is fine, mailbox doesn't exist)

Client-side regex catches formatting errors. It doesn't tell you whether the mailbox actually exists. For that, you need SMTP-level verification — and that requires a server-side API call.


Getting Your API Key

First, sign up at email-validaton.com and grab your API key from the dashboard. Free accounts get 100 verifications/month.

Keep your API key secret — it should never appear in client-side JavaScript. Always call the verification API from your backend.


Single Email Verification

Python

import requests
from typing import Optional

def verify_email(email: str, api_key: str) -> dict:
    """Verify a single email address."""
    resp = requests.get(
        "https://email-validaton.com/api/v1/verify",
        params={"email": email},
        headers={"Authorization": f"Bearer {api_key}"},
        timeout=15,
    )
    resp.raise_for_status()
    return resp.json()

# Usage
result = verify_email("user@example.com", "YOUR_API_KEY")
print(f"Status: {result['status']}")
print(f"Score: {result['score']}/10")
print(f"Confidence: {result.get('confidence', 'N/A')}")

if result["status"] == "valid":
    # Proceed with account creation
    create_user(email)
elif result["status"] in ("invalid", "disposable"):
    # Reject or flag
    raise ValueError(f"Invalid email: {result['reason']}")

JavaScript (Node.js)

async function verifyEmail(email, apiKey) {
  const url = new URL("https://email-validaton.com/api/v1/verify");
  url.searchParams.set("email", email);

  const resp = await fetch(url, {
    headers: { Authorization: `Bearer ${apiKey}` },
  });

  if (!resp.ok) throw new Error(`API error: ${resp.status}`);
  return resp.json();
}

// Usage
const result = await verifyEmail("user@example.com", process.env.EV_API_KEY);
console.log(`Status: ${result.status}, Score: ${result.score}/10`);

if (result.status === "valid") {
  await db.users.insert({ email, verified: true });
} else {
  throw new Error(result.reason || "Email verification failed");
}

PHP

function verify_email(string $email, string $api_key): array {
    $url = "https://email-validaton.com/api/v1/verify?email=" . urlencode($email);

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ["Authorization: Bearer $api_key"],
        CURLOPT_TIMEOUT => 15,
    ]);

    $body = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($http_code !== 200) {
        throw new RuntimeException("API returned $http_code");
    }

    return json_decode($body, true);
}

// Usage
$result = verify_email("user@example.com", getenv("EV_API_KEY"));
echo "Status: {$result['status']}, Score: {$result['score']}/10\n";

cURL (CLI / Shell Scripts)

#!/bin/bash
API_KEY="your_api_key"
EMAIL="user@example.com"

curl -s -H "Authorization: Bearer $API_KEY" \
  "https://email-validaton.com/api/v1/verify?email=$EMAIL" | python3 -m json.tool

Understanding the Response

Every verification returns a consistent JSON structure:

{
  "email": "user@gmail.com",
  "status": "valid",
  "score": 9,
  "confidence": "high",
  "reason": "Mailbox exists and accepts mail",
  "mx_server": "gmail-smtp-in.l.google.com",
  "duration_ms": 287,
  "disposable": false,
  "role_account": false,
  "free_provider": true
}

Key Fields Explained

Field Values Meaning
status valid, invalid, catch_all, unknown, risky, disposable, over_quota, retry, error The verification result
score 0–10 Composite quality score. 8+ = excellent, 5-7 = moderate, <5 = risky
confidence high, medium, low How reliable the result is
reason Human-readable string Why the status was assigned
disposable boolean Temporary email address
role_account boolean info@, support@, admin@, etc.
free_provider boolean Gmail, Yahoo, Outlook, etc.

Integration Patterns

Pattern 1: Registration Form Validation (Most Common)

@app.route("/register", methods=["POST"])
def register():
    email = request.form["email"]
    password = request.form["password"]

    # 1. Syntax check (fast, local)
    if "@" not in email or "." not in email.split("@")[-1]:
        return jsonify({"error": "Invalid email format"}), 400

    # 2. SMTP verification (API call)
    try:
        result = verify_email(email, current_app.config["EV_API_KEY"])
    except requests.RequestException:
        # API is down — don't block signups, just flag for review
        create_user(email, password, flags=["unverified"])
        return redirect(url_for("dashboard"))

    # 3. Business logic based on result
    if result["status"] == "valid":
        create_user(email, password, verified=True)
        return redirect(url_for("welcome"))

    if result["status"] == "disposable":
        return jsonify({
            "error": "Please use a permanent email address"
        }), 400

    if result["status"] == "invalid":
        return jsonify({
            "error": "This email address doesn't appear to exist"
        }), 400

    # Catch-all, unknown, risky — allow but flag
    create_user(email, password, flags=[result["status"]])
    return redirect(url_for("welcome"))

Pattern 2: Async Verification (Non-Blocking)

For production at scale, you don't want users waiting for the API call:

import threading

def verify_async(email: str, api_key: str, user_id: int):
    """Verify email in background and update user record."""
    try:
        result = verify_email(email, api_key)
        db.users.update(user_id, {
            "email_status": result["status"],
            "email_score": result["score"],
            "verified_at": datetime.utcnow(),
        })
    except Exception as e:
        logger.error(f"Background verification failed for {user_id}: {e}")

# In your registration handler:
user = create_user(email, password)
threading.Thread(
    target=verify_async,
    args=(email, api_key, user.id),
    daemon=True,
).start()

Pattern 3: Bulk List Processing

For cleaning large lists via the bulk upload endpoint:

import requests
import time

def bulk_verify(file_path: str, api_key: str) -> str:
    """Upload a file for bulk verification. Returns job ID."""
    with open(file_path, "rb") as f:
        resp = requests.post(
            "https://email-validaton.com/api/v1/bulk",
            files={"file": f},
            headers={"Authorization": f"Bearer {api_key}"},
        )
    return resp.json()["job_id"]

def wait_for_results(job_id: str, api_key: str, poll_sec: int = 5):
    """Poll until the bulk job completes, then download results."""
    while True:
        resp = requests.get(
            f"https://email-validaton.com/api/v1/bulk/status/{job_id}",
            headers={"Authorization": f"Bearer {api_key}"},
        )
        data = resp.json()
        if data["status"] in ("completed", "failed", "cancelled"):
            return data
        time.sleep(poll_sec)

# Usage
job_id = bulk_verify("my_list.csv", API_KEY)
result = wait_for_results(job_id, API_KEY)
# Download the result file
download = requests.get(result["result_url"])

Error Handling & Best Practices

Timeout Handling

The verification API typically responds in 200-800ms. Set a timeout of 10-15 seconds to handle network issues gracefully.

Rate Limiting

Free accounts are limited to 100/day. Paid plans range from 5,000 to 500,000/month. The API returns 429 Too Many Requests when you exceed your limit.

Retry Logic

For transient failures (network errors, 503), retry up to 3 times with exponential backoff:

import time
from requests.exceptions import RequestException

def verify_with_retry(email, api_key, max_retries=3):
    for attempt in range(max_retries):
        try:
            return verify_email(email, api_key)
        except RequestException:
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)

Do Not Block Signups on API Failure

If the verification API is temporarily unavailable, do not block user registration. Instead:
1. Allow signup, flag the account as "pending verification"
2. Run verification asynchronously when the API recovers
3. Notify the user if their email is invalid after the fact

Blocking signups on API downtime loses real users.


Conclusion

Email verification is not optional for production applications. Invalid emails cause bounces, disposable emails enable abuse, and unverified lists damage your sender reputation.

The integration patterns in this guide cover the most common use cases. Copy the code, adapt it to your stack, and start verifying every email that enters your system.

Ready to integrate? Get your free API key — 100 verifications included.

Ready to clean your email list?

200 free verifications. No credit card. Full SMTP validation in under 1 second.

🚀 Create Free Account
MP

Milan Pasić

Milan Pasić is the founder of N-Software and lead developer of Email Validator. He has spent over a decade building email infrastructure, deliverability tools, and SMTP validation systems used by thousands of marketers and developers worldwide.