Every signup form is a magnet for bad email addresses — typos like jhon@gmial.com, throwaway inboxes, and addresses that simply don't exist. Let them into your list and you pay for it later: bounces, a wrecked sender reputation, and your real emails landing in spam.
The fix is to verify an address before you trust it. Here's how to do it in real time from Node.js.
What "verifying" actually means
A good check goes well beyond a regex. It looks at:
- Syntax — is it a well-formed address?
- MX records — does the domain actually accept mail?
- SMTP — does the specific mailbox exist?
- Disposable — is it a 10-minute throwaway?
-
Role-based —
info@,support@(low engagement) - Catch-all — the domain accepts everything, so you can't be 100% sure
-
Typos —
gmial.comshould probably begmail.com
Calling the API
// verify.js
const API_KEY = process.env.CLEARBOUNCE_API_KEY;
async function verifyEmail(email) {
const res = await fetch("https://api.clearbounce.net/api/v1/verify", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
body: JSON.stringify({ email }),
});
if (!res.ok) throw new Error(`Verify failed: ${res.status}`);
return res.json();
}
A response looks like this:
{
"status": "deliverable",
"score": 98,
"isDeliverable": true,
"checks": {
"syntax": true,
"mxRecords": true,
"smtpValid": true,
"isDisposable": false,
"isRoleBased": false,
"isCatchAll": false,
"hasTypo": false
}
}
status is one of deliverable, undeliverable, risky, or unknown, and score (0–100) tells you how confident the result is.
Gating a signup with it
app.post("/signup", async (req, res) => {
const { email } = req.body;
const result = await verifyEmail(email);
if (result.status === "undeliverable") {
return res.status(400).json({ error: "That inbox doesn't exist — please double-check it." });
}
if (result.checks?.isDisposable) {
return res.status(400).json({ error: "Please use a permanent email address." });
}
// "risky" (catch-all / role) → let them in, but flag it for a later re-check
await createUser({ email, emailRisk: result.status === "risky" });
res.json({ ok: true });
});
Two habits that keep your list clean
- Verify at the point of entry (real time, like above) so junk never gets in.
- Re-clean the whole list periodically (bulk) — even good addresses go stale over time.
Full disclosure: I build ClearBounce, the verification API I used in the examples (it has a free tier if you want to try it). But the ideas matter more than the vendor — the same flow works with any real-time verification API.
Top comments (0)