Skip to main content

Batch Email Verification

Verify multiple email addresses asynchronously.

Overview​

Batch verification is a 3-step process:

  1. Upload - Submit emails for verification
  2. Poll - Check job status until complete
  3. Download - Retrieve results

Step 1: Upload Emails​

Endpoint​

POST /api/v1/bulk/upload

Request​

{
"emails": [
"user1@example.com",
"user2@example.com",
"user3@example.com"
]
}
FieldTypeRequiredDescription
emailsarrayYesArray of email addresses (max 10,000)

Response​

{
"success": true,
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"totalEmails": 3,
"duplicateCount": 0,
"skippedRows": 0,
"emptyRows": 0,
"invalidRows": 0,
"estimatedTimeSeconds": 6
}

Duplicate addresses are removed automatically — you are only charged for unique emails. Verification starts as soon as the upload is accepted.

Code Example​

const response = await fetch('https://api.clearbounce.net/api/v1/bulk/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.CLEARBOUNCE_API_KEY
},
body: JSON.stringify({
emails: [
'user1@example.com',
'user2@example.com',
'user3@example.com'
]
})
});

const { jobId } = await response.json();
console.log('Job ID:', jobId);

Step 2: Check Job Status​

Endpoint​

GET /api/v1/bulk/status/:jobId

Response​

{
"success": true,
"job": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"progress": 45,
"totalEmails": 1000,
"processedEmails": 450,
"deliverableCount": 380,
"undeliverableCount": 45,
"riskyCount": 20,
"unknownCount": 5,
"catchAllCount": 12,
"createdAt": "2026-02-03T20:00:00.000Z",
"completedAt": null,
"bounceEstimate": null
}
}

Job Status Values​

StatusDescription
pendingJob is queued
processingVerification in progress
completedAll emails verified
failedJob failed (see error)

Polling Example​

async function waitForCompletion(jobId) {
while (true) {
const response = await fetch(
`https://api.clearbounce.net/api/v1/bulk/status/${jobId}`,
{
headers: { 'X-API-Key': process.env.CLEARBOUNCE_API_KEY }
}
);

const { job } = await response.json();

console.log(`Progress: ${job.progress}%`);

if (job.status === 'completed') {
return job;
}

if (job.status === 'failed') {
throw new Error('Job failed');
}

// Wait 5 seconds before polling again
await new Promise(resolve => setTimeout(resolve, 5000));
}
}

Step 3: Download Results​

Endpoint​

GET /api/v1/bulk/results/:jobId

Results are available once the job status is completed (a 400 JOB_NOT_COMPLETED error is returned before that).

Response​

{
"success": true,
"results": [
{
"email": "user1@example.com",
"status": "deliverable",
"subStatus": "accepted_email",
"reason": "accepted_email",
"score": 95,
"isDeliverable": true,
"toxicity": 0,
"meta": {
"provider": "google.com",
"dnsType": "MX"
}
},
{
"email": "user2@example.com",
"status": "undeliverable",
"subStatus": "rejected_email",
"reason": "rejected_email",
"score": 0,
"isDeliverable": false,
"toxicity": 0,
"meta": {
"provider": null,
"dnsType": null
}
},
{
"email": "user3@example.com",
"status": "risky",
"subStatus": "low_quality",
"reason": "low_quality",
"score": 45,
"isDeliverable": false,
"toxicity": 2,
"meta": {
"provider": "example.com",
"dnsType": "MX"
}
}
],
"originalCsvData": null
}

Results come back in the same order as the uploaded list. Each entry is a full verification result object (same shape as the single verification endpoint). To filter by status or export CSV, process the JSON on your side — for example results.filter(r => r.status === 'deliverable').


Complete Example​

async function bulkVerify(emails) {
const apiKey = process.env.CLEARBOUNCE_API_KEY;
const baseUrl = 'https://api.clearbounce.net/api/v1';

// Step 1: Upload
console.log(`Uploading ${emails.length} emails...`);
const uploadResponse = await fetch(`${baseUrl}/bulk/upload`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({ emails })
});

const { jobId } = await uploadResponse.json();
console.log(`Job created: ${jobId}`);

// Step 2: Poll for completion
let status = 'pending';
while (status !== 'completed' && status !== 'failed') {
await new Promise(resolve => setTimeout(resolve, 5000));

const statusResponse = await fetch(`${baseUrl}/bulk/status/${jobId}`, {
headers: { 'X-API-Key': apiKey }
});

const { job } = await statusResponse.json();
status = job.status;
console.log(`Status: ${status}, Progress: ${job.progress}%`);
}

if (status === 'failed') {
throw new Error('Bulk verification failed');
}

// Step 3: Download results
const resultsResponse = await fetch(`${baseUrl}/bulk/results/${jobId}`, {
headers: { 'X-API-Key': apiKey }
});

const { results } = await resultsResponse.json();

// Summarize results
const summary = {
total: results.length,
deliverable: results.filter(r => r.status === 'deliverable').length,
undeliverable: results.filter(r => r.status === 'undeliverable').length,
risky: results.filter(r => r.status === 'risky').length,
unknown: results.filter(r => r.status === 'unknown').length
};

console.log('Summary:', summary);
return results;
}

// Usage
const emails = [
'user1@example.com',
'user2@example.com',
// ... more emails
];

bulkVerify(emails)
.then(results => console.log('Done!', results.length, 'emails verified'))
.catch(err => console.error('Error:', err));

Limits​

LimitValue
Max emails per request10,000
Max concurrent jobs5
Job retention7 days

Credits​

Batch verification consumes 1 credit per email, same as single verification.