Skip to main content
KaijuVerifier
Home Bulk Cleaner Free Checker Pricing Insights Company Log in Get Started Dashboard

Email Verification API Docs

Integrate Kaiju's powerful verification engine directly into your applications.

✓ API Live — Self-serve keys available

Generate your API key in Dashboard → Settings and start verifying in seconds. Free tier: 500 verifications/month.

Get your API key

Machine-readable OpenAPI 3.0 spec — import into Postman or Insomnia, or generate a client SDK in your language.

Quick Start

Verify a single email with the authenticated POST /api/v1/verify-single endpoint. Pass your key as a Bearer token (you can also use an X-API-Key header or an ?api_key= query param — see Authentication):

cURL

curl -X POST https://kaijuverifier.com/api/v1/verify-single \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{"email":"user@example.com","use_smtp":true}'

JavaScript (fetch)

const r = await fetch('https://kaijuverifier.com/api/v1/verify-single', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_API_KEY'
  },
  body: JSON.stringify({ email: 'user@example.com', use_smtp: true })
});
const data = await r.json();
console.log(data.status, data.score, data.grade);

Python (requests)

import requests
r = requests.post(
    'https://kaijuverifier.com/api/v1/verify-single',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    json={'email': 'user@example.com', 'use_smtp': True}
)
print(r.json())

PHP (cURL)

$ch = curl_init('https://kaijuverifier.com/api/v1/verify-single');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Authorization: Bearer YOUR_API_KEY'
    ],
    CURLOPT_POSTFIELDS => json_encode(['email' => 'user@example.com', 'use_smtp' => true])
]);
$data = json_decode(curl_exec($ch), true);

No key yet? The POST /api/validate-email endpoint works without authentication for low-volume testing (IP rate-limited). For production, use the authenticated /api/v1/* endpoints below.

Client libraries

Generate a typed client in 50+ languages directly from our OpenAPI spec:

npx @openapitools/openapi-generator-cli generate \
  -i https://kaijuverifier.com/openapi.json -g python -o ./kaiju-client

Or drop in one of these minimal single-file clients:

Python

import requests

class KaijuVerifier:
    def __init__(self, api_key, base="https://kaijuverifier.com"):
        self.s = requests.Session()
        self.s.headers["Authorization"] = f"Bearer {api_key}"
        self.base = base
    def verify(self, email, use_smtp=True):
        return self.s.post(f"{self.base}/api/v1/verify-single",
                           json={"email": email, "use_smtp": use_smtp}).json()
    def verify_batch(self, emails, use_smtp=True):
        return self.s.post(f"{self.base}/api/v1/verify-batch",
                           json={"emails": emails, "use_smtp": use_smtp}).json()
    def submit_job(self, emails, use_smtp=True, dedupe=True):
        return self.s.post(f"{self.base}/api/v1/jobs",
                           json={"emails": emails, "use_smtp": use_smtp, "dedupe": dedupe}).json()
    def job_status(self, job_id):
        return self.s.get(f"{self.base}/api/v1/jobs/{job_id}").json()
    def job_results(self, job_id):
        return self.s.get(f"{self.base}/api/v1/jobs/{job_id}/results").json()
    def account(self):
        return self.s.get(f"{self.base}/api/v1/account").json()

kaiju = KaijuVerifier("YOUR_API_KEY")
print(kaiju.verify("user@example.com"))

Node.js

class KaijuVerifier {
  constructor(apiKey, base = "https://kaijuverifier.com") {
    this.h = { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" };
    this.base = base;
  }
  _post(p, body) { return fetch(this.base + p, { method: "POST", headers: this.h, body: JSON.stringify(body) }).then(r => r.json()); }
  _get(p) { return fetch(this.base + p, { headers: this.h }).then(r => r.json()); }
  verify(email, useSmtp = true) { return this._post("/api/v1/verify-single", { email, use_smtp: useSmtp }); }
  verifyBatch(emails, useSmtp = true) { return this._post("/api/v1/verify-batch", { emails, use_smtp: useSmtp }); }
  submitJob(emails, useSmtp = true, dedupe = true) { return this._post("/api/v1/jobs", { emails, use_smtp: useSmtp, dedupe }); }
  jobStatus(id) { return this._get(`/api/v1/jobs/${id}`); }
  jobResults(id) { return this._get(`/api/v1/jobs/${id}/results`); }
  account() { return this._get("/api/v1/account"); }
}

const kaiju = new KaijuVerifier("YOUR_API_KEY");
kaiju.verify("user@example.com").then(console.log);

PHP

class KaijuVerifier {
    public function __construct(private string $apiKey, private string $base = "https://kaijuverifier.com") {}
    private function req(string $method, string $path, ?array $body = null): array {
        $ch = curl_init($this->base . $path);
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST  => $method,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => ["Authorization: Bearer {$this->apiKey}", "Content-Type: application/json"],
        ]);
        if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        return json_decode(curl_exec($ch), true) ?? [];
    }
    public function verify($e, $smtp = true)        { return $this->req("POST", "/api/v1/verify-single", ["email" => $e, "use_smtp" => $smtp]); }
    public function verifyBatch($emails, $smtp = true){ return $this->req("POST", "/api/v1/verify-batch", ["emails" => $emails, "use_smtp" => $smtp]); }
    public function submitJob($emails, $smtp = true, $dedupe = true){ return $this->req("POST", "/api/v1/jobs", ["emails" => $emails, "use_smtp" => $smtp, "dedupe" => $dedupe]); }
    public function jobStatus($id)   { return $this->req("GET", "/api/v1/jobs/$id"); }
    public function jobResults($id)  { return $this->req("GET", "/api/v1/jobs/$id/results"); }
    public function account()        { return $this->req("GET", "/api/v1/account"); }
}

$kaiju = new KaijuVerifier("YOUR_API_KEY");
print_r($kaiju->verify("user@example.com"));

Response fields

FieldTypeDescription
emailstringOriginal email input.
normalizedstringLowercased + plus-addressing stripped for Gmail.
is_validboolSafe-to-send decision.
statusstringOne of: valid, invalid, risky, unknown.
reasonstringMachine-readable detail (e.g. user_unknown, catch_all, domain_no_mx, disposable).
confidenceint0–100 confidence in the decision.
scoreintDeliverability score 0–100.
gradestringLetter grade A–F based on score.
suggestionstring|nullTypo correction if detected (e.g. user@gmail.com for user@gmial.com).
checksobjectSub-checks: syntax, dns, smtp, catch_all, is_disposable, role_based, free_provider, mx{count,providers,grade}.
quotaobjectReturned by authenticated /api/v1/* endpoints: limit, used_this_month, remaining.

Status codes & rate limits

  • 200 — Verification completed.
  • 400 — Missing or malformed email field.
  • 401 — Invalid or missing API key.
  • 429 — Rate limit exceeded.

Rate limits: 20 req/min for anonymous IPs (50 SMTP probes/day). Authenticated keys inherit their plan quota (Free 500/mo, Starter 10k/mo, Growth 50k/mo, Scale 250k/mo).

API Overview

The Kaiju Email Verification API allows developers to integrate our industry-leading email cleaning capabilities directly into their applications, CRMs, or registration forms. Verify emails in real-time to prevent fake signups or process bulk lists programmatically.

Authentication

All /api/v1/* endpoints require authentication. You can generate and manage your API keys in Dashboard → Settings. We accept three equivalent methods — pick whichever fits your client:

Method Where Example
Bearer token HTTP header (recommended) Authorization: Bearer YOUR_API_KEY
API key header HTTP header X-API-Key: YOUR_API_KEY
Query parameter URL (legacy) ?api_key=YOUR_API_KEY
Security Note: Keep your API keys secure. Do not expose them in client-side code (browsers). Always route API requests through your own backend server. Prefer the header methods over the ?api_key= query param, which can leak into server/proxy logs.

Authorization Header (recommended)

Authorization: Bearer YOUR_API_KEY_HERE

Base URL

All API endpoints are served from the main domain. Use the full paths shown below:

https://kaijuverifier.com

Example: https://kaijuverifier.com/api/v1/verify-single

API Endpoints

POST /api/v1/verify-single

Verify a single email address in real-time. Ideal for registration forms and lead capture. Requires authentication; counts as 1 verification against your quota.

Parameters (JSON Body)

Parameter Type Required Description
email string Yes The email address to verify.
use_smtp boolean No Enable the live SMTP mailbox probe (default: false). Requires a plan with SMTP enabled.

Example Request

curl -X POST "https://kaijuverifier.com/api/v1/verify-single" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "use_smtp": true
  }'

Example Response

{
  "success": true,
  "email": "user@example.com",
  "is_valid": true,
  "status": "valid",
  "score": 92,
  "grade": "A",
  "reason": null,
  "suggestion": null,
  "checks": {
    "syntax": true,
    "dns": true,
    "smtp": true,
    "catch_all": false,
    "is_disposable": false,
    "role_based": false,
    "free_provider": false,
    "mx": { "count": 5, "providers": ["google"], "grade": "A" }
  },
  "quota": {
    "limit": 10000,
    "used_this_month": 12,
    "remaining": 9988
  }
}

POST /api/v1/verify-batch

Verify a batch of email addresses in a single request. Each email counts as one verification against your quota. Maximum 10,000 emails per request (your plan may set a lower per-batch cap).

Request Body

{
  "emails": [
    "user1@example.com",
    "user2@example.com",
    "invalid@domain.com"
  ],
  "use_smtp": true
}

Code Examples

curl -X POST "https://kaijuverifier.com/api/v1/verify-batch" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "emails": ["user1@example.com", "user2@example.com"],
    "use_smtp": true
  }'
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://kaijuverifier.com/api/v1/verify-batch");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    "emails" => ["test@example.com"],
    "use_smtp" => true
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer YOUR_KEY",
    "Content-Type: application/json"
]);
$response = curl_exec($ch);
curl_close($ch);
?>
import requests

response = requests.post(
    "https://kaijuverifier.com/api/v1/verify-batch",
    headers={"Authorization": "Bearer YOUR_KEY"},
    json={
        "emails": ["test@example.com"],
        "use_smtp": True
    }
)
print(response.json())

Example Response

{
  "success": true,
  "batch_id": "batch_xxxxxxxxxxxx",
  "status": "completed",
  "summary": {
    "total": 2,
    "valid": 1,
    "invalid": 1,
    "accuracy": 50
  },
  "results": [
    {
      "email": "user1@example.com",
      "is_valid": true,
      "status": "valid",
      "reason": null,
      "score": 92,
      "suggestion": null,
      "checks": { "syntax": true, "dns": true, "smtp": true, "mx": { "count": 5, "providers": ["google"], "grade": "A" } }
    }
  ],
  "quota": {
    "limit": 10000,
    "used_this_month": 14,
    "remaining": 9986
  }
}

POST /api/v1/jobs

Submit a large list for asynchronous processing. Unlike /api/v1/verify-batch (which verifies inline and returns when finished), the jobs API returns immediately with a job_id. A background worker processes the list, and you either poll the status endpoint or receive a job.completed webhook. Ideal for lists too large for a single synchronous request (up to 1,000,000 emails per job). Quota is reserved when the job is accepted, so a job that would exceed your monthly limit is rejected up front with 429.

Request Body

{
  "emails": ["user1@example.com", "user2@example.com", "..."],
  "use_smtp": true,
  "dedupe": false
}

Set "dedupe": true to trim whitespace, drop empty entries and remove case-insensitive duplicates before quota is reserved — so you're never charged to re-verify the same address. The response reports submitted, duplicates_removed and the billed total.

Submit a job

curl -X POST "https://kaijuverifier.com/api/v1/jobs" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"emails": ["a@example.com", "b@example.com"], "use_smtp": true}'

Returns 202 Accepted:

{
  "success": true,
  "job_id": "job_ef2eb1f225da9e1c926c607a",
  "status": "queued",
  "submitted": 2,
  "duplicates_removed": 0,
  "total": 2,
  "status_url": "https://kaijuverifier.com/api/v1/jobs/job_ef2eb1f225da9e1c926c607a"
}

GET /api/v1/jobs/{job_id} — poll status

Returns live progress. status moves queuedprocessingcompleted (or failed). A job you don't own returns 404.

{
  "success": true,
  "job_id": "job_ef2eb1f225da9e1c926c607a",
  "status": "processing",
  "total": 2,
  "processed": 1,
  "valid_count": 1,
  "invalid_count": 0,
  "risky_count": 0,
  "progress_pct": 50
}

DELETE /api/v1/jobs/{job_id} — cancel a job

Cancel a queued or processing job and refund the unprocessed remainder of the quota reserved at submit. Completed, failed or already-cancelled jobs return 409; the worker never resurrects a cancelled job.

curl -X DELETE "https://kaijuverifier.com/api/v1/jobs/JOB_ID" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "job_id": "job_ef2eb1f225da9e1c926c607a",
  "status": "cancelled",
  "processed": 120,
  "refunded": 880
}

GET /api/v1/jobs/{job_id}/results — fetch results

Returns the per-email verdicts once the job is completed. Polling results before completion returns 409 Conflict. Results are paginated (?page=1&per_page=1000). Add ?format=csv to download the entire job as a CSV file (email,is_valid,status,reason,score,suggestion) — handy for spreadsheets and one-shot list cleaning.

curl "https://kaijuverifier.com/api/v1/jobs/JOB_ID/results?format=csv" \
  -H "Authorization: Bearer YOUR_API_KEY" -o cleaned_list.csv
{
  "success": true,
  "job_id": "job_ef2eb1f225da9e1c926c607a",
  "status": "completed",
  "total": 2,
  "page": 1,
  "per_page": 1000,
  "results": [
    { "email": "a@example.com", "is_valid": true,  "status": "valid",   "reason": null,         "score": 92, "suggestion": null },
    { "email": "b@example.com", "is_valid": false, "status": "invalid", "reason": "user_unknown", "score": 8,  "suggestion": null }
  ]
}

GET /api/v1/jobs — list your jobs

Lists your recent jobs with their status and progress — useful for a dashboard view.

GET /api/v1/account

Check your plan, remaining quota and enabled features without spending a verification. Handy for dashboards, pre-flight checks and low-balance alerts. Takes no parameters.

Example Request

curl "https://kaijuverifier.com/api/v1/account" \
  -H "Authorization: Bearer YOUR_API_KEY"

Example Response

{
  "success": true,
  "account": {
    "email": "you@company.com",
    "plan": "starter",
    "plan_name": "Starter"
  },
  "quota": {
    "limit": 10000,
    "used": 12,
    "remaining": 9988,
    "reset_date": "2026-07-01"
  },
  "rate_limit_rpm": 60,
  "features": {
    "smtp": true,
    "webhooks": false,
    "catch_all": true
  }
}

POST /api/validate-email

Public, no-auth single-email check for quick testing and client-side widgets. Rate-limited per IP (no API key required, no plan quota consumed). For production volume, use the authenticated /api/v1/verify-single endpoint above.

Example Request

curl -X POST "https://kaijuverifier.com/api/validate-email" \
  -H "Content-Type: application/json" \
  -d '{ "email": "user@example.com" }'

Response Object

The verification endpoints return a JSON object with the per-email verdict plus your live quota. See the full field reference in Response fields above and the worked examples under each endpoint. The key fields:

{
  "email": "user@example.com",
  "is_valid": true,
  "status": "valid",
  "score": 92,
  "grade": "A",
  "reason": null,
  "suggestion": null,
  "checks": {
    "syntax": true,
    "dns": true,
    "smtp": true,
    "catch_all": false,
    "is_disposable": false,
    "role_based": false,
    "free_provider": false,
    "mx": { "count": 5, "providers": ["google"], "grade": "A" }
  },
  "quota": { "limit": 10000, "used_this_month": 12, "remaining": 9988 }
}
Field Type Description
is_valid boolean Safe-to-send decision.
status string valid, invalid, risky, or unknown.
score / grade int / string Deliverability score 0–100 and its letter grade (A–F).
suggestion string|null Did-you-mean typo correction, when detected.
checks.mx object MX records found: count, providers, grade.
checks.is_disposable boolean True if the domain is a known temporary email provider.
quota object Your remaining allowance: limit, used_this_month, remaining.

Error Codes

Standard HTTP status codes are used to indicate success or failure.

Code Description
200 OK - Request processed successfully.
400 Bad Request - Invalid parameters or JSON format.
401 Unauthorized - Invalid or missing API key.
405 Method Not Allowed - Wrong HTTP method (e.g. GET on a POST endpoint).
429 Too Many Requests - Rate limit or monthly quota exceeded.
500 Internal Server Error - Something went wrong on our end.

Webhooks

Instead of polling, register a webhook and KaijuVerifier will POST a signed JSON payload to your endpoint when an event happens. Webhooks are managed in Dashboard → Settings (available on paid plans — the features.webhooks flag in GET /api/v1/account tells you whether your plan has them enabled). You can register multiple endpoints; each is issued its own signing secret, shown once at creation.

Events

EventFires when
batch.completedA synchronous /api/v1/verify-batch request finishes. Delivered after the API response, so it never delays your call.
job.completedAn asynchronous bulk job finishes processing. The recommended way to consume large jobs without polling.
job.failedAn async bulk job errored out and was marked failed. Payload carries job_id and a short error string.

Payload

The request body is JSON. The summary is always included; job.completed carries the job_id so you can fetch results.

{
  "event": "job.completed",
  "job_id": "job_ef2eb1f225da9e1c926c607a",
  "summary": { "total": 2, "valid": 1, "invalid": 1, "risky": 0 },
  "timestamp": "2026-06-15T01:14:13+00:00"
}

Verifying the signature

Every delivery includes an X-Kaiju-Signature header of the form sha256=<hex>, where <hex> is the HMAC-SHA256 of the raw request body keyed with your endpoint's signing secret. Recompute it and compare with a constant-time function before trusting the payload — reject anything that doesn't match.

<?php
$secret  = getenv('KAIJU_WEBHOOK_SECRET');          // shown once when you create the endpoint
$payload = file_get_contents('php://input');         // the RAW body, do not json_decode first
$sent    = $_SERVER['HTTP_X_KAIJU_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);

if (!hash_equals($expected, $sent)) {
    http_response_code(401);
    exit('invalid signature');
}
$event = json_decode($payload, true);
// ... handle $event, then respond 2xx quickly ...
http_response_code(200);
?>
import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header or "")

Delivery, retries & idempotency

  • Acknowledge fast: respond with any 2xx status as soon as you've stored the event. Do the heavy work afterwards — slow handlers count as failures.
  • Retries: the first attempt is best-effort at event time. Failed deliveries are queued and retried with exponential backoff; after repeated failures the event is moved to a dead-letter queue and stops retrying.
  • Idempotency: a retried event is identical to the original, so deduplicate on your side (e.g. by job_id + event) — a handler may legitimately run more than once.
  • HTTPS only: endpoints must be public HTTPS URLs.

Integration Best Practices

  • Timeout Handling: Set a timeout of at least 5 seconds for single verification requests to account for DNS lookups.
  • Asynchronous Processing: For bulk requests, consider using a background job queue to avoid blocking your main application thread.
  • Caching: Cache verification results for 24-48 hours to save API credits and improve performance.