---
title: "Validate a batch of email addresses"
method: POST
path: "/api/v1/email-validations"
tags: ["Validation"]
---

# Validate a batch of email addresses

`POST /api/v1/email-validations`

## Request

- Base URL: `https://api.sendly.now`
- URL: `https://api.sendly.now/api/v1/email-validations`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `better-auth.session_token`
  - OAuth 2 (authorization code, authorization URL `https://app.sendly.now/api/auth/oauth2/authorize`, token URL `https://app.sendly.now/api/auth/oauth2/token`, scopes: `validation:write`)

Check up to 50 addresses for whether they can receive mail, and for the signals that make one worth mailing. Billed per address.

The response publishes a `verdict` alongside the flags it was drawn from. Branch on the verdict: the two obvious readings of the flags are both wrong — a free-provider address (`is_personal`) and a role mailbox (`is_role_address`) are ordinary, deliverable addresses that real customers use, and refusing them would shrink a list for no reason. `is_disposable` is the only flag that lowers a verdict.

`unknown` means DNS did not answer in time, so that address was NOT checked. It is a separate value from `undeliverable` on purpose — acting on the two together deletes live contacts over a network hiccup.

The 50-address ceiling is a latency bound, not a payload one: every distinct DOMAIN in the batch costs a DNS round trip. To validate a whole list, use `POST /api/v1/lists/{id}/validation-runs`, which runs as a background job.

Requires the `validation:write` scope — Check whether email addresses can receive mail — this is billed per address.

## Request body

- EmailValidationBatchRequestV1
  - `emails` string[], required: The addresses to check, at most 50. Every distinct DOMAIN costs a DNS round trip, so this endpoint is bounded by latency rather than payload size — validate a whole list with `POST /api/v1/lists/{id}/validation-runs`, which is a background job.

## Response `200`

One verdict per address, in the order they were given

- EmailValidationBatchV1: One verdict per address, in the order they were given.
  - `results` EmailValidationV1[], required
    - `email` string, required
    - `has_mx_records` boolean, required: The domain publishes MX records.
    - `is_disposable` boolean, required: A throwaway-inbox provider. The ONLY flag here that lowers the verdict.
    - `is_personal` boolean, required: A free/consumer provider (Gmail, Outlook). List-quality information, not a problem.
    - `is_role_address` boolean, required: The local part addresses a role (`support@`, `info@`), not a person. List-quality information: role mailboxes are deliverable and companies answer them.
    - `reasons` string[], required: Human-readable findings. Prose for a person to read — branch on `verdict`, never on these.
    - `verdict` 'deliverable' | 'undeliverable' | 'risky' | 'unknown', required: `deliverable`: the domain resolves and accepts mail, with no badness signal. `undeliverable`: the domain does not exist or publishes no MX records. `risky`: deliverable, but a throwaway-inbox provider — mailing it costs sender reputation. `unknown`: DNS did not answer in time, so this address was NOT checked. Ask again; never delete a contact on `unknown`.

## Other responses

- `401`: `invalid_api_key` or `invalid_session` — missing or invalid credentials.
- `403`: `scope_missing`, `project_access_denied`, or `project_disabled`.
- `422`: `validation_error` — query, path, or body parameters did not match the schema.
- `429`: `rate_limited` — see `Retry-After` and the `RateLimit` headers.
- `500`: `internal_error`.

---

[API](https://skmtc.dev/sendly/apis/sendly-api.md) · [All operations](https://skmtc.dev/sendly/apis/sendly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sendly/apis/sendly-api/revisions/ce7cfb7f7c7e?raw)
