A signup form that only checks for an @ symbol isn't really checking anything. Bots, burner inboxes, and one-time trial hunters all pass that test just fine, and every one of them ends up in your user table looking exactly like a real customer.
Fraud-scoring research from IPQS suggests that disposable email addresses are closely associated with abusive account behavior. That's not a coincidence worth shrugging off at the point of signup, and catching it doesn't require a fraud team, just one API call before the account ever gets created.
- Real-time email verification catches disposable, fake, and bot-created signups before they become accounts, not after they bounce.
- One API call checks syntax, DNS and MX records, disposable-domain status, catch-all risk, and role-account flags in a single response.
- A bad address costs you twice: once as a bounce that hurts sender reputation, again as a fake account that skews every metric downstream.
- Adding the signup's IP address to the same call also screens for VPNs, Tor exits, proxies, and known attacker infrastructure.
- Real-time checks stop bad signups at the door. Bulk checks clean the list you already have.
- A free tier (10,000 credits, no card required) is enough to test both endpoints against your own data before committing to anything.
What a single check actually catches
A "valid or invalid" boolean hides too much. A real verdict needs several signals read together, since a domain that resolves fine can still be a burner provider, and a mailbox that accepts mail can still belong to no one in particular. Here's what each signal is actually telling you.
|
Signal |
What it catches |
False positive risk |
Suggested action |
|
Syntax check |
Typos and malformed addresses |
Very low |
Reject instantly, prompt for correction |
|
MX / DNS check |
Domains that can't receive mail at all |
Low |
Reject |
|
Disposable domain check |
Burner inboxes (Mailinator-style providers) |
Low, though new domains appear daily |
Reject, or flag for review |
|
Catch-all detection |
Domains where a "yes" doesn't confirm the mailbox exists |
Moderate, legitimate companies use catch-all too |
Route to review, don't hard-block |
|
Role account flag |
Team inboxes like support@ and admin@ |
Low |
Allow on B2B forms, block on personal signups |
|
IP threat screening (optional) |
VPN, Tor, proxy, or known attacker behind the signup |
Moderate, real users use VPNs too |
Add friction, don't auto-reject on this alone |
The code: one call, real fields
Here's the whole check in Node, called the moment a signup form submits, using APIFreaks' Email Checker API. The API key lives in an environment variable. Never hardcode a credential like this in your codebase.
js
// Requires an APIFreaks API key. Set it as an environment
variable,
// never hardcode a key in your code.
const API_KEY = process.env.APIFREAKS_API_KEY;
async function checkEmail(email, ip) {
const body = ip ? { email, ip } : { email };
const res = await fetch(
`https://api.apifreaks.com/v1.0/email-validation/single?apiKey=${API_KEY}`,
{
method: "POST",
headers: { "Content-Type":
"application/json" },
body: JSON.stringify(body),
}
);
if (!res.ok) {
throw new Error(`Email check failed:
${res.status}`);
}
const data = await res.json();
if (data.validEmail !== "valid") {
// reason explains why: bad syntax, dead domain,
disposable, etc.
return { allow: false, reason: data.reason ??
data.validEmail };
}
if (data.domain?.disposable) {
return { allow: false, reason:
"disposable_domain" };
}
if (data.domain?.catchAll) {
// Can't confirm the specific mailbox exists,
don't hard block
return { allow: "review", reason:
"catch_all_domain" };
}
return { allow: true };
}
Pass the connecting IP alongside the email, and the response also grows an address.security block: threat_score, is_proxy, is_tor, and is_known_attacker. One call now screens the account and the connection behind it. That extra field earns its keep on checkout and account-upgrade flows, where the cost of a mistake is higher than on a free signup.
Real-time checks like this one are built for the moment of entry. For a list you already have sitting in a database, a separate bulk endpoint runs the same checks on up to 100 addresses per request, useful for a monthly cleanup pass rather than a per-signup gate.
Where this breaks
A few honest caveats, opinion flagged as one: these checks confirm an address is real and reachable, not that a human owns it or that they are who they claim to be. A validated inbox can still sit behind a bot with a real Gmail account attached. Verification raises the cost of faking a signup, it doesn't eliminate the possibility.
Catch-all domains are the trickiest case. The mail server accepts anything addressed to that domain, so a "yes" from the mail exchange doesn't confirm the specific mailbox exists. Treat catch-all results as risky, not valid, and route them to a review step instead of an automatic accept.
Disposable-domain lists can't ever be complete either. New throwaway providers launch constantly, so a list that's current today has gaps by next month. Pairing the disposable flag with basic behavior monitoring, several signups from one IP in a short window, for instance, covers more ground than the flag alone.
And one more limit worth stating plainly: hard-blocking every flagged signup will cost you real users. A privacy-conscious visitor signing up through a VPN, or someone using a company's catch-all address, isn't automatically committing fraud. Use these signals to add friction, a confirmation step, a manual review queue, rather than an automatic rejection, unless the signal stacks with something else that actually raises the stakes.
How to actually wire this in
Validate at the point of entry. Call the single-check endpoint the moment a signup form submits, before the account gets created. A rejected or flagged address never becomes a database row in the first place.
Branch on the verdict, not a boolean. valid clears the signup. invalid blocks it and surfaces the reason so a real user can fix a typo. risky and unknown go to a review queue or a confirmation-email step instead of an automatic accept.
Add the IP for anything payment-adjacent. On checkout or account-upgrade flows, pass the connecting IP alongside the email so the same response includes the threat score and proxy or Tor flags.
Clean what you already have, on its own schedule. Point the bulk endpoint at your existing user or mailing list periodically, monthly is reasonable for most apps, so addresses that have gone stale since signup don't quietly drag down deliverability.
Log the borderline cases. Catch-all and role-account flags are exactly the data you'll want later if you ever need to tune how aggressive these checks should be.
FAQ
What's the difference between email verification and email validation?
In practice the two terms are used interchangeably, and this API treats them the same way: confirming an address is correctly formatted, resolves to a real domain, and can actually receive mail, not just guessing from the syntax alone.
Can email verification alone stop bot signups?
No, and treating it as a complete solution is a mistake. Opinion, flagged as one: it's the single highest-leverage first filter, since it removes the laziest bot signups and burner-email trial abuse for free, but pair it with rate limiting and CAPTCHA for anything more determined.
Will this ever block a real user by mistake?
Occasionally, yes, mostly on catch-all domains where the API can't confirm a specific mailbox exists. That's exactly why catch-all results come back as risky instead of invalid, so they can be routed to review rather than rejected outright.
Is there a free way to test this before integrating?
Yes. New accounts start with 10,000 free credits and no card required, enough to test both the real-time and bulk endpoints against your own list before committing to anything.
Does checking MX records catch disposable emails? Not by itself. MX records confirm a domain can receive mail at all, they say nothing about whether that domain is a throwaway provider. Disposable detection is a separate check against a maintained list of known temporary-email domains, and a complete picture needs both.
Where to start
None of this needs to become a whole fraud stack on day one. Start with the single-check endpoint on the signup form, reject the obvious invalids and disposables, and route catch-all results to a review step. Once that's running, point the bulk endpoint at the existing user list and see how much of it was never reachable to begin with.

