The disposable email npm package for Node.js

Install the mailsurity npm package to detect disposable email addresses from Node, or skip it and call the same HTTP API directly with curl.

Last updated July 25, 2026.

Do you even need the npm package to detect disposable emails?

Not necessarily. The Mailsurity disposable email detection API is a plain POST over HTTP, so curl, fetch, or whatever HTTP client your language already gives you works fine without installing anything. If that's you, see the raw request at the bottom of this page. This guide is for the case where you'd rather use the official mailsurity npm package than write and maintain that HTTP call yourself: a small, server-side wrapper with typed results, a timeout, and a fail-open default baked in.

Step 1: install the npm package

The package is zero-dependency and ships types for TypeScript out of the box.

terminal
npm i mailsurity

Step 2: create a server-side client

Construct one Mailsurity client with your API key and reuse it. The constructor throws immediately if it's ever evaluated in a browser context, so a stray import into a Client Component fails loudly instead of leaking your key.

Pass baseUrl: 'https://www.mailsurity.com' explicitly, as the example below does. The SDK's own default is the apex host (mailsurity.com), which 308-redirects to www.mailsurity.com — and that cross-host redirect strips the Authorization header, so a client built without an explicit baseUrl will 401 on every request and, under the default fail-open behavior below, silently resolve to { isDisposable: null, source: 'error' } instead of ever returning a real verdict. This is the same www gotcha the API reference calls out for raw HTTP calls — the fix here is filed as a package bug, since the default should already point at the www host.

lib/mailsurity.ts
import { Mailsurity } from 'mailsurity';

// One shared, server-side client. The API key is secret, never import
// this into a Client Component — the SDK throws if it detects a browser.
//
// Pass baseUrl explicitly: the SDK's built-in default is the apex host
// (mailsurity.com), which 308-redirects to www.mailsurity.com, and that
// cross-host redirect strips the Authorization header. Without this, every
// call below would silently fail open instead of returning a real verdict.
export const mailsurity = new Mailsurity({
  apiKey: process.env.MAILSURITY_API_KEY!,
  baseUrl: 'https://www.mailsurity.com',
});

The full set of constructor options:

OptionTypeDefaultNotes
apiKeystringRequired. Your Mailsurity bearer token.
baseUrlstringhttps://mailsurity.comOverride the API origin. Pass the www host explicitly — see above.
timeoutMsnumber2000Per-request timeout before the SDK aborts and fails open (or throws, if failOpen is false).
failOpenbooleantrueWhen true, errors resolve to a safe { isDisposable: null, source: 'error' } instead of throwing. See below.

Step 3: call checkEmail() for a disposable-email verdict

checkEmail(email) always returns email, isDisposable, and source — which layer of the detection cascade produced the verdict:

sourceMeaning
allowlistMatched one of your team allow rules, forced to legitimate.
denylistMatched one of your team deny rules, forced to disposable.
legit-listA curated, globally known-legitimate domain (e.g. gmail.com).
listMatched the curated global disposable-domains list.
cacheA cached result from a recent live detection of this domain.
heuristicResolved live from MX, entropy, brand similarity, and cluster reputation.
llmA borderline case escalated to the AI classifier.

The richer fields only show up on a cache, heuristic, or llm verdict: classification (disposable, legitimate, risky, or unknown), a confidence score from 0 to 1, and a human-readable reason. A list, legit-list, or team allow/deny hit skips straight to a verdict without them, as the two response shapes in the example below show.

anywhere on your server
import { mailsurity } from '@/lib/mailsurity';

const result = await mailsurity.checkEmail('user@mailinator.com');

// mailinator.com is already in Mailsurity's tracked disposable-domain
// list, so this is a fast "list" hit — just the essentials, no
// classification fields:
// {
//   email: 'user@mailinator.com',
//   isDisposable: true,
//   source: 'list',
// }

// A domain Mailsurity hasn't seen before instead gets a full verdict from
// the heuristic/LLM layer, e.g. (the "reason" fragments below are the
// literal ones the scorer emits, joined with "; "):
// {
//   email: 'user@fresh-lookalike-domain.example',
//   isDisposable: true,
//   classification: 'disposable',
//   confidence: 0.82,
//   reason: 'mx cluster suspicious; similar to gmail',
//   source: 'heuristic',
// }

Or just the boolean: isDisposable()

Most integrations only care about one thing: block or allow. For that, isDisposable(email) is a convenience wrapper around checkEmail() that resolves straight to a boolean.

app/api/sign-up/route.ts
import { mailsurity } from '@/lib/mailsurity';

export async function POST(req: Request) {
  const { email } = await req.json();

  if (await mailsurity.isDisposable(email)) {
    return Response.json(
      { error: 'Please use a permanent email address.' },
      { status: 422 },
    );
  }

  // ...create the user as normal
  return Response.json({ ok: true });
}

Fail-open by default: an outage should never block a signup

A transient outage on our end should never block a real signup. By default the SDK fails open: on timeout, network error, or any non-2xx response (a 5xx, a 402 for out of credits, or even a misconfigured 401/403 API key), checkEmail() resolves to { isDisposable: null, source: 'error' } instead of throwing, and isDisposable() resolves to false. A 401/403 is still logged loudly via console.error, so a broken integration gets noticed; it just doesn't throw by default, same as every other error. The per-request timeout defaults to 2000ms and is configurable via timeoutMs. If you'd rather fail closed and handle every error yourself, pass failOpen: false and catch the thrown MailsurityError (it carries the HTTP status when there is one).

fail-closed variant
import { Mailsurity, MailsurityError } from 'mailsurity';

const strict = new Mailsurity({
  apiKey: process.env.MAILSURITY_API_KEY!,
  baseUrl: 'https://www.mailsurity.com',
  failOpen: false, // throw instead of resolving to a safe default
});

try {
  const result = await strict.checkEmail(email);
} catch (err) {
  if (err instanceof MailsurityError) {
    console.error(`Mailsurity check failed (${err.status}): ${err.message}`);
  }
  throw err;
}

npm package facts: version, license, and runtime support

mailsurity is at version 0.1.0, MIT-licensed, and has zero runtime dependencies. Its engines field requires Node.js 18 or newer, but nothing in the client is Node-specific: it's built entirely on fetch and AbortController, both standard Web APIs, so it should also run fine in edge/serverless runtimes — that combination just isn't verified yet on an edge runtime specifically, so treat it as likely-compatible rather than confirmed until you've tried it.

Skip the SDK entirely: call the disposable-email API with curl

Since the SDK is only a thin wrapper, you can call the same endpoint directly whenever that's more convenient: a serverless runtime without npm, a non-Node backend, or just a quick test. Same authentication, same response shape:

terminal
curl -X POST https://www.mailsurity.com/api/check-email \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"user@mailinator.com"}'

You get the same 200 OK verdict and the same fields without installing a package, so pick whichever fits the runtime you're calling it from.

Where else this SDK shows up

The same lib/mailsurity.ts client (one shared, server-side instance) is what the Next.js signup guide and the Vercel deploy guide build on. Start with whichever one matches your stack, then adapt the lib/mailsurity.ts client shown here.

FAQ

Why does checkEmail() return { isDisposable: null, source: 'error' } even though my API key is correct?

This almost always means the client is hitting the apex host instead of www. mailsurity.com 308-redirects to www.mailsurity.com, and that cross-host redirect drops the Authorization header, so the redirected request comes back 401 — which the SDK's fail-open default quietly turns into a null-source result instead of a visible error. Fix it by passing baseUrl: 'https://www.mailsurity.com' to the Mailsurity constructor, as shown in Step 2 above.

What happens to checkEmail() during a Mailsurity outage or a billing gap?

It resolves instead of throwing. On a timeout, network error, any 5xx, or a 402 (out of credits), checkEmail() resolves to { isDisposable: null, source: 'error' } and isDisposable() resolves to false, so a real signup is never blocked by our downtime. A 401/403 auth error still goes through the same fail-open path, but it's also logged via console.error so a broken API key gets noticed. Pass failOpen: false if you'd rather catch a thrown MailsurityError yourself.

Does the mailsurity package work on edge or serverless runtimes?

It's built entirely on fetch and AbortController — standard Web APIs, not Node-specific ones — so it should run the same on an edge runtime as on a traditional Node server. That combination just hasn't been verified yet against an edge runtime specifically; the package's own test suite mocks global fetch rather than running against a real edge deployment. The engines field only requires Node.js >=18, which is what's exercised so far.

What version of the mailsurity package is this guide for, and is it free?

mailsurity is at version 0.1.0 on npm, MIT-licensed, and has zero runtime dependencies. The package itself has no separate cost — you pay for usage through your Mailsurity API credits (500 free, no card required), not for the SDK.

See how many disposable signups slip past your current setup

Paste a list of your signups. Mailsurity shows how many a standard blocklist misses that we catch by mail-server fingerprint. No signup needed; the part before the @ never leaves your browser.

Need the full field and error reference? See the API reference.