Validation Jobs
Validation jobs allow you to validate large batches of email addresses asynchronously. Instead of waiting for immediate results, you can upload a file or provide a list of emails, and the validation will be processed in the background. This is ideal for validating thousands or millions of email addresses.
When to Use Validation Jobs
Use validation jobs when:
- Validating more than 100 emails at once
- Processing large CSV or Excel files
- You don’t need immediate results
- You want to download filtered results (e.g., only valid emails)
Use the direct validation endpoint when:
- Validating 100 or fewer emails
- You need immediate, synchronous results
- Integrating real-time validation into forms or applications
Create Validation Job
Create a new validation job to process a batch of email addresses asynchronously.
Endpoint
POST https://api.campaignkit.cc/v1/email/validate/jobAuthentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEYRequest
This endpoint accepts multipart/form-data for file uploads.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
label | string | No | A descriptive label for this job (e.g., “Q1 Marketing List”) |
source | string | Yes | Source type: file, input, or excel |
file | binary | Conditional | File to upload (required if source is file or excel) |
input | string | Conditional | Comma-separated or newline-separated emails (required if source is input) |
integration | integer | No | Integration ID if importing from a connected service |
Example Request
Upload a CSV File
curl -X POST https://api.campaignkit.cc/v1/email/validate/job \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "label=Newsletter Subscribers" \
-F "source=file" \
-F "file=@emails.csv"Provide Emails Directly
curl -X POST https://api.campaignkit.cc/v1/email/validate/job \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "label=Quick Validation" \
-F "source=input" \
-F "input=user1@example.com,user2@example.com,user3@example.com"Supported file formats: CSV (.csv), Excel (.xlsx, .xls). Files should contain email addresses in the first column or have a column header named “email”.
Response
201 CreatedThe API returns the ID of the created job:
{
"id": 123
}The job will begin processing in the background. Use the returned job ID with the Get Job or Get Job Results endpoints to check the status and retrieve results.
Code Examples
JavaScript (Node.js)
const FormData = require('form-data');
const fs = require('fs');
const fetch = require('node-fetch');
async function createValidationJob(filePath, label) {
const form = new FormData();
form.append('label', label);
form.append('source', 'file');
form.append('file', fs.createReadStream(filePath));
const response = await fetch('https://api.campaignkit.cc/v1/email/validate/job', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
},
body: form
});
if (response.ok) {
const data = await response.json();
console.log(`Job created successfully with ID: ${data.id}`);
return data.id;
} else {
console.error('Failed to create job:', response.statusText);
return null;
}
}
// Usage
createValidationJob('./emails.csv', 'Marketing List 2024')
.then(jobId => {
if (jobId) {
console.log(`Monitor job at: /v1/email/validate/job/${jobId}`);
}
});Python
import requests
def create_validation_job(file_path, label):
url = 'https://api.campaignkit.cc/v1/email/validate/job'
headers = {
'Authorization': 'Bearer YOUR_API_KEY'
}
files = {
'file': open(file_path, 'rb')
}
data = {
'label': label,
'source': 'file'
}
response = requests.post(url, headers=headers, files=files, data=data)
if response.status_code == 201:
job_id = response.json()['id']
print(f'Job created successfully with ID: {job_id}')
return job_id
else:
print(f'Failed to create job: {response.status_code}')
return None
# Usage
job_id = create_validation_job('emails.csv', 'Marketing List 2024')
if job_id:
print(f'Monitor job at: /v1/email/validate/job/{job_id}')PHP
<?php
function createValidationJob($filePath, $label) {
$url = 'https://api.campaignkit.cc/v1/email/validate/job';
$cfile = new CURLFile($filePath);
$data = [
'label' => $label,
'source' => 'file',
'file' => $cfile
];
$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, [
'Authorization: Bearer YOUR_API_KEY'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 201) {
$data = json_decode($response, true);
$jobId = $data['id'];
echo "Job created successfully with ID: $jobId\n";
return $jobId;
} else {
echo "Failed to create job: $httpCode\n";
return null;
}
}
// Usage
$jobId = createValidationJob('emails.csv', 'Marketing List 2024');
if ($jobId) {
echo "Monitor job at: /v1/email/validate/job/$jobId\n";
}
?>List Validation Jobs
Get a list of all your validation jobs, including their current status and summary statistics.
Endpoint
GET https://api.campaignkit.cc/v1/email/validate/jobAuthentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEYExample Request
curl -X GET https://api.campaignkit.cc/v1/email/validate/job \
-H "Authorization: Bearer YOUR_API_KEY"Response
[
{
"id": 123,
"label": "Newsletter Subscribers",
"state": "done",
"emailCount": 5000,
"source": "file",
"integration": null,
"summary": {
"totalEmails": 5000,
"deliverableCount": 4250,
"undeliverableCount": 500,
"riskyCount": 250
},
"createdAt": "2024-01-15T10:30:00Z",
"finishedAt": "2024-01-15T11:45:00Z"
},
{
"id": 124,
"label": "Q1 Marketing List",
"state": "running",
"emailCount": 10000,
"source": "excel",
"integration": null,
"summary": {
"totalEmails": 10000,
"deliverableCount": 3200,
"undeliverableCount": 150,
"riskyCount": 80
},
"createdAt": "2024-01-15T14:20:00Z",
"finishedAt": null
}
]Response Fields
Each job object contains:
| Field | Type | Description |
|---|---|---|
id | integer | Unique job identifier |
label | string | Job label/description |
state | string | Current state: pending, running, paused, failed, or done |
emailCount | integer | Total number of emails in this job |
source | string | Source type: file, input, hubspot, mailjet, mailchimp, excel, or api |
integration | object | Integration details if job was created from a connected service |
summary | object | Validation summary statistics (see below) |
createdAt | string | ISO 8601 timestamp when job was created |
finishedAt | string | ISO 8601 timestamp when job completed (null if still running) |
Summary Object
| Field | Type | Description |
|---|---|---|
totalEmails | integer | Total emails processed so far |
deliverableCount | integer | Count of valid/deliverable emails |
undeliverableCount | integer | Count of invalid/undeliverable emails |
riskyCount | integer | Count of risky emails (role-based, catch-all, etc.) |
Job States
| State | Description |
|---|---|
pending | Job is queued and waiting to start |
running | Job is currently being processed |
paused | Job has been paused by the user |
failed | Job encountered an error and could not complete |
done | Job completed successfully |
Jobs in the running state will have their summary object updated in real-time as emails are processed. Poll this endpoint to track progress.
Code Examples
JavaScript (Node.js)
const fetch = require('node-fetch');
async function listJobs() {
const response = await fetch('https://api.campaignkit.cc/v1/email/validate/job', {
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
const jobs = await response.json();
jobs.forEach(job => {
console.log(`Job ${job.id}: ${job.label}`);
console.log(` State: ${job.state}`);
console.log(` Progress: ${job.summary.totalEmails}/${job.emailCount} emails`);
if (job.state === 'done') {
console.log(` Deliverable: ${job.summary.deliverableCount}`);
console.log(` Risky: ${job.summary.riskyCount}`);
console.log(` Undeliverable: ${job.summary.undeliverableCount}`);
}
console.log('');
});
}
listJobs();Python
import requests
def list_jobs():
url = 'https://api.campaignkit.cc/v1/email/validate/job'
headers = {
'Authorization': 'Bearer YOUR_API_KEY'
}
response = requests.get(url, headers=headers)
jobs = response.json()
for job in jobs:
print(f"Job {job['id']}: {job['label']}")
print(f" State: {job['state']}")
print(f" Progress: {job['summary']['totalEmails']}/{job['emailCount']} emails")
if job['state'] == 'done':
print(f" Deliverable: {job['summary']['deliverableCount']}")
print(f" Risky: {job['summary']['riskyCount']}")
print(f" Undeliverable: {job['summary']['undeliverableCount']}")
print()
list_jobs()PHP
<?php
function listJobs() {
$url = 'https://api.campaignkit.cc/v1/email/validate/job';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer YOUR_API_KEY'
]);
$response = curl_exec($ch);
curl_close($ch);
$jobs = json_decode($response, true);
foreach ($jobs as $job) {
echo "Job {$job['id']}: {$job['label']}\n";
echo " State: {$job['state']}\n";
echo " Progress: {$job['summary']['totalEmails']}/{$job['emailCount']} emails\n";
if ($job['state'] == 'done') {
echo " Deliverable: {$job['summary']['deliverableCount']}\n";
echo " Risky: {$job['summary']['riskyCount']}\n";
echo " Undeliverable: {$job['summary']['undeliverableCount']}\n";
}
echo "\n";
}
}
listJobs();
?>Get Single Job
Retrieve details for a specific validation job by its ID.
Endpoint
GET https://api.campaignkit.cc/v1/email/validate/job/{id}Authentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEYParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Job ID (in URL path) |
Example Request
curl -X GET https://api.campaignkit.cc/v1/email/validate/job/123 \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"id": 123,
"label": "Newsletter Subscribers",
"state": "done",
"emailCount": 5000,
"source": "file",
"integration": null,
"summary": {
"totalEmails": 5000,
"deliverableCount": 4250,
"undeliverableCount": 500,
"riskyCount": 250
},
"createdAt": "2024-01-15T10:30:00Z",
"finishedAt": "2024-01-15T11:45:00Z"
}Response Fields
See the List Validation Jobs section for detailed field descriptions.
Code Examples
JavaScript (Node.js)
const fetch = require('node-fetch');
async function getJob(jobId) {
const response = await fetch(`https://api.campaignkit.cc/v1/email/validate/job/${jobId}`, {
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
if (!response.ok) {
if (response.status === 404) {
console.error('Job not found');
return null;
}
throw new Error(`HTTP error ${response.status}`);
}
const job = await response.json();
console.log(`Job ${job.id}: ${job.label}`);
console.log(`State: ${job.state}`);
if (job.state === 'done') {
console.log(`Results: ${job.summary.deliverableCount} valid, ${job.summary.riskyCount} risky, ${job.summary.undeliverableCount} invalid`);
} else if (job.state === 'running') {
console.log(`Progress: ${job.summary.totalEmails}/${job.emailCount} emails processed`);
}
return job;
}
// Usage
getJob(123);Python
import requests
def get_job(job_id):
url = f'https://api.campaignkit.cc/v1/email/validate/job/{job_id}'
headers = {
'Authorization': 'Bearer YOUR_API_KEY'
}
response = requests.get(url, headers=headers)
if response.status_code == 404:
print('Job not found')
return None
response.raise_for_status()
job = response.json()
print(f"Job {job['id']}: {job['label']}")
print(f"State: {job['state']}")
if job['state'] == 'done':
summary = job['summary']
print(f"Results: {summary['deliverableCount']} valid, {summary['riskyCount']} risky, {summary['undeliverableCount']} invalid")
elif job['state'] == 'running':
print(f"Progress: {job['summary']['totalEmails']}/{job['emailCount']} emails processed")
return job
# Usage
get_job(123)PHP
<?php
function getJob($jobId) {
$url = "https://api.campaignkit.cc/v1/email/validate/job/$jobId";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer YOUR_API_KEY'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 404) {
echo "Job not found\n";
return null;
}
if ($httpCode != 200) {
echo "HTTP error: $httpCode\n";
return null;
}
$job = json_decode($response, true);
echo "Job {$job['id']}: {$job['label']}\n";
echo "State: {$job['state']}\n";
if ($job['state'] == 'done') {
$summary = $job['summary'];
echo "Results: {$summary['deliverableCount']} valid, {$summary['riskyCount']} risky, {$summary['undeliverableCount']} invalid\n";
} elseif ($job['state'] == 'running') {
echo "Progress: {$job['summary']['totalEmails']}/{$job['emailCount']} emails processed\n";
}
return $job;
}
// Usage
getJob(123);
?>Get Job Results
Retrieve validation results for a specific job. Results are returned in pages for easy consumption.
Endpoint
GET https://api.campaignkit.cc/v1/email/validate/job/{id}/resultAuthentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEYParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Job ID (in URL path) |
pos | integer | No | Starting position for pagination (default: 0) |
Example Request
# Get first page of results
curl -X GET https://api.campaignkit.cc/v1/email/validate/job/123/result \
-H "Authorization: Bearer YOUR_API_KEY"
# Get results starting from position 1000
curl -X GET https://api.campaignkit.cc/v1/email/validate/job/123/result?pos=1000 \
-H "Authorization: Bearer YOUR_API_KEY"Response
[
{
"email": "valid@example.com",
"result": {
"syntax": "pass",
"mx": "pass",
"mailbox": "pass",
"score": 10,
"classifier": "valid",
"description": [],
"smtpResponse": "250 OK"
}
},
{
"email": "invalid@fake-domain.xyz",
"result": {
"syntax": "pass",
"mx": "fail",
"mailbox": "n/a",
"score": 0,
"classifier": "invalid",
"description": ["invalid_mx"],
"smtpResponse": ""
}
},
{
"email": "admin@company.com",
"result": {
"syntax": "pass",
"mx": "pass",
"mailbox": "n/a",
"score": 5,
"classifier": "risky",
"description": ["role_email"],
"smtpResponse": ""
}
}
]Results are returned in batches of up to 1000 emails per request. Use the pos parameter to paginate through large result sets.
Response Fields
Each result object contains:
| Field | Type | Description |
|---|---|---|
email | string | The email address that was validated |
result | object | Validation result details |
For detailed information about the validation result object, see the Email Validation documentation.
Code Examples
JavaScript (Node.js)
const fetch = require('node-fetch');
async function getJobResults(jobId, position = 0) {
const url = `https://api.campaignkit.cc/v1/email/validate/job/${jobId}/result?pos=${position}`;
const response = await fetch(url, {
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});
const results = await response.json();
// Filter results by classifier
const valid = results.filter(r => r.result.classifier === 'valid');
const risky = results.filter(r => r.result.classifier === 'risky');
const invalid = results.filter(r => r.result.classifier === 'invalid');
console.log(`Valid: ${valid.length}, Risky: ${risky.length}, Invalid: ${invalid.length}`);
return results;
}
// Get all results by paginating
async function getAllJobResults(jobId) {
let position = 0;
let allResults = [];
while (true) {
const results = await getJobResults(jobId, position);
if (results.length === 0) break;
allResults.push(...results);
position += results.length;
console.log(`Fetched ${allResults.length} results so far...`);
}
return allResults;
}
// Usage
getAllJobResults(123).then(results => {
console.log(`Total results: ${results.length}`);
});Python
import requests
def get_job_results(job_id, position=0):
url = f'https://api.campaignkit.cc/v1/email/validate/job/{job_id}/result'
params = {'pos': position}
headers = {
'Authorization': 'Bearer YOUR_API_KEY'
}
response = requests.get(url, headers=headers, params=params)
results = response.json()
# Filter results by classifier
valid = [r for r in results if r['result']['classifier'] == 'valid']
risky = [r for r in results if r['result']['classifier'] == 'risky']
invalid = [r for r in results if r['result']['classifier'] == 'invalid']
print(f"Valid: {len(valid)}, Risky: {len(risky)}, Invalid: {len(invalid)}")
return results
def get_all_job_results(job_id):
position = 0
all_results = []
while True:
results = get_job_results(job_id, position)
if not results:
break
all_results.extend(results)
position += len(results)
print(f"Fetched {len(all_results)} results so far...")
return all_results
# Usage
results = get_all_job_results(123)
print(f"Total results: {len(results)}")PHP
<?php
function getJobResults($jobId, $position = 0) {
$url = "https://api.campaignkit.cc/v1/email/validate/job/$jobId/result?pos=$position";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer YOUR_API_KEY'
]);
$response = curl_exec($ch);
curl_close($ch);
$results = json_decode($response, true);
// Filter results by classifier
$valid = array_filter($results, fn($r) => $r['result']['classifier'] === 'valid');
$risky = array_filter($results, fn($r) => $r['result']['classifier'] === 'risky');
$invalid = array_filter($results, fn($r) => $r['result']['classifier'] === 'invalid');
echo "Valid: " . count($valid) . ", Risky: " . count($risky) . ", Invalid: " . count($invalid) . "\n";
return $results;
}
function getAllJobResults($jobId) {
$position = 0;
$allResults = [];
while (true) {
$results = getJobResults($jobId, $position);
if (empty($results)) break;
$allResults = array_merge($allResults, $results);
$position += count($results);
echo "Fetched " . count($allResults) . " results so far...\n";
}
return $allResults;
}
// Usage
$results = getAllJobResults(123);
echo "Total results: " . count($results) . "\n";
?>Download Job Results
Download validation results as a CSV file, optionally filtered by validation status.
Endpoint
GET https://api.campaignkit.cc/v1/email/validate/job/{id}/downloadAuthentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEYParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Job ID (in URL path) |
filters | array | No | Filter results by classifier: valid, risky, invalid |
Example Request
# Download all results
curl -X GET https://api.campaignkit.cc/v1/email/validate/job/123/download \
-H "Authorization: Bearer YOUR_API_KEY" \
-o results.csv
# Download only valid emails
curl -X GET "https://api.campaignkit.cc/v1/email/validate/job/123/download?filters=valid" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o valid-emails.csv
# Download valid and risky emails
curl -X GET "https://api.campaignkit.cc/v1/email/validate/job/123/download?filters=valid&filters=risky" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o deliverable-emails.csvResponse
The response is a CSV file with the following columns:
email,syntax,mx,mailbox,score,classifier,description,smtpResponse
valid@example.com,pass,pass,pass,10,valid,,250 OK
invalid@fake-domain.xyz,pass,fail,n/a,0,invalid,invalid_mx,
admin@company.com,pass,pass,n/a,5,risky,role_email,The download endpoint streams the entire result set. For very large jobs (millions of emails), consider using the paginated Get Job Results endpoint instead.
Delete Validation Job
Delete a validation job and all its associated results.
Endpoint
DELETE https://api.campaignkit.cc/v1/email/validate/job/{id}Authentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEYParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Job ID to delete (in URL path) |
Example Request
curl -X DELETE https://api.campaignkit.cc/v1/email/validate/job/123 \
-H "Authorization: Bearer YOUR_API_KEY"Response
200 OKThe job and all its results have been deleted.
Deleting a job is permanent and cannot be undone. Make sure you’ve downloaded any results you need before deleting.
Error Responses
400 Bad Request
Invalid request parameters:
{
"error": "Invalid source type"
}401 Unauthorized
Invalid or missing API key:
{
"error": "Unauthorized"
}404 Not Found
Job not found:
{
"error": "Job not found"
}500 Internal Server Error
Server error during processing:
{
"error": "Internal server error"
}Best Practices
Polling for Job Completion
When creating a job, use the single job endpoint to poll for completion:
async function waitForJobCompletion(jobId, checkInterval = 5000) {
while (true) {
const response = await fetch(`https://api.campaignkit.cc/v1/email/validate/job/${jobId}`, {
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
});
if (!response.ok) {
if (response.status === 404) {
throw new Error('Job not found');
}
throw new Error(`HTTP error ${response.status}`);
}
const job = await response.json();
if (job.state === 'done') {
console.log('Job completed!');
console.log(`Deliverable: ${job.summary.deliverableCount}`);
console.log(`Risky: ${job.summary.riskyCount}`);
console.log(`Undeliverable: ${job.summary.undeliverableCount}`);
return job;
}
if (job.state === 'failed') {
throw new Error('Job failed');
}
console.log(`Progress: ${job.summary.totalEmails}/${job.emailCount} emails processed`);
await new Promise(resolve => setTimeout(resolve, checkInterval));
}
}Consider using Webhooks to receive automatic notifications when jobs complete instead of polling.
Efficient Result Pagination
When fetching large result sets, use pagination efficiently:
async function processJobResults(jobId, batchSize = 1000) {
let position = 0;
while (true) {
const results = await fetch(
`https://api.campaignkit.cc/v1/email/validate/job/${jobId}/result?pos=${position}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
).then(r => r.json());
if (results.length === 0) break;
// Process this batch
await processBatch(results);
position += results.length;
}
}Filtering Downloads
Use the filters parameter to download only the emails you need:
# Download only deliverable emails (valid + risky might be acceptable)
curl "https://api.campaignkit.cc/v1/email/validate/job/123/download?filters=valid&filters=risky" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o deliverable.csv
# Download only high-quality emails (valid only)
curl "https://api.campaignkit.cc/v1/email/validate/job/123/download?filters=valid" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o high-quality.csvFile Upload Best Practices
When uploading files:
- Ensure your CSV has email addresses in the first column or has an “email” column header
- Remove duplicates before uploading to save credits
- For Excel files, use the first sheet with emails in the first column
- Maximum file size may vary based on your plan
Job vs. Direct Validation
Choose the right endpoint for your use case:
| Use Case | Recommended Endpoint |
|---|---|
| Real-time form validation | Direct validation (/email/validate) |
| Batch of 1-100 emails | Direct validation (/email/validate) |
| Batch of 100+ emails | Validation job (/email/validate/job) |
| File upload (CSV/Excel) | Validation job (/email/validate/job) |
| Need to download filtered results | Validation job with download |
| Integration with third-party platform | Validation job (check for integration support) |
Need Help?
If you encounter any issues or have questions about validation jobs:
- Check the API Overview for authentication and general information
- Review Email Validation documentation for result field details
- Set up Webhooks for automatic job completion notifications
- Contact support at support@campaignkit.cc