Black Friday: 50% OFF with code BF2025!Sign Up

Email Validation

The Email Validation endpoint allows you to verify the deliverability and quality of email addresses. Use this endpoint to validate single or multiple emails in one request.

Endpoint

POST https://api.campaignkit.cc/v1/email/validate

Authentication

Include your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Request

Request Body

Send a JSON object with an array of email addresses to validate:

ParameterTypeRequiredDescription
emailsarrayYesArray of email addresses to validate (minimum 1 email)

Example Request

curl -X POST https://api.campaignkit.cc/v1/email/validate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "emails": [
      "valid@example.com",
      "invalid@fake-domain.xyz",
      "admin@company.com"
    ]
  }'

You can validate up to 100 email addresses in a single API request. For larger batches, consider breaking them into multiple requests or using batch validation features.

Response

Response Format

The API returns a JSON object with a results array containing validation data for each email, and the total credits used:

{
  "creditsUsed": 3,
  "results": [
    {
      "email": "valid@example.com",
      "contextId": "ctx_abc123",
      "result": {
        "syntax": "pass",
        "mx": "pass",
        "mailbox": "pass",
        "score": 10,
        "classifier": "valid",
        "description": [],
        "smtpResponse": "250 OK"
      }
    },
    {
      "email": "invalid@fake-domain.xyz",
      "contextId": "ctx_def456",
      "result": {
        "syntax": "pass",
        "mx": "fail",
        "mailbox": "n/a",
        "score": 0,
        "classifier": "invalid",
        "description": ["invalid_mx"],
        "smtpResponse": ""
      }
    },
    {
      "email": "admin@company.com",
      "contextId": "ctx_ghi789",
      "result": {
        "syntax": "pass",
        "mx": "pass",
        "mailbox": "n/a",
        "score": 5,
        "classifier": "risky",
        "description": ["role_email"],
        "smtpResponse": ""
      }
    }
  ]
}

Response Fields

The top-level response contains:

FieldTypeDescription
creditsUsedintegerNumber of validation credits consumed by this request
resultsarrayArray of validation results for each email

Each item in the results array contains:

FieldTypeDescription
emailstringThe email address that was validated
contextIdstringUnique identifier for this validation result
resultobjectValidation result details

Validation Result Object

FieldTypeValuesDescription
syntaxstringpass, fail, n/aWhether the email syntax is valid
mxstringpass, fail, n/aWhether the domain has valid MX records
mailboxstringpass, fail, n/aWhether the mailbox exists (n/a if server doesn’t allow checking)
scoreinteger0-10Deliverability score (10 = best, 0 = will bounce)
classifierstringvalid, risky, invalidOverall classification of the email
descriptionarraystringsDetailed validation flags (see below)
smtpResponsestring-SMTP server response message (if available)

Score Interpretation

The score field provides a quick assessment of email quality:

  • 10: Email is valid and safe to send
  • 7-9: Email is likely valid but has minor issues
  • 4-6: Email is risky (role-based, catch-all, or disposable)
  • 1-3: Email is likely invalid or undeliverable
  • 0: Email will definitely bounce
⚠️

Emails with scores below 7 should be reviewed carefully. Consider excluding scores below 4 from your sending lists.

Classifier Field

The classifier field provides a simple, categorical assessment of email quality:

  • valid: Email is safe to send (typically score 7-10)
  • risky: Email may have issues but could be valid (typically score 4-6)
  • invalid: Email is undeliverable and will bounce (typically score 0-3)

Use classifier for quick filtering and routing decisions, and score for more nuanced quality assessment.

Description Flags

The description array contains detailed validation flags:

FlagMeaning
role_emailRole-based email (admin@, info@, support@, etc.)
disposableDisposable/temporary email address
catchallDomain accepts all emails (catch-all server)
blacklistEmail or domain is on a known blacklist
invalid_syntaxEmail format is invalid
invalid_mxDomain has no valid mail servers
mailbox_fullRecipient’s mailbox is full
mailbox_disabledMailbox exists but is disabled

Code Examples

JavaScript (Node.js)

const fetch = require('node-fetch');
 
async function validateEmails(emails) {
  const response = await fetch('https://api.campaignkit.cc/v1/email/validate', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ emails })
  });
 
  const data = await response.json();
  return data;
}
 
// Usage
validateEmails(['test@example.com'])
  .then(data => {
    console.log(`Credits used: ${data.creditsUsed}`);
 
    data.results.forEach(item => {
      console.log(`${item.email}: ${item.result.classifier} (Score: ${item.result.score}/10)`);
    });
  })
  .catch(error => console.error('Error:', error));

Python

import requests
 
def validate_emails(emails):
    url = 'https://api.campaignkit.cc/v1/email/validate'
    headers = {
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json'
    }
    data = {'emails': emails}
 
    response = requests.post(url, json=data, headers=headers)
    return response.json()
 
# Usage
data = validate_emails(['test@example.com'])
print(f"Credits used: {data['creditsUsed']}")
 
for item in data['results']:
    result = item['result']
    print(f"{item['email']}: {result['classifier']} (Score: {result['score']}/10)")

PHP

<?php
 
function validateEmails($emails) {
    $url = 'https://api.campaignkit.cc/v1/email/validate';
    $data = json_encode(['emails' => $emails]);
 
    $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',
        'Content-Type: application/json'
    ]);
 
    $response = curl_exec($ch);
    curl_close($ch);
 
    return json_decode($response, true);
}
 
// Usage
$data = validateEmails(['test@example.com']);
echo "Credits used: {$data['creditsUsed']}\n";
 
foreach ($data['results'] as $item) {
    $result = $item['result'];
    echo "{$item['email']}: {$result['classifier']} (Score: {$result['score']}/10)\n";
}
?>

Error Responses

401 Unauthorized

Invalid or missing API key:

{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key"
  }
}

400 Bad Request

Invalid request format:

{
  "error": {
    "code": "invalid_request",
    "message": "The 'emails' field is required and must be an array"
  }
}

429 Too Many Requests

Rate limit exceeded:

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Please wait before making more requests"
  }
}

Best Practices

Filter by Classifier

Use the classifier field for simple, reliable filtering:

const data = await validateEmails(emailList);
const results = data.results;
 
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}`);
console.log(`Total credits used: ${data.creditsUsed}`);

Filter by Score

For more granular control, use the score:

const results = data.results;
 
const highQuality = results.filter(r => r.result.score >= 8);
const acceptable = results.filter(r => r.result.score >= 6 && r.result.score < 8);
const questionable = results.filter(r => r.result.score >= 4 && r.result.score < 6);
const invalid = results.filter(r => r.result.score < 4);
 
console.log(`High: ${highQuality.length}, Acceptable: ${acceptable.length}, Questionable: ${questionable.length}, Invalid: ${invalid.length}`);

Check Specific Flags

Look for specific issues in the description array:

results.forEach(item => {
  const { email, result } = item;
 
  if (result.description.includes('disposable')) {
    console.log(`${email} is a disposable email address`);
  }
 
  if (result.description.includes('role_email')) {
    console.log(`${email} is a role-based email (may have low engagement)`);
  }
});

Handle Catch-All Domains

Catch-all domains accept all emails, making verification difficult:

const hasCatchAll = result.description.includes('catchall');
 
if (hasCatchAll && result.score < 7) {
  // Consider this email risky since we can't verify the mailbox
  console.log('Catch-all domain detected - mailbox verification unavailable');
}

Batch Processing

For large lists, process in batches of 100:

async function validateLargeList(emails) {
  const batchSize = 100;
  const allResults = [];
  let totalCreditsUsed = 0;
 
  for (let i = 0; i < emails.length; i += batchSize) {
    const batch = emails.slice(i, i + batchSize);
    const data = await validateEmails(batch);
 
    allResults.push(...data.results);
    totalCreditsUsed += data.creditsUsed;
 
    // Optional: Add delay to respect rate limits
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
 
  return {
    results: allResults,
    totalCreditsUsed
  };
}
 
// Usage
const { results, totalCreditsUsed } = await validateLargeList(largeEmailList);
console.log(`Validated ${results.length} emails using ${totalCreditsUsed} credits`);

Validation Credits

Each email validation consumes one credit from your account. The API response includes a creditsUsed field showing how many credits were consumed by the request.

const data = await validateEmails(['email1@example.com', 'email2@example.com']);
console.log(`Credits used: ${data.creditsUsed}`); // Output: Credits used: 2

You can also check your remaining credits in the CampaignKit dashboard.

Duplicate emails in the same request are deduplicated automatically and only consume one credit per unique email address. The creditsUsed field reflects the actual number of credits consumed after deduplication.

Need Help?

If you encounter any issues or have questions about email validation: