DEV Community

Cover image for Building a Disposable Email Detector API Endpoint from Scratch
Arjun CS
Arjun CS

Posted on

Building a Disposable Email Detector API Endpoint from Scratch

Disposable email addresses are a recurring headache for anything with a signup form: they pass basic syntax validation, have a real domain, and even receive a one-time code, but the inbox self-destructs shortly after. If you're trying to keep fake signups out of your product, here's how to build a basic detector endpoint yourself, and where the DIY approach actually hits its limits.

The basic approach

A disposable email detector needs at least two layers to be useful:

  1. Domain-list matching — check the address's domain against a known list of disposable providers
  2. MX record validation — confirm the domain can actually receive mail at all

Let's build a simple Node.js/Express endpoint that does both.

Step 1: Set up the endpoint

const express = require('express');
const dns = require('dns').promises;
const app = express();
app.use(express.json());

// A small sample list — in production this needs to be much larger
// and updated regularly, new disposable domains appear constantly
const disposableDomains = new Set([
  'mailinator.com',
  'yopmail.com',
  'guerrillamail.com',
  '10minutemail.com',
  'temp-mail.org',
  'throwawaymail.com',
  'maildrop.cc',
  'emailondeck.com'
]);

app.post('/api/check-email', async (req, res) => {
  const { email } = req.body;
  if (!email || !email.includes('@')) {
    return res.status(400).json({ error: 'Invalid email format' });
  }

  const domain = email.split('@')[1].toLowerCase();
  const result = await checkEmail(domain);
  res.json(result);
});

app.listen(3000, () => console.log('Running on port 3000'));
Enter fullscreen mode Exit fullscreen mode

Step 2: Write the actual check logic

async function checkEmail(domain) {
  const result = {
    domain,
    isDisposable: false,
    hasMxRecord: false,
    risk: 'unknown'
  };

  // Layer 1: domain-list match
  if (disposableDomains.has(domain)) {
    result.isDisposable = true;
    result.risk = 'high';
    return result;
  }

  // Layer 2: MX record check
  try {
    const mxRecords = await dns.resolveMx(domain);
    result.hasMxRecord = mxRecords.length > 0;
    result.risk = result.hasMxRecord ? 'low' : 'high';
  } catch (err) {
    // No MX record found, domain likely can't receive mail
    result.hasMxRecord = false;
    result.risk = 'high';
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

This catches the obvious cases: known disposable domains and domains that simply can't receive mail. Good enough for a first pass.

Where this approach breaks down

A few real limitations worth knowing before you ship this:

The domain list goes stale fast. New disposable email services launch regularly, and a static list, even a long one, lags behind. You'd need a process to keep pulling updates, which is its own maintenance burden.

MX records don't confirm a live mailbox. A domain can have valid MX records and still be a disposable service, having mail infrastructure doesn't mean the inbox isn't designed to self-destruct in an hour.

No SMTP-level verification. This endpoint can't tell you whether the specific mailbox actually exists right now, that requires an actual SMTP handshake with the destination mail server, which is more involved to build and maintain reliably (handling timeouts, greylisting, rate limits from mail providers who don't want random servers pinging them repeatedly).

Catch-all domains will slip through. A domain configured to accept mail for any address will pass the MX check every time, even disposable ones, since "accepts mail" and "mailbox genuinely exists" aren't the same thing.

Where I'd actually draw the line

For a hobby project or internal tool, this basic version is a reasonable starting point. For anything production-facing where fake signups cost you real money (lead gen, trial abuse, gated content), I've found it's worth pairing this with a dedicated validation API rather than maintaining the disposable-domain list and SMTP logic yourself. I've used Gamalogic's Fake Email Checker API for this specifically, it runs the domain match, a live SMTP handshake, and catch-all/risk scoring together, with the disposable list kept updated server-side so you're not maintaining it. Tools like ZeroBounce have a similar API available if you're already integrated with them elsewhere.

Either way, the core idea holds: catch this at the point of signup, not after a bounce. By the time you see the bounce, you've already paid for the send and polluted your lead data.

Top comments (1)

Collapse
 
ishan_shrestha profile image
Ishan Shrestha •

One thing that helped me a lot. Fresh burner domains usually point at the same few MX hosts as the old ones. So I match the MX hostname too, not just the domain.

Catches new throwaways before any list has them. I pair it with the live list from Email Score (emailscore.neuralevo.com).

Have you tried keying on the MX host?