Email Verification API Integration Guide: Real-Time Validation at Sign-Up | BounceZero
| Developer Guide | 8 min read | | 80 views

Email Verification API Integration Guide: Real-Time Validation at Sign-Up

Integrating email verification at the point of sign-up is the most effective way to keep your list clean. Here's how to do it in JavaScript, Python, and PHP with real code examples.

Cleaning your existing list is a reactive fix. The proactive solution is verifying email addresses at the point of capture — before they enter your database, CRM, or marketing platform. An email verification API call takes under 500ms and can block invalid, disposable, and role addresses from ever touching your list. This guide covers integration patterns for common languages and frameworks, error handling, UX considerations, and what to do with the API response.

When to Call the API

Real-time verification makes sense at two moments: on form submit (before the form completes) and on import (before a bulk import reaches your database). For form submit, the API call happens server-side after the form posts — you validate, then decide whether to accept or reject the submission. Never call the verification API directly from client-side JavaScript with your API key exposed. For imports, run the API in batch mode (or use the bulk verification endpoint) before processing. A third integration point is webhook-triggered: when a new contact is added via a CRM integration, trigger verification automatically and flag or quarantine risky addresses.

API Request Structure

The BounceZero single-address verification endpoint is: POST https://app.bouncezero.io/api/v1/verify with JSON body {"email": "[email protected]"}. Authentication uses an API key in the Authorization header: Authorization: Bearer YOUR_API_KEY. The response includes: result (valid / invalid / risky / unknown), score (0–100 confidence), classification (the specific verdict: valid, hard_bounce, disposable, catch_all, role_account, etc.), is_disposable (boolean), is_catch_all (boolean), is_role_account (boolean), domain_exists (boolean), mx_found (boolean), and smtp_check (boolean). Use the result field for pass/fail logic and the classification for more granular handling.

JavaScript / Node.js Integration

Server-side Node.js example using fetch:

async function verifyEmail(email) {

const response = await fetch('https://app.bouncezero.io/api/v1/verify', {

method: 'POST',

headers: {

'Authorization': 'Bearer ' + process.env.BOUNCEZERO_API_KEY,

'Content-Type': 'application/json'

},

body: JSON.stringify({ email })

});

if (!response.ok) throw new Error('Verification API error: ' + response.status);

return response.json();

}

// In your Express route:

app.post('/register', async (req, res) => {

const { email } = req.body;

try {

const result = await verifyEmail(email);

if (result.result === 'invalid') {

return res.status(422).json({ error: 'This email address is not valid. Please check and try again.' });

}

if (result.is_disposable) {

return res.status(422).json({ error: 'Disposable email addresses are not accepted. Please use your real email.' });

}

// Proceed with registration...

} catch (err) {

// Fail open: if API is unavailable, allow the registration

console.error('Email verification failed:', err);

}

});

Python Integration

Python example using httpx (async) or requests (sync):

import httpx

import os

async def verify_email(email: str) -> dict:

api_key = os.environ['BOUNCEZERO_API_KEY']

async with httpx.AsyncClient(timeout=5.0) as client:

response = await client.post(

'https://app.bouncezero.io/api/v1/verify',

headers={'Authorization': f'Bearer {api_key}'},

json={'email': email}

)

response.raise_for_status()

return response.json()

# FastAPI route example:

@app.post('/register')

async def register(email: str):

try:

result = await verify_email(email)

if result['result'] == 'invalid':

raise HTTPException(422, 'Invalid email address')

if result.get('is_disposable'):

raise HTTPException(422, 'Disposable email addresses are not accepted')

except httpx.RequestError:

pass # Fail open if API unreachable

# Proceed with registration...

PHP Integration

PHP example using cURL:

function verifyEmail(string $email): ?array {

$apiKey = getenv('BOUNCEZERO_API_KEY');

$ch = curl_init('https://app.bouncezero.io/api/v1/verify');

curl_setopt_array($ch, [

CURLOPT_RETURNTRANSFER => true,

CURLOPT_POST => true,

CURLOPT_POSTFIELDS => json_encode(['email' => $email]),

CURLOPT_HTTPHEADER => [

'Authorization: Bearer ' . $apiKey,

'Content-Type: application/json'

],

CURLOPT_TIMEOUT => 5,

]);

$body = curl_exec($ch);

$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

curl_close($ch);

if ($status !== 200 || !$body) return null; // fail open

return json_decode($body, true);

}

// Usage:

$result = verifyEmail($_POST['email']);

if ($result && $result['result'] === 'invalid') {

$errors[] = 'This email address is not valid.';

}

Handling the Response: What to Block and What to Allow

Not all negative results should block registration. Here's a practical decision matrix: result=invalid → block, show error. result=valid → allow. result=risky + is_disposable=true → block (disposable addresses indicate throwaway intent). result=risky + is_role_account=true → allow but flag for suppression from marketing; still useful for transactional. result=risky + is_catch_all=true → allow but tag in database; monitor engagement. result=unknown → allow (fail open); you can retry verification asynchronously after registration. The key principle: be strict about invalids and disposables, lenient about catch-all and unknown. Blocking too aggressively frustrates real users; blocking too leniently defeats the purpose.

UX: How to Present Validation Errors

The error message matters. 'Invalid email' is confusing when users typed their address correctly — the problem might be a typo they don't see. Better messages: For invalid addresses: 'We couldn't verify this email address. Please double-check for typos and try again.' For disposables: 'Please use your work or personal email — temporary email addresses are not accepted.' For domain doesn't exist: 'The domain [domain.com] doesn't appear to exist. Did you mistype it?' Show the error inline under the email field, not as a page-level alert. Don't block form submission on a slow API response — if the verification takes more than 3 seconds, allow submission and verify asynchronously. Never reveal to users the specific reason an address failed (e.g., 'this is a catch-all domain') — that's internal classification data.

Rate Limits, Caching, and Fallback

Cache verification results for the same address to avoid duplicate API calls. A simple in-memory or Redis cache with a 24-hour TTL is sufficient — email validity doesn't change minute to minute. Cache key: SHA-256 of the normalized email (lowercased, trimmed). Always implement a timeout on the API call (5 seconds maximum) with fail-open behavior — if the verification API is unreachable, allow the registration rather than blocking a real user. For high-traffic sign-up forms, use the asynchronous pattern: accept the registration immediately, trigger verification in a background job, then suppress or flag the contact if it comes back invalid. This approach has zero latency impact on the user experience.

Frequently Asked Questions

Is it safe to call an email verification API from the browser?

No. Never expose your API key in client-side JavaScript — it would be visible to anyone who inspects network requests. Always make the verification call from your server, where the API key is stored as an environment variable and never exposed to the client.

How fast is the email verification API?

BounceZero's single-address verification averages 340ms for addresses that can be fully resolved. SMTP checks on slow mail servers can take up to 3–4 seconds. For real-time sign-up flows, set a 5-second timeout and fail open if it's exceeded — the vast majority of checks will complete well within that window.

What is 'fail open' and why does it matter?

Fail open means allowing an action to proceed when a validation system is unavailable, rather than blocking it. For email verification at sign-up, failing open means: if the API times out or returns an error, let the user register anyway. The alternative — failing closed — would block real users every time the API has a hiccup, which is a much worse user experience than occasionally letting an invalid address through.

Can I verify emails in bulk via the API?

Yes. The BounceZero bulk endpoint accepts a list of emails in one request and returns results asynchronously. For large lists (1,000+ addresses), use the bulk upload endpoint rather than looping the single-address API — it's faster, cheaper per verification, and avoids rate limit issues.

How do I handle users who claim their valid email is being rejected?

Build an override mechanism: a manual re-check button that re-calls the API with a freshly cleared cache, and an admin interface to allowlist specific addresses. Some corporate mail servers are intermittently slow to respond, causing 'unknown' results to be incorrectly cached. A 24-hour TTL on your cache handles most of these cases automatically.

email verification API API integration real-time validation JavaScript Python PHP
AL

Written by

Ayoub Lebda

Founder, BounceZero - Email-infrastructure engineer

Ayoub built BounceZero's 5-stage validation pipeline, its dedicated BGP-announced IP infrastructure, and the Patroni HA PostgreSQL cluster behind every verification. Previously built high-volume email delivery infrastructure. Trained at 1337 Benguerir (École 42 network, 2019). Open-source: bgp_analyzer.