CleanValidate API
Verify any email address or clean lists in bulk with a single REST endpoint. Built for production — low latency, predictable pricing, and no SDK required.
Introduction
The CleanValidate API exposes the same engine that powers our dashboard. It performs syntax checks, MX-record lookups, SMTP probes, catch-all detection and disposable / role-account filtering, then returns a normalized result.
All endpoints share the base URL:
https://cleanvalidate.com/api/public/v1Authentication
Every request must include a Authorization: Bearer <key> header. Create a key from your API Keys page. Keys are shown only once at creation — store them securely. Lost keys cannot be recovered; create a new one and revoke the old.
Authorization: Bearer cv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxRequests without a valid key return 401 Unauthorized.
Verify single email
Verify one email and consume 1 credit.
/api/public/v1/verify?email={email}/api/public/v1/verifyRequest
curl "https://cleanvalidate.com/api/public/v1/verify?email=test@example.com" \
-H "Authorization: Bearer cv_live_..."Or as JSON:
curl -X POST "https://cleanvalidate.com/api/public/v1/verify" \
-H "Authorization: Bearer cv_live_..." \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com"}'Response 200
{
"email": "test@example.com",
"status": "valid",
"score": 95,
"reason": "Mailbox accepts mail",
"suggestion": null,
"checks": {
"syntax": true,
"mx": true,
"smtp": true,
"catch_all": false,
"disposable": false,
"free_provider": false,
"role_account": false,
"mailbox_exists": "yes"
}
}Verify in bulk
Verify up to 100 emails per request. Each verified email consumes 1 credit. For larger lists, use the dashboard bulk uploader or page through this endpoint.
/api/public/v1/verify/bulkRequest
curl -X POST "https://cleanvalidate.com/api/public/v1/verify/bulk" \
-H "Authorization: Bearer cv_live_..." \
-H "Content-Type: application/json" \
-d '{"emails":["a@example.com","b@example.com","c@example.com"]}'Response 200
{
"count": 3,
"credits_used": 3,
"summary": { "valid": 2, "risky": 1, "invalid": 0, "unknown": 0 },
"results": [
{ "email": "a@example.com", "status": "valid", "score": 95, "reason": "...", "checks": { ... } },
{ "email": "b@example.com", "status": "risky", "score": 65, "reason": "...", "checks": { ... } },
{ "email": "c@example.com", "status": "valid", "score": 90, "reason": "...", "checks": { ... } }
]
}Response object
| Field | Type | Description |
|---|---|---|
| string | Echoed email, lowercased. | |
| status | enum | valid · risky · invalid · unknown |
| score | number 0–100 | Confidence score. |
| reason | string | Human-readable explanation. |
| suggestion | string | null | Typo-fix suggestion (e.g. gnail.com → gmail.com). |
| checks.syntax | bool | Valid RFC-5322 syntax. |
| checks.mx | bool | Domain has MX records. |
| checks.smtp | bool | SMTP server reachable. |
| checks.catch_all | bool | Domain accepts any address. |
| checks.disposable | bool | Temporary / disposable provider. |
| checks.free_provider | bool | Free webmail (Gmail, Yahoo, …). |
| checks.role_account | bool | Role-based (info@, support@, …). |
| checks.mailbox_exists | enum | yes · no · unknown |
Status meanings
- valid — safe to send to. Mailbox confirmed.
- risky — deliverable but unreliable (catch-all, role address, privacy provider). Send with caution.
- invalid — will bounce. Do not send.
- unknown — verification was inconclusive (provider blocked, timeout). Not charged when caused by upstream rate-limits.
Credits & billing
Each successfully verified email consumes 1 credit. Credits are deducted from your plan balance. If your balance is too low for a bulk request, the API returns 402 Insufficient credits without performing any verification. Top up from the Billing page.
Provider rate-limit refunds: if the upstream provider rate-limits us on individual rows, those rows are refunded automatically and not counted against your balance.
Rate limits
Reasonable usage limits apply per API key. The bulk endpoint accepts up to 100 emails per request; for larger sets, send sequential requests or use the dashboard uploader.
Errors
Errors return JSON with an error field and the appropriate HTTP status.
| HTTP | Meaning |
|---|---|
| 400 | Bad request — invalid email or JSON body. |
| 401 | Missing, invalid, or revoked API key. |
| 402 | Insufficient credits. |
| 429 | Upstream rate limit — retry shortly. |
| 500 | Internal error — please retry or contact support. |
{ "error": "Insufficient credits" }Code samples
Node.js / TypeScript
const res = await fetch("https://cleanvalidate.com/api/public/v1/verify?email=test@example.com", {
headers: { Authorization: `Bearer ${process.env.CLEANVALIDATE_KEY}` },
});
const data = await res.json();
console.log(data.status, data.score);Python
import os, requests
r = requests.post(
"https://cleanvalidate.com/api/public/v1/verify/bulk",
headers={"Authorization": f"Bearer {os.environ['CLEANVALIDATE_KEY']}"},
json={"emails": ["a@example.com", "b@example.com"]},
)
print(r.json())PHP
$ch = curl_init("https://cleanvalidate.com/api/public/v1/verify?email=test@example.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . getenv("CLEANVALIDATE_KEY"),
]);
$response = json_decode(curl_exec($ch), true);
echo $response["status"];Go
req, _ := http.NewRequest("GET", "https://cleanvalidate.com/api/public/v1/verify?email=test@example.com", nil)
req.Header.Set("Authorization", "Bearer " + os.Getenv("CLEANVALIDATE_KEY"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))Support
Questions or feature requests? Email support@cleanvalidate.com or open a ticket from the Support page in your dashboard.