# Email Verification API

The Email Verification API validates email addresses against syntax, DNS/MX records, disposable-domain lists, role-based prefixes, common typos, and (optionally) a live SMTP probe. It is implemented as an internal Go microservice that wraps [AfterShip/email-verifier](https://github.com/AfterShip/email-verifier); the public surface preserves the legacy response shape for existing integrations and adds a richer `meta` block.

## Base URL

```
https://api.zaits.net/v1/email
```

## Authentication

All requests require your API key in the Authorization header:

```http
Authorization: Bearer YOUR_API_KEY
```

Dashboard sessions can also use a Supabase JWT. Both routes apply tier-based rate limiting and per-call credit metering — see [Rate Limits](/api/guides/rate-limits.md).

***

## Single Email Verification

### Endpoint

```http
GET /v1/email/verify
```

### Parameters

| Parameter   | Type    | Required | Description                                                                                                                                                                                                           |
| ----------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`     | string  | Yes      | Email address to verify. URL-encode if needed.                                                                                                                                                                        |
| `reachable` | boolean | No       | When `true`, performs a live SMTP `RCPT TO` probe against the recipient's mail server. Off by default — adds 1–5 seconds of latency and frequently returns `unknown` against major providers (Gmail, Outlook, Yahoo). |

### Request Example

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET "https://api.zaits.net/v1/email/verify?email=user@example.com" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const response = await fetch(
  `https://api.zaits.net/v1/email/verify?email=${encodeURIComponent("user@example.com")}`,
  { headers: { Authorization: "Bearer YOUR_API_KEY" } }
);
const result = await response.json();
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

response = requests.get(
    "https://api.zaits.net/v1/email/verify",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    params={"email": "user@example.com"},
)
result = response.json()
```

{% endtab %}
{% endtabs %}

### Response

```json
{
  "success": true,
  "data": {
    "email": "user@example.com",
    "status": "VALID",
    "score": 95,
    "validations": {
      "syntax": true,
      "domain_exists": true,
      "mx_records": true,
      "is_disposable": false,
      "is_role_based": false
    },
    "aliasOf": null,
    "meta": {
      "suggestion": null,
      "free": false,
      "gravatar": false,
      "reachability": "unknown",
      "smtp": null
    },
    "processing_time": 0.234
  },
  "processing_time": 0.235
}
```

### Status Values

| Value            | Meaning                                                              |
| ---------------- | -------------------------------------------------------------------- |
| `VALID`          | Passes syntax, has MX records, not disposable.                       |
| `INVALID_FORMAT` | RFC-invalid syntax.                                                  |
| `INVALID_DOMAIN` | Domain has no MX records (or DNS lookup failed).                     |
| `DISPOSABLE`     | Domain matches the bundled disposable-email list.                    |
| `ERROR`          | Upstream verifier could not complete (DNS timeout, malformed input). |

***

## Batch Email Verification

Validate up to **100** emails in a single request, in parallel.

### Endpoint

```http
POST /v1/email/verify/batch
```

### Request Body

```json
{
  "emails": [
    "user1@example.com",
    "test+alias@gmail.com",
    "admin@company.com"
  ],
  "reachable": false
}
```

| Field       | Type      | Required | Description                                                                   |
| ----------- | --------- | -------- | ----------------------------------------------------------------------------- |
| `emails`    | string\[] | Yes      | 1–100 addresses.                                                              |
| `reachable` | boolean   | No       | Same semantics as the single-call flag — applied to every email in the batch. |

### Request Example

```bash
curl -X POST "https://api.zaits.net/v1/email/verify/batch" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"emails":["user@example.com","test+alias@gmail.com","admin@company.com"]}'
```

### Response

```json
{
  "success": true,
  "data": {
    "results": [
      { "email": "user@example.com", "status": "VALID", "score": 95, "...": "..." },
      { "email": "test+alias@gmail.com", "status": "VALID", "aliasOf": "test@gmail.com", "...": "..." },
      { "email": "admin@company.com", "status": "VALID", "validations": { "is_role_based": true }, "...": "..." }
    ],
    "statistics": {
      "total": 3,
      "valid": 3,
      "invalid_format": 0,
      "invalid_domain": 0,
      "disposable": 0,
      "role_based": 1,
      "has_alias": 1
    }
  },
  "processing_time": 0.458
}
```

***

## Response Fields

### `validations` object

| Field           | Type    | Description                                                        |
| --------------- | ------- | ------------------------------------------------------------------ |
| `syntax`        | boolean | RFC-compliant format.                                              |
| `domain_exists` | boolean | Domain resolves in DNS.                                            |
| `mx_records`    | boolean | Domain has at least one MX record.                                 |
| `is_disposable` | boolean | Domain is on the disposable-email list.                            |
| `is_role_based` | boolean | Local part is a generic role (e.g. `admin`, `support`, `noreply`). |

### `aliasOf`

If the input is a recognized alias, this contains the canonical form. The verifier normalizes:

* Gmail / Googlemail — strips `+suffix` and dots: `j.doe+promo@gmail.com` → `jdoe@gmail.com`.
* Yahoo — strips `-suffix`.
* Outlook / Hotmail / Live — strips `+suffix`.

`null` when no alias normalization applies.

### `meta` object

Added 2026-05-07. Surfaces fields from the upstream verifier that aren't part of the legacy shape.

| Field          | Type           | Description                                                                                                                                     |
| -------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `suggestion`   | string \| null | Suggested correction for likely typos (e.g. `gmial.com` → `gmail.com`).                                                                         |
| `free`         | boolean        | Email is from a free consumer provider (Gmail, Yahoo, Outlook, etc.).                                                                           |
| `gravatar`     | boolean        | Email has an associated Gravatar profile.                                                                                                       |
| `reachability` | string         | `yes` / `no` / `unknown`. Only meaningful when the request used `reachable=true`.                                                               |
| `smtp`         | object \| null | Detailed SMTP probe result (only populated when `reachable=true`). Fields: `host_exists`, `full_inbox`, `catch_all`, `deliverable`, `disabled`. |

***

## Quality Score

The `score` field is a 0–100 quality rating computed from `validations`:

| Component      | Points |
| -------------- | ------ |
| Valid syntax   | +20    |
| Domain exists  | +20    |
| MX records     | +25    |
| Not disposable | +25    |
| Not role-based | +10    |
| Alias detected | -5     |

| Range  | Interpretation                                   |
| ------ | ------------------------------------------------ |
| 90–100 | Excellent — high-quality personal email          |
| 80–89  | Good — valid, may be role-based or alias         |
| 60–79  | Fair — valid but with concerns                   |
| 40–59  | Poor — syntax OK but domain/MX issues            |
| 0–39   | Very poor — invalid format, disposable, or no MX |

***

## Errors

| Status | Body                                                                                         | Cause                                                               |
| ------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| 400    | `{ "success": false, "error": "Email parameter is required" }`                               | Missing `email` query param.                                        |
| 400    | `{ "success": false, "error": "emails parameter is required and must be an array" }`         | Batch body malformed.                                               |
| 400    | `{ "success": false, "error": "Maximum 100 emails allowed per batch request" }`              | Batch over the limit.                                               |
| 401    | `{ "success": false, "error": "Invalid authentication token or API key" }`                   | Bad / expired credentials.                                          |
| 403    | `{ "success": false, "error": "Permission denied. Your API key doesn't have read access." }` | API key missing `read` scope.                                       |
| 429    | `{ "success": false, "error": "Rate limit exceeded. ..." }`                                  | Tier rate limit hit. See [Rate Limits](/api/guides/rate-limits.md). |
| 500    | `{ "success": false, "error": "Internal server error", "message": "..." }`                   | Upstream verifier failed.                                           |

***

## Best Practices

1. **Default to `reachable=false`.** SMTP probes are slow and unreliable from non-residential IPs (Gmail/Outlook/Yahoo throttle or refuse them). Use the flag only when you specifically need mailbox-level confirmation and can accept higher latency + a high `unknown` rate.
2. **Use the batch endpoint for ≥3 emails.** Validation runs in parallel and the per-domain DNS lookups benefit from connection reuse inside the verifier.
3. **Cache by `(email, reachable)` for short windows.** Most fields (syntax, MX, disposable, role) are stable for hours or days. The `reachability` field can flip in seconds — cache it shorter.
4. **Treat `is_role_based` as advisory.** Generic local parts (`admin@`, `support@`) are usually valid for B2B contexts; only block them for consumer signups where you need a person, not a shared mailbox.
5. **Use `aliasOf` to deduplicate accounts.** If two records have the same canonical alias, treat them as the same identity.

***

## Limitations

* DNS lookups add 100–300ms per unique domain on the first hit (cached for the verifier's lifetime).
* The disposable-domain list is loaded at process start and refreshed on container restart.
* SMTP probes from the production egress IP are blocked or rate-limited by major free providers; expect `reachability: "unknown"` for the majority of Gmail/Outlook/Yahoo addresses.
* Batch is capped at 100 per request.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zaits.gitbook.io/api/api-reference/email.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
