npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@isdisposable/js

v1.0.7

Published

Detect disposable and temporary email addresses. Open source, zero dependencies, 160k+ domains.

Downloads

10,089

Readme


Install

npm install @isdisposable/js
pnpm add @isdisposable/js
yarn add @isdisposable/js

Usage

import { isDisposable } from '@isdisposable/js';

isDisposable('[email protected]'); // true
isDisposable('[email protected]');      // false

Synchronous. Offline. Zero config. That's it.

Bulk Check

import { isDisposableBulk } from '@isdisposable/js';

const results = isDisposableBulk([
  '[email protected]',
  '[email protected]',
  '[email protected]',
]);
// [true, false, true]

Domain Check

import { isDomainDisposable } from '@isdisposable/js';

isDomainDisposable('mailinator.com'); // true
isDomainDisposable('gmail.com');      // false

API Mode — Real-time Detection

For DNS/MX validation, risk scoring, and domain age analysis:

import { createIsDisposable } from '@isdisposable/js';

const client = createIsDisposable({
  apiKey: 'isd_live_xxxxx', // Get one at https://isdisposable.com
});

const result = await client.check('[email protected]');
// {
//   disposable: true,
//   score: 95,          ← risk score (0-100)
//   reason: "blocklist_match",
//   mx_valid: true,
//   domain_age_days: 3,
//   cached: false
// }

The API adds signals that offline detection can't: MX record validation, self-referencing mail server detection, domain age via RDAP, and username entropy scoring. If the API is unreachable, it automatically falls back to offline detection — your app never breaks.

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | — | API key from isdisposable.com | | apiUrl | string | https://isdisposable.com | API base URL | | timeout | number | 5000 | Request timeout (ms) | | cache | boolean | true | Cache API responses | | cacheTTL | number | 3600 | Cache duration (seconds) |

const results = await client.checkBulk([
  '[email protected]',
  '[email protected]',
]);

Framework Integrations

'use server';
import { isDisposable } from '@isdisposable/js';

export async function signup(formData: FormData) {
  const email = formData.get('email') as string;

  if (isDisposable(email)) {
    return { error: 'Please use a real email address' };
  }

  // Continue with signup...
}
import express from 'express';
import { isDisposable } from '@isdisposable/js';

const app = express();
app.use(express.json());

app.post('/signup', (req, res, next) => {
  if (isDisposable(req.body.email)) {
    return res.status(400).json({ error: 'Disposable emails not allowed' });
  }
  next();
});
import { betterAuth } from 'better-auth';
import { isDisposable } from '@isdisposable/js';

export const auth = betterAuth({
  databaseHooks: {
    user: {
      create: {
        before: async (user) => {
          if (isDisposable(user.email)) {
            return false; // Block signup
          }
          return user;
        },
      },
    },
  },
});
import { isDisposable } from '@isdisposable/js';

Deno.serve(async (req) => {
  const { email } = await req.json();

  if (isDisposable(email)) {
    return new Response(
      JSON.stringify({ error: 'Disposable emails not allowed' }),
      { status: 400, headers: { 'Content-Type': 'application/json' } }
    );
  }

  return new Response(JSON.stringify({ ok: true }));
});
import { Injectable, CanActivate, ExecutionContext, BadRequestException } from '@nestjs/common';
import { isDisposable } from '@isdisposable/js';

@Injectable()
export class DisposableEmailGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    if (isDisposable(request.body?.email)) {
      throw new BadRequestException('Disposable emails not allowed');
    }
    return true;
  }
}
import Fastify from 'fastify';
import { isDisposable } from '@isdisposable/js';

const app = Fastify();

app.addHook('preHandler', async (request, reply) => {
  const email = (request.body as any)?.email;
  if (email && isDisposable(email)) {
    return reply.status(400).send({ error: 'Disposable emails not allowed' });
  }
});

How It Works

Email input
    │
    ▼
┌──────────────┐     ┌─────────────────────┐
│  Blocklist   │────▶│  160k+ domains      │
│  (offline)   │     │  O(1) Set lookup     │
└──────┬───────┘     │  < 1ms              │
       │ not found   └─────────────────────┘
       ▼
┌──────────────┐     ┌─────────────────────┐
│  API Mode    │────▶│  DNS/MX validation  │
│  (optional)  │     │  Domain age (RDAP)  │
└──────────────┘     │  Username entropy   │
                     │  Risk score 0-100   │
                     └─────────────────────┘

Offline mode checks against a bundled blocklist of 160,000+ domains. Instant, synchronous, zero network calls.

API mode adds real-time signals: MX record validation, self-referencing mail server detection, domain age analysis, suspicious MX infrastructure matching, and username entropy scoring.

Comparison

| | isDisposable | mailchecker | disposable-email-domains | |---|:---:|:---:|:---:| | Domains | 160,000+ | 55,000 | 5,000 | | Boolean API | ✅ | ✅ | ❌ (raw list) | | Zero deps | ✅ | ❌ | N/A | | TypeScript | ✅ | Partial | N/A | | Hosted API | ✅ | ❌ | ❌ | | Risk scoring | ✅ | ❌ | ❌ | | DNS/MX checks | ✅ | ❌ | ❌ | | Maintained | ✅ | Sporadic | Sporadic |

SDKs

| Language | Package | Status | |----------|---------|--------| | JavaScript/TypeScript | @isdisposable/js | ✅ Stable | | Python | isdisposable | 🔜 Coming soon | | Go | isdisposable-go | 🔜 Coming soon |

API Reference

isDisposable(email: string): boolean

Synchronous. Returns true if the email is from a disposable provider.

isDisposableBulk(emails: string[]): boolean[]

Synchronous. Returns array of booleans matching input order.

isDomainDisposable(domain: string): boolean

Check a domain directly without an email address.

extractDomain(email: string): string | null

Extract and validate the domain from an email address.

createIsDisposable(config): ApiClient

Create an API client with .check(email), .checkBulk(emails), and .clearCache().

Score Calculation (API Mode)

| Signal | Score Impact | |--------|:-----------:| | Domain in blocklist (160k+) | +95 | | Self-referencing MX server | +50 | | MX points to known disposable infra | = 90 | | Suspicious MX IP match | +45 | | No valid MX records | +40 | | Domain registered < 7 days | +30 | | MX pattern match | +20 | | Domain registered < 30 days | +15 | | Username entropy (bot-generated) | +15 | | Plus-addressing detected | +10 | | Valid MX + old domain + clean | = 5 |

Score ≥ 50 = disposable. Capped at 100.

Contributing

Found a domain that should be blocked? Open an issue with the domain name. We review and update the blocklist regularly.

License

MIT — use it in any project, commercial or open-source.