Disposable Email Checker: How to Detect Throwaway Email Addresses

Disposable Email Checker: How to Detect Throwaway Email Addresses

A disposable email checker tells you whether an address comes from a temporary inbox service, the kind people use once and then abandon. To check one address, paste it into our free email verifier and look at the "Temporary Email" row. It works as a temporary email checker with no signup.

This guide covers both jobs: checking a single address right now, and blocking disposable addresses automatically at signup with the API. It also explains exactly how the check works and where it can miss.

Short answer

  1. Paste the address into the free checker at /email-verifier. You get 5 free validations per hour, no account needed.
  2. Read the "Temporary Email" row and the score. "yes" means the domain is on our disposable list. A disposable address that passes every other check scores 2 out of 10.
  3. For signups, call the API (POST /email/validate) and reject the address when the result's description array contains disposable.

What counts as a disposable email address

A disposable email address (also called a throwaway, temporary or burner email address) is an inbox that's meant to be used briefly and then dropped. Well-known examples are Mailinator, Guerrilla Mail, 10 Minute Mail, YOPmail and Temp Mail.

People use them for understandable reasons:

  • Privacy: they don't want to give a real address to a site they don't trust yet.
  • Avoiding spam: they want the download or the coupon, not the newsletter that follows.
  • Gaming free trials: a fresh address per signup means a fresh trial each time.

For you as the sender, the problem is the same in every case. The address may stop being read soon after signup, so your onboarding emails, receipts and password resets go nowhere, and the contact still sits on your list.

A throwaway email checker can't read anyone's intent. What it can do is recognize the domain. That's why most tools, ours included, work as a disposable email domain checker: they look at the part after the @ and compare it with a list of domains known to belong to disposable services.

Check one email right now

The free email verifier runs the full validation on one address, including the disposable check.

  1. Open /email-verifier and enter the address.
  2. Click Verify and complete the quick captcha.
  3. Read the result panel. It lists Syntax, Domain, Mailbox, Temporary Email, Catch-All and Spam trap detection, followed by a score out of 10.

The Temporary Email row shows "yes" when the address was flagged as disposable and "no" when it wasn't. A flagged address shows a low score in red: 2 / 10 at most, and often 1 / 10, because many disposable services also accept mail for any address (more on the scoring below).

You can run 5 free validations per hour without signing up. In the tool's own words: "Email addresses are validated in real-time and not stored. Validation logs are retained for up to 30 days for debugging purposes."

If you want the broader picture of what makes an address valid (syntax, MX records, mailbox), see our guide on how to check if an email is valid.

How CampaignKit's disposable check works

Some vendors keep their detection method private. QuickEmailVerification, for example, says it doesn't disclose its process so disposable providers can't work around it. We think knowing what the check does helps you decide what to do with the result, so here it is.

Illustration of an email address being compared against a list of known disposable domains

The list. Our validator builds its disposable-domain list from two live sources and merges them:

  • the community-maintained disposable-email-domains blocklist on GitHub (CC0-licensed, actively maintained, and also used by PyPI to block signups), and
  • a second live list from the deviceandbrowserinfo.com API.

The refresh. The list is rebuilt when the service starts and then every 6 hours. If both live sources are unreachable at the same time, the validator falls back to a list bundled with the service, so the check keeps working, just with older data.

The match. The validator takes the domain part of the address, lowercases it, and looks for an exact match in the list. A match adds disposable to the result's description and sets the classifier to risky.

The score. A disposable match subtracts 8 from the score. An address that passes every other check starts at 10, so it lands at 2, which our API reference describes as "Email can be used, but looks like a SPAM Trap or temporary email address." Many disposable services accept mail for any address, so they also get the catch-all deduction of 1 and land at 1. Other issues push the score lower still (it never drops below 0).

The disposable check runs as part of the full validation: syntax, MX records, mailbox, blacklist, catch-all and role checks, all scored from 0 to 10. That's why a single request tells you both "is this a burner?" and "will mail to it even arrive?"

Block disposable signups automatically (API)

Checking by hand doesn't scale to a signup form. The disposable email checker API is the POST /email/validate endpoint, which runs the full validation described above. Send the address at signup and reject it when the disposable flag is set.

Illustration of a gate blocking a paper-airplane-shaped signup, representing blocking disposable signups automatically

You need an API key. Create a free account (200 free validations, no credit card) and add a key under Profile, in the API Keys section. Each validation uses 1 credit.

The request

curl --request POST \
  --url 'https://api.campaignkit.cc/v1/email/validate' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "emails": ["jane@mailinator.com"] }'

For this Mailinator address, the response looks like this. Mailinator accepts mail for any address, so the result also carries the catch-all flag:

{
  "creditsUsed": 1,
  "results": [
    {
      "email": "jane@mailinator.com",
      "result": {
        "syntax": "pass",
        "mx": "pass",
        "mailbox": "pass",
        "score": 1,
        "description": ["catchAll", "disposable"],
        "classifier": "risky",
        "smtpResponse": ""
      }
    }
  ]
}

The score and the other entries in description can differ from address to address. The disposable entry is what tells you the domain was matched, so check for that instead of a score threshold.

A signup check in Node.js

This script works in Node.js 18 or later (it uses the built-in fetch). Save it as check-disposable.mjs:

// check-disposable.mjs
// Usage: CAMPAIGNKIT_API_KEY=your_key node check-disposable.mjs someone@example.com

const API_URL = "https://api.campaignkit.cc/v1/email/validate";

export async function checkSignupEmail(email) {
  const response = await fetch(API_URL, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CAMPAIGNKIT_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ emails: [email] }),
    // Don't keep a signup form waiting on a slow check.
    signal: AbortSignal.timeout(5000),
  });

  if (!response.ok) {
    // 400 = bad request, 402 = out of credits, and so on.
    throw new Error(`CampaignKit API returned ${response.status}`);
  }

  const { results = [] } = await response.json();
  const result = results[0]?.result;
  const description = result?.description || [];

  if (description.includes("disposable")) {
    return { allow: false, reason: "disposable" };
  }
  // A blacklisted local part is reported instead of the disposable flag,
  // and "invalid" means mail to the address will bounce.
  if (description.includes("blacklist") || result?.classifier === "invalid") {
    return { allow: false, reason: result.classifier === "invalid" ? "invalid" : "blacklist" };
  }
  return { allow: true, reason: null };
}

const email = process.argv[2];
if (!email) {
  console.error("Usage: node check-disposable.mjs someone@example.com");
  process.exit(1);
}

try {
  const decision = await checkSignupEmail(email);
  console.log(JSON.stringify(decision));
} catch (err) {
  // Fail open: if the check can't run, let the signup through.
  console.error(`Validation failed: ${err.message}`);
  console.log(JSON.stringify({ allow: true, reason: "check_failed" }));
}

In your app, import checkSignupEmail into the signup handler (and drop the command-line part at the bottom). When allow is false with reason disposable, show a message such as "Please use a permanent email address," rather than failing silently.

The script fails open: if the API is unreachable, times out or returns an error, the signup goes through. That keeps an outage from locking out real users. If you'd rather fail closed, return { allow: false } in the catch block instead.

Test it

Run the script with a disposable address, then with one you know is real:

CAMPAIGNKIT_API_KEY=your_key node check-disposable.mjs jane@mailinator.com
# {"allow":false,"reason":"disposable"}

CAMPAIGNKIT_API_KEY=your_key node check-disposable.mjs you@yourcompany.com
# {"allow":true,"reason":null}

For a production example, our Auth0 pre-user registration action uses the same disposable check to block signups in Auth0, with a user-facing error message and a 5-second timeout. The full request and response reference is in the email validation API docs.

Troubleshooting and limitations

Can a disposable email checker miss a new domain or flag a real one? Yes, both can happen with any list-based check, including ours. Here's where and what to do about it.

A brand-new disposable domain isn't flagged. New disposable domains keep appearing, and a domain can only be matched once it's on one of the lists. The 6-hour refresh shortens the gap between a domain being listed and our validator knowing about it, but it doesn't remove it. If signups from a domain you suspect keep getting through, a confirmation email before activation catches addresses nobody reads.

A custom or self-hosted domain is used like a burner. Detection is domain-based. Someone who runs their own domain and creates throwaway addresses on it looks like any other private domain, so it won't get the disposable flag.

A real user's domain is flagged. If a domain ends up on a disposable list, every address on it gets the flag, whoever uses it. If a legitimate user reports this, you can allow that domain in your own signup code. The GitHub blocklist also takes corrections through issues and pull requests.

Subdomains aren't matched automatically. The match is exact on the domain. An address at a subdomain of a listed domain is only flagged if that subdomain is listed too.

The flag is missing even though the domain is disposable. A few results skip the disposable flag by design:

  • If the domain doesn't exist or has no MX record, the result is invalid with domain in description. Mail to it bounces anyway.
  • If the mailbox check fails, the result is invalid with mailbox.
  • If the local part (before the @) is blacklisted, you get blacklist instead of disposable.
  • If validation takes longer than 10 seconds, the result comes back with score 6 and verifyRejected, without the disposable check.

The Node.js example above blocks the first three. A timed-out result is classified valid, so the example lets it through; a confirmation email at signup covers that gap.

Catch-all is not the same as disposable. A catch-all domain accepts mail for any address, so the mailbox can't be confirmed. That's a separate check with its own flag, and plenty of catch-all domains belong to ordinary companies. Don't treat a catch-all result as a burner.

Next step

To check an address right now, use the free disposable email checker in our email verifier. There's no signup, and you get 5 free validations per hour.

To block disposable signups automatically, start with 200 free validations, no credit card required. After that, credits start at $6 for 2,500 emails and never expire. Disposable email detection is included on every plan.

Join 1,000+ CompaniesImproving Email Deliverability

Start with 200 free validations. Upgrade only when you're ready.

No credit card required • Cancel anytime