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.