Email Verification API Tutorial with Code Examples | emails-wipes.com
Complete guide to integrating email verification APIs. Includes code examples in JavaScript, Python, PHP, Ruby. Real-time validation, bulk processing.
⏱️ 15 minute read | Published: February 10, 2026
Email Verification API Tutorial with Code Examples
TL;DR: Learn how to integrate email verification APIs into your applications with practical code examples in JavaScript, Python, PHP, and Ruby. Covers real-time validation, bulk processing, error handling, and best practices.
Why Use an Email Verification API?
Email verification APIs provide programmatic access to validation services, allowing you to:
- Real-time validation - verify emails instantly during user signup
- Bulk processing - validate thousands of emails in minutes (see our email list cleaning guide)
- Automated workflows - integrate with CRMs, marketing tools, webhooks
- Multi-layer checks - syntax, domain, SMTP, disposable detection
- Detailed results - structured JSON responses with validation details
API Basics: RESTful Email Verification
Endpoint Structure
Most email verification APIs follow REST principles:
Base URL
https://emails-wipes.com/api/v1
Authentication
API key via header:
Authorization: Bearer YOUR_API_KEY
Common Endpoints
POST /verify- Single email verificationPOST /verify/bulk- Batch verification (up to 10,000 emails)GET /status/{job_id}- Check bulk job statusGET /credits- Check remaining API credits
Single Email Verification
Request Format
POST https://emails-wipes.com/api/v1/verify
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
{
"email": "[email protected]"
}
Response Format
{
"email": "[email protected]",
"valid": true,
"details": {
"syntax": "valid",
"domain": "valid",
"smtp": "valid",
"disposable": false,
"role_based": false,
"free_email": false,
"catch_all": false
},
"quality_score": 95,
"verification_time_ms": 1847,
"timestamp": "2026-02-10T01:30:00Z"
}
Response Fields Explained
| Field | Type | Description |
|---|---|---|
email |
string | The verified email address |
valid |
boolean | true = deliverable, false = invalid |
syntax |
string | valid | invalid (RFC 5322 compliance) |
domain |
string | valid | invalid (DNS + MX records) |
smtp |
string | valid | invalid | unknown (mailbox verification) |
disposable |
boolean | true = temporary email service (10minutemail, etc.) |
role_based |
boolean | true = generic address (info@, support@) |
free_email |
boolean | true = free provider (Gmail, Yahoo, Hotmail) |
catch_all |
boolean | true = domain accepts any email (uncertain deliverability) |
quality_score |
number | 0-100 (confidence score, higher = better) |
Code Examples: Single Email Verification
JavaScript (Fetch API)
async function verifyEmail(email) {
const response = await fetch('https://emails-wipes.com/api/v1/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({ email })
});
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const result = await response.json();
if (result.valid && result.quality_score >= 80) {
console.log('✅ Email is valid and high quality');
} else if (result.valid && result.catch_all) {
console.log('⚠️ Email is valid but catch-all (uncertain)');
} else {
console.log('❌ Email is invalid');
}
return result;
}
// Usage
verifyEmail('[email protected]')
.then(result => console.log(result))
.catch(error => console.error('Verification failed:', error));
JavaScript (Node.js with Axios)
const axios = require('axios');
async function verifyEmail(email) {
try {
const response = await axios.post('https://emails-wipes.com/api/v1/verify',
{ email },
{
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
}
);
const { valid, details, quality_score } = response.data;
if (valid) {
console.log(`✅ Email valid (score: ${quality_score})`);
// Check for red flags
if (details.disposable) {
console.warn('⚠️ Disposable email detected');
}
if (details.role_based) {
console.warn('⚠️ Role-based email (low engagement)');
}
} else {
console.log('❌ Email invalid');
}
return response.data;
} catch (error) {
if (error.response) {
console.error('API error:', error.response.status, error.response.data);
} else {
console.error('Request failed:', error.message);
}
throw error;
}
}
module.exports = { verifyEmail };
Python (requests library)
import requests
def verify_email(email):
"""Verify a single email address"""
url = 'https://emails-wipes.com/api/v1/verify'
headers = {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
}
payload = {'email': email}
try:
response = requests.post(url, json=payload, headers=headers, timeout=10)
response.raise_for_status() # Raise exception for 4xx/5xx
result = response.json()
if result['valid']:
print(f"✅ {email} is valid (score: {result['quality_score']})")
# Check for warnings
if result['details']['disposable']:
print("⚠️ Disposable email detected")
if result['details']['catch_all']:
print("⚠️ Catch-all domain (uncertain deliverability)")
else:
print(f"❌ {email} is invalid")
return result
except requests.exceptions.RequestException as e:
print(f"❌ Verification failed: {e}")
return None
# Usage
if __name__ == '__main__':
verify_email('[email protected]')
PHP (cURL)
<?php
function verifyEmail($email) {
$url = 'https://emails-wipes.com/api/v1/verify';
$apiKey = 'YOUR_API_KEY';
$data = json_encode(['email' => $email]);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
"Authorization: Bearer $apiKey"
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
error_log("API error: HTTP $httpCode");
return null;
}
$result = json_decode($response, true);
if ($result['valid']) {
echo "✅ Email is valid (score: {$result['quality_score']})\n";
// Check warnings
if ($result['details']['disposable']) {
echo "⚠️ Disposable email\n";
}
} else {
echo "❌ Email is invalid\n";
}
return $result;
}
// Usage
verifyEmail('[email protected]');
Ruby
require 'net/http'
require 'json'
require 'uri'
def verify_email(email)
url = URI('https://emails-wipes.com/api/v1/verify')
api_key = 'YOUR_API_KEY'
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request['Authorization'] = "Bearer #{api_key}"
request['Content-Type'] = 'application/json'
request.body = { email: email }.to_json
response = http.request(request)
if response.code.to_i != 200
puts "❌ API error: #{response.code}"
return nil
end
result = JSON.parse(response.body)
if result['valid']
puts "✅ Email valid (score: #{result['quality_score']})"
# Check warnings
puts "⚠️ Disposable email" if result['details']['disposable']
puts "⚠️ Catch-all domain" if result['details']['catch_all']
else
puts "❌ Email invalid"
end
result
end
# Usage
verify_email('[email protected]')
Bulk Email Verification
For validating large lists (1,000+), use the bulk API for better performance and cost efficiency.
Step 1: Submit Bulk Job
POST https://emails-wipes.com/api/v1/verify/bulk
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
{
"emails": [
"[email protected]",
"[email protected]",
"[email protected]"
// ... up to 10,000 emails
],
"webhook_url": "https://yoursite.com/webhook/verification-complete" // optional
}
Response: Job Created
{
"job_id": "bulk_abc123xyz",
"status": "processing",
"total_emails": 10000,
"estimated_completion_seconds": 1200,
"created_at": "2026-02-10T01:30:00Z"
}
Step 2: Poll Job Status
GET https://emails-wipes.com/api/v1/status/bulk_abc123xyz
Authorization: Bearer YOUR_API_KEY
Response: Job Status
{
"job_id": "bulk_abc123xyz",
"status": "completed",
"progress": 100,
"total_emails": 10000,
"processed_emails": 10000,
"valid_emails": 8735,
"invalid_emails": 1265,
"processing_time_seconds": 1147,
"download_url": "https://emails-wipes.com/api/v1/download/bulk_abc123xyz",
"expires_at": "2026-02-17T01:30:00Z"
}
Step 3: Download Results
GET https://emails-wipes.com/api/v1/download/bulk_abc123xyz
Authorization: Bearer YOUR_API_KEY
Response: CSV file with validation results:
email,valid,syntax,domain,smtp,disposable,role_based,catch_all,quality_score [email protected],true,valid,valid,valid,false,false,false,98 [email protected],false,valid,valid,invalid,false,false,false,25 [email protected],true,valid,valid,valid,false,false,true,75
Bulk Verification Code Example (Python)
import requests
import time
import csv
class EmailVerifier:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = 'https://emails-wipes.com/api/v1'
self.headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
def verify_bulk(self, emails, webhook_url=None):
"""Submit bulk verification job"""
url = f'{self.base_url}/verify/bulk'
payload = {'emails': emails}
if webhook_url:
payload['webhook_url'] = webhook_url
response = requests.post(url, json=payload, headers=self.headers)
response.raise_for_status()
return response.json()
def check_status(self, job_id):
"""Check bulk job status"""
url = f'{self.base_url}/status/{job_id}'
response = requests.get(url, headers=self.headers)
response.raise_for_status()
return response.json()
def download_results(self, job_id, output_file='results.csv'):
"""Download verification results"""
url = f'{self.base_url}/download/{job_id}'
response = requests.get(url, headers=self.headers, stream=True)
response.raise_for_status()
with open(output_file, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f"✅ Results saved to {output_file}")
return output_file
def wait_for_completion(self, job_id, poll_interval=30):
"""Poll job status until completion"""
while True:
status_data = self.check_status(job_id)
status = status_data['status']
progress = status_data.get('progress', 0)
print(f"Status: {status} | Progress: {progress}%")
if status == 'completed':
return status_data
elif status == 'failed':
raise Exception(f"Job failed: {status_data.get('error')}")
time.sleep(poll_interval)
# Usage Example
if __name__ == '__main__':
verifier = EmailVerifier('YOUR_API_KEY')
# Read emails from file
with open('emails.txt', 'r') as f:
emails = [line.strip() for line in f if line.strip()]
print(f"Submitting {len(emails)} emails for verification...")
# Submit bulk job
job = verifier.verify_bulk(emails)
job_id = job['job_id']
print(f"Job created: {job_id}")
print(f"Estimated completion: {job['estimated_completion_seconds']} seconds")
# Wait for completion
result = verifier.wait_for_completion(job_id)
print(f"\n✅ Verification complete!")
print(f"Valid: {result['valid_emails']}")
print(f"Invalid: {result['invalid_emails']}")
# Download results
verifier.download_results(job_id, 'verified_emails.csv')
Error Handling
Common HTTP Status Codes
| Code | Meaning | Action |
|---|---|---|
200 |
Success | Parse response |
400 |
Bad request (invalid email format) | Check input validation |
401 |
Unauthorized (invalid API key) | Check API key |
402 |
Insufficient credits | Top up account |
429 |
Rate limit exceeded | Retry with exponential backoff |
500 |
Server error | Retry or contact support |
solid Error Handling Example
async function verifyEmailWithRetry(email, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('https://emails-wipes.com/api/v1/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({ email })
});
// Handle different HTTP status codes
if (response.status === 200) {
return await response.json();
} else if (response.status === 400) {
throw new Error('Invalid email format');
} else if (response.status === 401) {
throw new Error('Invalid API key');
} else if (response.status === 402) {
throw new Error('Insufficient credits');
} else if (response.status === 429) {
// Rate limited - exponential backoff
const waitTime = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
console.warn(`Rate limited, retrying in ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
} else if (response.status >= 500) {
// Server error - retry
if (attempt < maxRetries) {
console.warn(`Server error, attempt ${attempt}/${maxRetries}`);
await new Promise(resolve => setTimeout(resolve, 2000));
continue;
}
throw new Error('Server error after retries');
} else {
throw new Error(`Unexpected status: ${response.status}`);
}
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
console.warn(`Attempt ${attempt} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
Real-Time Form Validation
Client-Side + Server-Side Pattern
<!-- HTML Form -->
<form id="signupForm">
<input type="email" id="emailInput" placeholder="Enter your email" />
<span id="validationMessage"></span>
<button type="submit" id="submitButton" disabled>Sign Up</button>
</form>
<script>
const emailInput = document.getElementById('emailInput');
const validationMessage = document.getElementById('validationMessage');
const submitButton = document.getElementById('submitButton');
let debounceTimer;
emailInput.addEventListener('input', (e) => {
const email = e.target.value;
// Clear previous timer
clearTimeout(debounceTimer);
// Basic client-side syntax check (instant)
const basicRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!basicRegex.test(email)) {
validationMessage.textContent = '❌ Invalid email format';
validationMessage.className = 'error';
submitButton.disabled = true;
return;
}
// Debounce API call (wait for user to stop typing)
debounceTimer = setTimeout(async () => {
validationMessage.textContent = '⏳ Verifying...';
validationMessage.className = 'loading';
try {
const response = await fetch('/api/verify-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const result = await response.json();
if (result.valid && result.quality_score >= 70) {
validationMessage.textContent = '✅ Email is valid';
validationMessage.className = 'success';
submitButton.disabled = false;
} else if (result.details.disposable) {
validationMessage.textContent = '⚠️ Disposable email not allowed';
validationMessage.className = 'warning';
submitButton.disabled = true;
} else {
validationMessage.textContent = '❌ Email address is invalid';
validationMessage.className = 'error';
submitButton.disabled = true;
}
} catch (error) {
console.error('Verification failed:', error);
validationMessage.textContent = '⚠️ Could not verify (optional check)';
validationMessage.className = 'warning';
submitButton.disabled = false; // Allow signup if API fails
}
}, 800); // Wait 800ms after user stops typing
});
</script>
Backend Server Route (Express.js)
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.post('/api/verify-email', async (req, res) => {
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email required' });
}
try {
const response = await axios.post('https://emails-wipes.com/api/v1/verify',
{ email },
{
headers: {
'Authorization': `Bearer ${process.env.EMAIL_VERIFY_API_KEY}`
},
timeout: 5000 // 5 second timeout
}
);
res.json(response.data);
} catch (error) {
console.error('Email verification error:', error.message);
res.status(500).json({ error: 'Verification service unavailable' });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Best Practices
✅ Do This
- Cache results - store validation results for 30-90 days, don't re-verify the same email
- Implement rate limiting - use exponential backoff if you hit API rate limits
- Set timeouts - 5-10 seconds max per API call to prevent slow responses from blocking users
- Graceful degradation - if API fails, allow signup but flag for manual review
- Use bulk API for large lists - 10x faster and cheaper than individual calls
- Monitor API usage - track credits/costs to avoid unexpected bills
- Log validation results - helps debug issues and track quality trends
❌ Don't Do This
- Expose API keys client-side - always proxy through your backend
- Block signups on API failure - prioritize user experience over perfect validation
- Verify on every keystroke - debounce input (500-800ms) to reduce API calls
- Ignore catch-all emails - treat as "uncertain", not "invalid" (learn about catch-all detection)
- Hard-reject disposable emails - depends on your use case (some are legitimate)
- Forget double opt-in - API validation + email confirmation = best security
Rate Limiting & Cost Optimization
Caching Strategy
// Redis cache example (Node.js)
const redis = require('redis');
const client = redis.createClient();
async function verifyCached(email) {
// Check cache first
const cached = await client.get(`email:${email}`);
if (cached) {
console.log('✅ Cache hit');
return JSON.parse(cached);
}
// Cache miss - call API
console.log('⏳ Cache miss - calling API');
const result = await verifyEmail(email);
// Store in cache (30 days)
await client.setex(`email:${email}`, 30 * 24 * 60 * 60, JSON.stringify(result));
return result;
}
Request Batching
// Batch multiple emails into single bulk request
class EmailVerificationQueue {
constructor(apiKey, batchSize = 100, flushInterval = 5000) {
this.apiKey = apiKey;
this.batchSize = batchSize;
this.flushInterval = flushInterval;
this.queue = [];
this.timer = null;
}
add(email) {
return new Promise((resolve, reject) => {
this.queue.push({ email, resolve, reject });
// Flush if batch size reached
if (this.queue.length >= this.batchSize) {
this.flush();
} else if (!this.timer) {
// Set timer to flush after interval
this.timer = setTimeout(() => this.flush(), this.flushInterval);
}
});
}
async flush() {
if (this.queue.length === 0) return;
clearTimeout(this.timer);
this.timer = null;
const batch = this.queue.splice(0, this.batchSize);
const emails = batch.map(item => item.email);
try {
const response = await axios.post('https://emails-wipes.com/api/v1/verify/bulk',
{ emails },
{ headers: { 'Authorization': `Bearer ${this.apiKey}` } }
);
const jobId = response.data.job_id;
const results = await this.waitForResults(jobId);
// Resolve individual promises
batch.forEach((item, index) => {
item.resolve(results[index]);
});
} catch (error) {
batch.forEach(item => item.reject(error));
}
}
async waitForResults(jobId) {
// Poll until completion (simplified)
while (true) {
const status = await axios.get(`https://emails-wipes.com/api/v1/status/${jobId}`,
{ headers: { 'Authorization': `Bearer ${this.apiKey}` } }
);
if (status.data.status === 'completed') {
const download = await axios.get(status.data.download_url,
{ headers: { 'Authorization': `Bearer ${this.apiKey}` } }
);
return download.data; // Assuming JSON response
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
}
// Usage
const queue = new EmailVerificationQueue('YOUR_API_KEY');
// Individual requests are automatically batched
const result1 = await queue.add('[email protected]');
const result2 = await queue.add('[email protected]');
// ... batched together if within 5 seconds
Security Considerations
🔒 API Key Security
- Never expose keys client-side - API calls must go through your backend
- Use environment variables - store keys in
.envfiles, not code - Rotate keys regularly - change API keys every 3-6 months
- Implement rate limiting - prevent abuse of your proxy endpoint
- Validate input - sanitize user-provided emails before passing to API
Monitoring & Logging
// Structured logging example
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'email-verification.log' })
]
});
async function verifyEmailWithLogging(email) {
const startTime = Date.now();
try {
const result = await verifyEmail(email);
const duration = Date.now() - startTime;
logger.info({
event: 'email_verification',
email: email.replace(/^(.{2}).*(@.*)$/, '$1***$2'), // Partially redact
valid: result.valid,
quality_score: result.quality_score,
duration_ms: duration,
timestamp: new Date().toISOString()
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
logger.error({
event: 'email_verification_failed',
email: email.replace(/^(.{2}).*(@.*)$/, '$1***$2'),
error: error.message,
duration_ms: duration,
timestamp: new Date().toISOString()
});
throw error;
}
}
Testing
Unit Test Example (Jest)
const { verifyEmail } = require('./email-verifier');
const nock = require('nock');
describe('Email Verification', () => {
beforeEach(() => {
nock.cleanAll();
});
test('should return valid for existing email', async () => {
nock('https://emails-wipes.com')
.post('/api/v1/verify', { email: '[email protected]' })
.reply(200, {
email: '[email protected]',
valid: true,
details: { syntax: 'valid', domain: 'valid', smtp: 'valid' },
quality_score: 95
});
const result = await verifyEmail('[email protected]');
expect(result.valid).toBe(true);
expect(result.quality_score).toBeGreaterThan(90);
});
test('should return invalid for non-existent email', async () => {
nock('https://emails-wipes.com')
.post('/api/v1/verify', { email: '[email protected]' })
.reply(200, {
email: '[email protected]',
valid: false,
details: { syntax: 'valid', domain: 'invalid', smtp: 'invalid' },
quality_score: 15
});
const result = await verifyEmail('[email protected]');
expect(result.valid).toBe(false);
});
test('should handle API errors gracefully', async () => {
nock('https://emails-wipes.com')
.post('/api/v1/verify')
.reply(500, { error: 'Internal server error' });
await expect(verifyEmail('[email protected]')).rejects.toThrow();
});
});
Key Takeaways
- ✅ Always proxy API calls through backend - never expose API keys client-side
- ✅ Implement caching - 30-day cache reduces costs by 90%+
- ✅ Use bulk API for large lists - 10x faster, more cost-effective
- ✅ Graceful degradation - don't block users if API fails
- ✅ Debounce input validation - 500-800ms delay reduces unnecessary calls
- ✅ Monitor usage and costs - track API calls and credit consumption
- ✅ Combine with double opt-in - API validation + email confirmation = best practice
🚀 Get Your API Key
Start verifying emails with our fast, accurate API
✅ 98.5% accuracy | ⚡ < 2 seconds per email | 🔒 Zero data retention
$0.75 per 1,000 emails with code REDDIT50
Get API Access →Related Articles
- Why Email Validation Matters - Business impact and ROI
- Hard Bounce vs Soft Bounce - Understanding bounce types
- Email Deliverability Guide 2024 - Complete inbox placement guide
Related Articles
Email Verification API: How It Works & Why You Need One
Learn how an email verification API works, why your application needs one, and how to integrate real-time email validati...
February 09, 2026 · 11 min readBest Email Verification Services Compared (2024)
Compare the top 6 email verification services in 2024: pricing, features, speed, accuracy. See why emails-wipes.com is 5...
December 03, 2025 · 11 min readEmail Verification for Shopify Stores: Complete Guide (2024)
Learn how to validate emails in your Shopify store to reduce bounce rate, improve deliverability, and increase abandoned...
January 16, 2026 · 9 min read