# E-mail checks

> Check e-mail addresses with the MobileValidate API: which services exist, how addresses are normalized, result rows, and anti-enumeration rules.

Canonical: https://mobilevalidate.com/docs/emails · Last updated: 2026-09-25

E-mail checks answer whether a mailbox exists, or whether an account on a platform is registered with an address. Send the addresses in `emails` (next to or instead of `numbers`) and request at least one e-mail check. Answers are `registered: true`, `false` or `null` only. They never include names, avatars or profile data.

## Which e-mail services are there?

| Code | Alias | Answers | Modes |
|---|---|---|---|
| `email.valid` | `email` | The mailbox exists (major webmail providers) | real time + bulk |
| `gmail.email` | `gmail` | A Gmail account exists | bulk only |
| `outlook.email` | `outlook` | An Outlook account exists | bulk only |
| `yahoo.email` | `yahoo` | A Yahoo account exists | bulk only |
| `yandex.email` | `yandex` | A Yandex account exists | bulk only |
| `mailru.email` | `mailru` | A Mail.ru account exists | bulk only |
| `apple.email` | `apple.email` | An Apple Account uses this address | real time + bulk |
| `amazon.email`, `facebook.email`, `instagram.email`, `netflix.email`, `spotify.email` | — | An account on that platform uses this address | real time + bulk |
| `linkedin.email`, `x.email` | — | An account on that platform uses this address | bulk only |

Bulk-only services work in [bulk jobs](/docs/bulk-jobs). On `POST /v1/lookup` they return `403 service_disabled`. `GET /v1/services` is the live list for your key (`input_type: "email"`).

## How do I send e-mails in a request?

```bash
curl https://api.mobilevalidate.com/v1/lookup \
  -H "Authorization: Bearer $MOBILEVALIDATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"emails": ["registered@test.mobilevalidate.com", "unsupported@test.mobilevalidate.com"], "checks": ["email"]}'
```

Phone checks run on `numbers` and e-mail checks on `emails`, so one request can mix both, up to 100 identifiers in total. If you send e-mails without an e-mail check, the request is refused before anything is stored: `400 invalid_request`, `param: "checks"`, "No e-mail check was requested: add an e-mail check (e.g. email, gmail…) for the emails. See GET /v1/services."

## What does an e-mail result row look like?

Real test-mode output, first row:

```json
{
  "kind": "email",
  "input": "registered@test.mobilevalidate.com",
  "email": "registered@test.mobilevalidate.com",
  "email_status": "valid",
  "e164": null,
  "country": null,
  "checks": {
    "email.valid": {
      "service": "email.valid",
      "status": "completed",
      "registered": true,
      "attributes": null,
      "confidence": "high",
      "confidence_score": 0.99,
      "checked_at": "2026-09-25T14:23:15.781Z",
      "cached": false,
      "age_seconds": 0,
      "billed": false,
      "reason": null,
      "poll_after_ms": null
    }
  },
  "test": true
}
```

E-mail rows have `kind: "email"`, the normalized `email` (`null` if invalid), and `email_status`: `valid`, `invalid_email`, `duplicate` or `suppressed`. `e164` and `country` are always `null`, and there is no `number_status` or `whatsapp` copy. Rows are ordered numbers first, then e-mails, in input order. The second test address above returns `status: "unknown"`, `reason: "UNSUPPORTED_PROVIDER"`, `billed: false`.

## How are addresses normalized?

Addresses are trimmed and lowercased, and nothing else. We do not remove dots or `+tags`, and we do not map one provider domain to another, because such rewriting could merge addresses that belong to different people. An address that isn't syntactically valid gets `email_status: "invalid_email"` and no checks. A repeat of an earlier address in the same request is `duplicate`. An address on the suppression list is `suppressed`. None of these are billed. When you read results back later, `input` is masked (`re•••@test.mobilevalidate.com`) and `email` holds the normalized address.

## What are the anti-enumeration rules for e-mails?

Requests that look like generated e-mail lists are rejected with `403 suspected_enumeration`, `param: "emails"`:

- **Per request:** 20 or more distinct addresses on one domain whose local parts differ only by digits and/or separators. Every run of non-letters counts as one class, so `john1@`, `john.2@`, `john_3@`, `john-4@` and `john+5@` fall into one group, and `googlemail.com` is grouped with `gmail.com`.
- **Per day (live keys):** 50 or more distinct addresses of one such pattern from the same account in one UTC day, across all requests. Splitting a generated list into small requests doesn't get around this.

The account's daily cap counts e-mails the same way as numbers. E-mail checks are for fraud prevention and deliverability, such as screening sign-ups and keeping lists clean. They are not for discovering who owns an address.

## Frequently asked questions

### Do you send an e-mail to the address or log in to the mailbox?

No. Nothing is sent to the address and no mailbox is read. The answer is only yes, no or unknown.

### Why is my company domain unknown for the email check?

The email check covers major webmail providers. Addresses on other domains return unknown with reason UNSUPPORTED_PROVIDER, which is not billed.

### Do you treat john.smith@ and johnsmith@ as the same address?

No. We only trim spaces and lowercase the address. Dots and plus tags are kept, because rewriting them could merge different people's addresses.
