Email Validation Integration Guide: API, ESPs & No-Code (2026) | Email Wipes
Complete email validation integration guide for developers and marketers. REST API quickstart in Node.js, Python, PHP, webhook setup, Mailchimp, HubSpot, Klaviyo, ActiveCampaign, SendGrid, and Zapier workflows.
Email Validation Integration Guide: API, ESPs & No-Code (2026)
Table of Contents
Email validation should be a background layer in your stack — invisible to users, automatic in operation, and feeding clean data everywhere it's needed. This guide walks you through integrating the Email Wipes API from zero to production: quickstart examples in the three most common server-side languages, webhook configuration, native integrations with five major ESPs, a Zapier workflow for no-code teams, and production-grade error handling and rate limiting patterns.
For the full API reference, see the API documentation. For a faster path to your first API call, see the API quick start guide.
API Overview & Authentication
The Email Wipes API is a REST API that accepts JSON and returns JSON. All requests are made over HTTPS. Authentication uses a Bearer token passed in the Authorization header.
Base URL: https://api.emails-wipes.com/v1
Key endpoints:
- POST /verify — single email verification (synchronous, <300ms)
- POST /bulk — batch verification (async, returns a job ID)
- GET /bulk/{jobId} — poll job status and retrieve results
- GET /account — check credit balance and account details
A verification response includes these fields:
| Field | Type | Description |
|---|---|---|
| string | The verified email address | |
| status | string | valid, invalid, risky, unknown |
| sub_status | string | Reason code: mailbox_not_found, disposable, catch_all, role_based, etc. |
| is_disposable | boolean | Disposable/temporary address detection |
| is_role_based | boolean | Role-based address detection |
| mx_found | boolean | Valid MX record present |
| smtp_check | boolean | Mailbox-level SMTP verification passed |
| score | float | 0.0–1.0 deliverability confidence score |
REST API Quickstart
Node.js
// npm install node-fetch (or use built-in fetch in Node 18+)
const API_KEY = process.env.EMAILWIPES_API_KEY;
async function verifyEmail(email) {
const response = await fetch('https://api.emails-wipes.com/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
if (!response.ok) {
const err = await response.json();
throw new Error(`API error ${response.status}: ${err.message}`);
}
return response.json();
}
// Usage
const result = await verifyEmail('[email protected]');
console.log(result.status); // "valid"
console.log(result.score); // 0.98
console.log(result.sub_status); // null
// Block invalid and risky emails at signup
if (result.status === 'invalid' || result.is_disposable) {
throw new Error('Please enter a valid business email address.');
}
Express.js Middleware Example
// Middleware: validate email before processing signup
async function validateEmailMiddleware(req, res, next) {
const { email } = req.body;
if (!email) return res.status(400).json({ error: 'Email is required' });
try {
const result = await verifyEmail(email);
if (result.status === 'invalid') {
return res.status(422).json({
error: 'Invalid email address',
field: 'email'
});
}
if (result.is_disposable) {
return res.status(422).json({
error: 'Disposable email addresses are not allowed',
field: 'email'
});
}
// Attach result to request for downstream use
req.emailValidation = result;
next();
} catch (err) {
// On API error, allow through (fail open) — log for review
console.error('Email validation failed:', err.message);
next();
}
}
// Apply to signup route
app.post('/signup', validateEmailMiddleware, signupHandler);
Python
import os
import requests
API_KEY = os.environ["EMAILWIPES_API_KEY"]
BASE_URL = "https://api.emails-wipes.com/v1"
def verify_email(email: str) -> dict:
"""Verify a single email address. Returns validation result dict."""
response = requests.post(
f"{BASE_URL}/verify",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
json={"email": email},
timeout=10 # Always set a timeout in production
)
response.raise_for_status()
return response.json()
def is_safe_to_send(email: str) -> bool:
"""Returns True if the email is valid and safe to send to."""
try:
result = verify_email(email)
return (
result["status"] == "valid"
and not result["is_disposable"]
and not result["is_role_based"]
and result["score"] > 0.7
)
except requests.RequestException as e:
print(f"Validation API error: {e}")
return True # Fail open — don't block signups on API outage
# Django / Flask form validation example
from django.core.exceptions import ValidationError
def validate_email_field(value):
result = verify_email(value)
if result["status"] == "invalid":
raise ValidationError("This email address appears to be invalid.")
if result["is_disposable"]:
raise ValidationError("Please use a permanent email address.")
PHP
<?php
class EmailWipesClient {
private string $apiKey;
private string $baseUrl = 'https://api.emails-wipes.com/v1';
public function __construct(string $apiKey) {
$this->apiKey = $apiKey;
}
public function verify(string $email): array {
$ch = curl_init("{$this->baseUrl}/verify");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['email' => $email]),
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json',
'Accept: application/json'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new RuntimeException("API returned HTTP {$httpCode}");
}
return json_decode($response, true);
}
public function isSafeToSend(string $email): bool {
try {
$result = $this->verify($email);
return $result['status'] === 'valid'
&& !$result['is_disposable']
&& $result['score'] > 0.7;
} catch (RuntimeException $e) {
error_log("Email validation error: " . $e->getMessage());
return true; // Fail open on API errors
}
}
}
// Usage
$client = new EmailWipesClient($_ENV['EMAILWIPES_API_KEY']);
$result = $client->verify('[email protected]');
if ($result['status'] === 'invalid') {
// Return validation error to form
$errors['email'] = 'Please enter a valid email address.';
}
Webhook Setup
Webhooks are ideal for async bulk validation jobs and for triggering downstream actions (CRM updates, suppression list updates, re-engagement queues) without polling. Configure your webhook endpoint in the API dashboard.
When a bulk job completes, Email Wipes sends a POST request to your endpoint with this payload:
{
"event": "bulk_job.completed",
"job_id": "job_abc123",
"total": 15000,
"valid": 12480,
"invalid": 1830,
"risky": 690,
"download_url": "https://api.emails-wipes.com/v1/bulk/job_abc123/results",
"created_at": "2026-02-19T14:32:00Z",
"completed_at": "2026-02-19T14:34:12Z"
}
Verify webhook authenticity using the X-EmailWipes-Signature header (HMAC-SHA256 of the raw request body using your webhook secret):
import crypto from 'crypto';
function verifyWebhookSignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// Express webhook handler
app.post('/webhooks/emailwipes', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-emailwipes-signature'];
if (!verifyWebhookSignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
if (event.event === 'bulk_job.completed') {
// Trigger your downstream processing
processValidationResults(event.job_id, event.download_url);
}
res.sendStatus(200); // Acknowledge immediately
});
Best practice: Always acknowledge webhooks with a 200 immediately, then process asynchronously. If processing takes more than a few seconds, push to a queue (SQS, BullMQ, etc.) and return 200 before the work starts.
ESP Integrations
Mailchimp
Use the Mailchimp Merge Tag approach: add a hidden EMAIL_SCORE field to your signup form, populate it via the Email Wipes API before form submission (via a small client-side call or a server-side form processor), then use Mailchimp segments to exclude contacts with low scores. For bulk cleaning: export your Mailchimp audience as CSV → run through the bulk verifier → re-import with an EMAILWIPES_STATUS tag → create a segment where that tag = "invalid" → archive the segment. Never delete — archive for compliance.
HubSpot
HubSpot's native forms don't support real-time external validation, but you can work around this via HubSpot Workflows + the Email Wipes API. Trigger: "Contact is created." Action: call the Email Wipes /verify endpoint via a webhook action in HubSpot. Then use a property setter action to write the result to a custom contact property (Email Validation Status). Use this property in active lists to suppress invalid contacts from enrollment in nurture sequences.
ActiveCampaign
ActiveCampaign supports webhook actions in automations. Build an automation: trigger = "Contact subscribes to list" → action = "Webhook" → POST to your Email Wipes proxy endpoint → your server calls the API and writes back the status via the ActiveCampaign contacts API. Alternatively, use the ActiveCampaign contact.add webhook in reverse: receive the contact data, validate externally, and update the contact record with a custom field before they enter any campaign automation.
Klaviyo
The cleanest Klaviyo integration uses Klaviyo's flow trigger webhook. When a profile is added to a list, Klaviyo can trigger a flow that POSTs to your endpoint. Validate the email, then call the Klaviyo API to suppress invalid profiles (PUT /v2/list/{list_id}/suppress). For bulk cleaning of existing Klaviyo lists, see our dedicated Klaviyo list cleaning guide.
SendGrid
SendGrid's Event Webhook (bounces, spam reports) should feed directly into your Email Wipes suppression workflow. Set up a relay: SendGrid bounce event → your endpoint → add to Email Wipes bulk suppression list → sync back to SendGrid suppression group. For real-time validation, add a server-side call to /verify in your SendGrid contact ingestion pipeline before calling PUT /v3/marketing/contacts.
Zapier No-Code Workflow
For teams without dedicated engineering resources, Zapier provides a no-code path to real-time email validation in your lead capture stack:
- Trigger: "New row in Google Sheets" (or Typeform, Gravity Forms, Webflow form — any lead source that Zapier supports)
- Action 1 — Webhooks by Zapier: POST to https://api.emails-wipes.com/v1/verify with the email address from step 1. Set the Authorization header to Bearer YOUR_API_KEY.
- Filter: "Continue if" — status equals "valid" AND is_disposable equals "false"
- Action 2 — Mailchimp / HubSpot / ActiveCampaign: Add the validated contact to your ESP list, passing the validation score as a custom field.
- Action 3 (optional) — Google Sheets: Update the original row with the validation status for your records.
This workflow validates every new lead at the moment of collection, ensures only valid addresses enter your ESP, and costs a fraction of a cent per lead in API credits. The Zapier free plan handles up to 100 tasks per month; paid plans scale to millions.
Error Handling Best Practices
The Email Wipes API uses standard HTTP status codes. Here's how to handle each category correctly:
| Status Code | Meaning | Recommended Action |
|---|---|---|
| 200 OK | Successful verification | Process the result normally |
| 400 Bad Request | Malformed request or invalid input | Fix request format; log for debugging |
| 401 Unauthorized | Missing or invalid API key | Check key; rotate if compromised |
| 402 Payment Required | Credit balance exhausted | Alert on-call; top up credits; fail open temporarily |
| 422 Unprocessable | Email syntax invalid (pre-SMTP) | Return validation error to user immediately |
| 429 Too Many Requests | Rate limit exceeded | Implement exponential backoff (see below) |
| 5xx Server Error | API service issue | Retry with backoff; fail open after 3 attempts |
Fail open vs. fail closed: For signup forms, the default best practice is to fail open on API errors — allow the email through rather than blocking a potentially valid user because of a transient API issue. Log every failure for later batch re-verification. For high-risk contexts (preventing fraud, pre-send list cleaning), consider fail closed with a user-facing message: "We're having trouble verifying your email right now. Please try again in a moment."
// Exponential backoff with jitter
async function verifyWithRetry(email, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await verifyEmail(email);
} catch (err) {
const isRetryable = err.status === 429 || err.status >= 500;
if (!isRetryable || attempt === maxRetries) {
// Fail open: log and return a "pass" result
console.error(`Validation failed after ${attempt} retries:`, err);
return { status: 'unknown', score: 0.5, _failedOpen: true };
}
// Exponential backoff: 100ms, 200ms, 400ms + random jitter
const delay = Math.pow(2, attempt) * 100 + Math.random() * 50;
await new Promise(r => setTimeout(r, delay));
}
}
}
Rate Limiting Guide
Email Wipes API rate limits vary by plan. The standard limits are:
| Plan | Single Verify | Bulk Jobs | Concurrent Bulk |
|---|---|---|---|
| Free | 100/min | 1,000/job | 1 |
| Starter | 300/min | 100,000/job | 3 |
| Growth | 1,000/min | 1,000,000/job | 10 |
| Enterprise | Custom | Unlimited | Custom |
For high-throughput integrations, use bulk verification instead of individual /verify calls whenever possible. Batching 10,000 emails into a single bulk job is always more efficient than 10,000 individual API calls — both for rate limit management and for cost.
For real-time signup validation at scale (>500 signups/minute), consider a local caching layer: cache validation results for 24 hours by email address. Most email domains don't change SMTP configuration within a 24-hour window, and caching eliminates redundant API calls for repeat submissions.
Production tip: Monitor your X-RateLimit-Remaining response header. When it drops below 10% of your limit, trigger a backpressure mechanism — queue requests and process them with a slight delay rather than hitting 429 errors.
For complete endpoint documentation, request/response schemas, and SDK downloads in Go, Ruby, and Java, visit the full API documentation or follow the API quick start guide to make your first call in under 2 minutes.
Start Integrating in Minutes
Get your free API key and 100 free verifications. No credit card required.
Get Your Free API Key →