# Email check

`POST /v1/email/check` · **1 credit** (+5 with `verify_mailbox`)

## Request

| Field | Type | Required | Description |
|---|---|---|---|
| `email` | string | yes | The address to check. |
| `verify_mailbox` | boolean | no | Also ask the domain's mail server whether this mailbox exists. Adds up to a few seconds and 5 credits. Charged only if the server was actually asked. Default `false`. |

```bash [curl]
curl https://veqen-app.com/v1/email/check \
  -H "Authorization: Bearer $VEQEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane.doe+promo@gmial.com"}'
```

```python [Python]
r = requests.post(
    "https://veqen-app.com/v1/email/check",
    headers={"Authorization": f"Bearer {VEQEN_API_KEY}"},
    json={"email": "jane.doe+promo@gmial.com"},
    timeout=10,
)
```

```javascript [JavaScript]
const r = await fetch("https://veqen-app.com/v1/email/check", {
  method: "POST",
  headers: { Authorization: `Bearer ${VEQEN_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ email: "jane.doe+promo@gmial.com" }),
});
```

You can also use `GET /v1/email/check?email=...` for quick tests. Prefer `POST` in production so addresses don't end up in URL logs.

## Response

Typo domains like this are often registered by throwaway services, so this one is both a typo and disposable:

```json
{
  "email": "jane.doe+promo@gmial.com",
  "valid_syntax": true,
  "normalized": "jane.doe+promo@gmial.com",
  "canonical": "jane.doe@gmial.com",
  "local_part": "jane.doe+promo",
  "domain": "gmial.com",
  "disposable": true,
  "free_provider": false,
  "role_account": false,
  "plus_addressed": true,
  "did_you_mean": "jane.doe+promo@gmail.com",
  "domain_info": {
    "exists": true,
    "accepts_mail": true,
    "mx": [],
    "null_mx": false,
    "spf": false,
    "dmarc": false,
    "registered_at": "2003-05-12",
    "age_days": 8562,
    "registrar": "GoDaddy.com, LLC"
  },
  "mailbox": null,
  "risk": {
    "score": 93,
    "level": "high",
    "verdict": "refuse",
    "reasons": [
      { "code": "disposable_domain", "impact": 90, "message": "gmial.com hands out throwaway inboxes." },
      { "code": "possible_typo", "impact": 30, "message": "Looks like a typo of gmail.com." },
      { "code": "plus_addressed", "impact": 5, "message": "Uses a +tag, so one inbox can sign up many times." }
    ]
  }
}
```

### Fields

| Field | What it tells you |
|---|---|
| `valid_syntax` | `false` means it isn't an email address at all. `syntax_error` then explains why. |
| `normalized` | The address with a lower-cased, standard domain. |
| `canonical` | The inbox it really lands in: `+tags` removed, and for Gmail, dots removed too. **Compare this to spot one person making many accounts.** |
| `disposable` | The domain hands out throwaway inboxes (Mailinator, 10 Minute Mail and thousands more). |
| `free_provider` | A consumer mailbox like Gmail or Outlook. Normal for people, unusual for a B2B signup. |
| `role_account` | A shared inbox such as `info@` or `support@`. |
| `plus_addressed` | The address has a `+tag`. |
| `did_you_mean` | A suggested fix for a likely typo. Show it: "Did you mean jane@gmail.com?" |
| `domain_info.accepts_mail` | Whether mail can be delivered to the domain: it has MX records, or (as the email standard allows) an address record to fall back on. `false` means mail can never arrive. `null` means DNS couldn't be reached. |
| `domain_info.mx` | Mail servers, most preferred first. |
| `domain_info.age_days` | Days since the domain was registered, when the registry publishes it. |
| `mailbox` | Only when `verify_mailbox` was `true`. `result` is `deliverable`, `undeliverable`, `catch_all` (the server accepts any address) or `unknown`. |
| `risk` | Score, level, verdict and reasons. See [risk scores and reasons](/docs/reasons). |

## Typical handling

```python
risk = result["risk"]
if result.get("did_you_mean"):
    ask_user(f"Did you mean {result['did_you_mean']}?")
elif risk["verdict"] == "refuse":
    reject("Please use a permanent email address you can receive mail at.")
elif risk["verdict"] == "review":
    require_email_confirmation()
else:
    accept()
```

## Frequently asked questions

### Does the email check send an email?

No. We look at the address and its domain's DNS records. Even with `verify_mailbox`, we only start an SMTP conversation and stop before any message is sent.

### Why is mailbox verification optional?

It's slower (up to a few seconds), some mail providers always answer "yes" (catch-all), and large providers like Gmail don't reveal whether a mailbox exists. For signup forms, the default check plus email confirmation is usually the best mix. Mailbox verification shines for cleaning lists of business addresses.
