Watch-IP

Catch disposable, mistyped and no-mail-server signup emails

Give your signup form an early check on every address: syntax, known disposable/temporary domains, whether the domain has a mail server at all, likely typos and role addresses — returned as an accept, review or reject verdict with the reasons behind it. Call POST /v1/email/validate from your form's own JavaScript, or check lists in bulk from your backend or the dashboard.

Request and authentication

Send a JSON body of { "email": "user@example.com" } to POST /v1/email/validate with an origin-locked publishable key in the X-Api-Key header — the same key type and allowed-origins model as GET /v1/geo, so it's safe to call directly from a signup form as the visitor types or submits. Server-side, your secret key unlocks POST /v1/email/batch (up to 100 addresses per request), asynchronous list jobs of up to 100,000 addresses (API or the dashboard's Email lists page, CSV in and CSV out), and POST /v1/signup/check, which combines the email check with the signing-up IP's network indicators into one score.

Read the verdict, then the reasons

verdict is the most severe outcome: reject for invalid syntax, a domain that doesn't exist, a domain with no mail server, or a disposable domain; review for a suspected typo, a role address such as info@, a domain with only an implicit mail server, or a DNS lookup that didn't complete; otherwise accept. reasons lists every condition that was hit, so your form can show a specific message — and when didYouMean is set, offer the correction instead of an error.

When to call it

Call on blur or submit rather than on every keystroke — checking a still-incomplete address on each character adds requests without adding useful signal and can flicker an error message while the visitor is still typing. Each call counts as one request.

If the request fails

Fail open: let the signup continue if the network request errors or times out, rather than blocking every visitor because of a client-side outage. On the server side, a DNS lookup that fails is reported as reason dns_unavailable (verdict review), never as an error or a reject. Preserve whatever the visitor already typed so a failed check never costs them their input.

How disposable domains are detected

Known disposable/temporary domains are merged from three community-maintained open-source lists (see data sources), refreshed every few hours, with an allowlist so a bad upstream entry can never flag a major provider. Subdomains of a listed domain match too. New throwaway domains that no list has yet are often caught by their mail server: disposable services point many domains at the same few mail hosts, and a domain whose mail server is known to serve listed domains is flagged with disposableSource: mx_host. A very new provider on a new mail server may still not be flagged.

Usage and quota

Every address checked counts as one request against your plan's shared monthly cap — a single check, each address in a batch, each distinct address in a list job — alongside your other traffic, with the same per-minute rate limit. There's no separate email plan or price. The per-minute rate limit counts HTTP calls, not items: a batch of up to 100 IPs or email addresses is one request toward it, while each item still counts separately toward the monthly request cap.

What this does not verify

Every check is domain- or address-level. Watch-IP never contacts the recipient's mail server, so a result does not establish that the mailbox exists, that mail to it will arrive, or that it belongs to the person signing up. An accept verdict means “no problem found,” not a confirmed working address — use a confirmation email for that.

Response fields

syntaxValid
Whether the address is syntactically valid.
isDisposable
Whether the domain matches a known disposable/temporary provider, directly, through a parent domain or through its mail server. Always false when syntaxValid is false.
disposableSource
list, parent_domain or mx_host — why isDisposable is true; null otherwise.
mx
The domain's mail-server status (found, implicit, none, null_mx, no_domain or unknown) and its primary MX host.
didYouMean
A likely typo correction such as gmial.com → gmail.com, or null.
isRoleAccount / isFreeProvider
Role address (admin@, info@, noreply@…) and consumer mailbox provider flags. Free provider is informational only.
normalizedEmail / canonicalEmail
Lowercased address, and the same without +tags (and Gmail dots) for spotting duplicate accounts.
verdict / reasons
accept, review or reject, plus every reason code that was hit — see the reference for the full list.

Act on the verdict

Signup form JavaScript

async function checkEmail(email) {
  const response = await fetch('https://api.watch-ip.com/v1/email/validate', {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_PUBLISHABLE_KEY', 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  });
  if (!response.ok) return { state: 'unknown' }; // fail open — don't block signup on a network error

  const result = await response.json();
  if (result.didYouMean) return { state: 'suggest', suggestion: result.didYouMean };
  if (result.verdict === 'reject') return { state: 'reject', reasons: result.reasons };
  return { state: 'ok' }; // accept, or review: let the signup through and flag it on your backend
}

Show a distinct message per reason — invalid_syntax, disposable_domain and no_mail_server call for different corrective action from the visitor — and offer didYouMean as a one-click fix.

Frequently asked questions

Does it verify the mailbox exists?

No. It checks syntax, the domain's lists and DNS mail-server records, and the address's shape. It never contacts the mail server or confirms the individual mailbox is real — a reject for no_mail_server is about the whole domain.

Is accept proof the email works?

No. accept means none of the checks found a problem, not that the address is guaranteed to receive mail or belongs to the person signing up. Use a confirmation email when you need that.

Can users bypass browser validation?

Yes. Any browser-side check, including this one, can be skipped by disabling JavaScript or calling your signup endpoint directly. Enforce whatever decision matters on your backend — POST /v1/signup/check is built for that.

Can I clean an existing list?

Yes. Upload a CSV on the dashboard's Email lists page, or submit up to 100,000 addresses to POST /v1/email/jobs with your secret key, then download a results CSV with a verdict and reasons per row. Duplicates are charged once, and uploads and results are deleted after 7 days.

How are requests counted?

One request per address checked, against your plan's shared monthly cap. Duplicates within a list job count once; a signup check counts as one request.

Related pages