Email Validation
Check the deliverability of an email address before sending — syntax, MX, disposable, role-account, and free-provider signals.
The Validation API returns a per-address deliverability assessment without sending mail. It performs syntax validation, MX lookup with a 3-second timeout, and signal checks for disposable domains, role accounts, free providers, and common-domain typos (e.g. gmial.com → gmail.com).
Results for the same (org, email) pair are cached for 24 hours — repeat validations within that window return the cached result and do not consume quota. Transient DNS failures return result: "unknown" and are also not billed.
Validate a single email
POST /v1/validation/email
Requires sending_access scope.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to validate (1–254 chars). |
Example request
curl -X POST https://api.sendry.online/v1/validation/email \
-H "Authorization: Bearer $SENDRY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "stanley@example.com"}'
Response
{
"email": "stanley@example.com",
"result": "deliverable",
"risk_score": 0,
"checks": {
"syntax": true,
"mx": true,
"disposable": false,
"role_account": false,
"free_provider": false,
"did_you_mean": null
}
}
Result values:
| Value | Meaning |
|---|---|
deliverable | Address looks valid (risk_score 0–30) |
risky | Valid syntax + MX but flagged (disposable / role account, risk_score 31–70) |
undeliverable | Bad syntax or no MX records (risk_score 71–100) |
unknown | Transient DNS failure (SERVFAIL/timeout). Not billed, not persisted. Safe to retry. |
Checks:
| Field | Type | Description |
|---|---|---|
syntax | boolean | RFC-5322-lite validation of local + domain parts |
mx | boolean | Domain has at least one MX record (3s timeout per lookup) |
disposable | boolean | Domain matches a known disposable-email provider |
role_account | boolean | Local part is a role address (info@, admin@, support@, etc.) |
free_provider | boolean | Domain is a common free provider (gmail, yahoo, outlook, etc.) |
did_you_mean | string | null | Suggested common domain if the input looks like a typo |
Risk scoring:
| Signal | Score | Result |
|---|---|---|
| Bad syntax or no MX | 100 | undeliverable |
| Disposable domain | 70 | risky |
| Role account | 40 | risky |
| Free provider only | 20 | deliverable |
| All clear | 0 | deliverable |
Example: disposable address
{
"email": "test@mailinator.com",
"result": "risky",
"risk_score": 70,
"checks": {
"syntax": true,
"mx": true,
"disposable": true,
"role_account": false,
"free_provider": false,
"did_you_mean": null
}
}
Example: domain typo
{
"email": "user@gmial.com",
"result": "undeliverable",
"risk_score": 100,
"checks": {
"syntax": true,
"mx": false,
"disposable": false,
"role_account": false,
"free_provider": false,
"did_you_mean": "gmail.com"
}
}
Validate a batch
POST /v1/validation/batch
Requires sending_access scope. Validates up to 100 emails per request with bounded DNS concurrency. For larger lists, send multiple batches.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
emails | string[] | Yes | 1–100 email addresses. Each 1–254 chars. |
Example request
curl -X POST https://api.sendry.online/v1/validation/batch \
-H "Authorization: Bearer $SENDRY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"emails": [
"stanley@example.com",
"support@example.com",
"user@mailinator.com"
]
}'
Response
{
"results": [
{
"email": "stanley@example.com",
"result": "deliverable",
"risk_score": 0,
"checks": { "syntax": true, "mx": true, "disposable": false, "role_account": false, "free_provider": false, "did_you_mean": null }
},
{
"email": "support@example.com",
"result": "risky",
"risk_score": 40,
"checks": { "syntax": true, "mx": true, "disposable": false, "role_account": true, "free_provider": false, "did_you_mean": null }
},
{
"email": "user@mailinator.com",
"result": "risky",
"risk_score": 70,
"checks": { "syntax": true, "mx": true, "disposable": true, "role_account": false, "free_provider": false, "did_you_mean": null }
}
]
}
If the batch exceeds quota mid-way, the request aborts with 429 validation_quota_reached. Already-counted validations stay persisted.
List past validations
GET /v1/validation
Requires read_only scope. Cursor-paginated by validation id, newest first.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Results per page (1–100). |
cursor | string | Validation id from a previous response's next_cursor. | |
result | string | Filter by result: deliverable, undeliverable, risky, unknown. | |
email_domain | string | Filter by email domain (lowercased). |
Response
{
"data": [
{
"id": "evl_abc123",
"email": "stanley@example.com",
"email_domain": "example.com",
"result": "deliverable",
"risk_score": 0,
"checks": {
"syntax": true,
"mx": true,
"disposable": false,
"role_account": false,
"free_provider": false,
"did_you_mean": null
},
"created_at": "2026-05-30T14:00:00Z"
}
],
"has_more": false,
"next_cursor": null
}
Aggregate stats
GET /v1/validation/stats
Requires read_only scope. Returns counts per result type for the given window. Defaults to the last 30 days.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
from | string | now − 30 days | ISO 8601 lower bound (inclusive). |
to | string | now | ISO 8601 upper bound (inclusive). |
Response
{
"from": "2026-04-30T14:00:00Z",
"to": "2026-05-30T14:00:00Z",
"total": 1843,
"counts": {
"deliverable": 1502,
"undeliverable": 211,
"risky": 117,
"unknown": 13
}
}
Every status is always present in counts, even when zero, so dashboards can render empty buckets without extra logic.
Quotas
Monthly validation quota is enforced per plan, atomically at insert time. unknown results from transient DNS failures are not counted.
| Plan | Monthly validations |
|---|---|
| Free | 100 |
| Pro | 5,000 |
| Business | 25,000 |
| Enterprise | Unlimited |
When the quota is reached, the API returns 429 validation_quota_reached. Cached results (same email within 24h) bypass the quota.
Errors
| Status | Code | When |
|---|---|---|
| 401 | unauthorized | Missing or invalid API key |
| 403 | forbidden | API key lacks the required scope |
| 422 | validation_error | Missing/empty email, empty batch, or batch over 100 |
| 429 | validation_quota_reached | Monthly plan quota exhausted |
| 429 | rate_limited | Request rate limit exceeded |