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

username-checker

v0.2.1

Published

TypeScript-first username checking for Node.js, inspired by Sherlock's site intelligence.

Readme

Username Checker

npm version Node.js License: MIT CI

TypeScript-first username checking for Node.js, inspired by Sherlock's site intelligence.

username-checker checks username availability across 478 bundled sites. Use it as a CLI for quick reconnaissance, or as a library inside scripts, apps, and internal tooling.

Table of Contents

Highlights

  • 478 bundled sites derived from Sherlock's site intelligence
  • Concurrent checks with per-domain rate limiting, retry, and timeout controls
  • Memory, file, and hybrid cache modes with configurable TTL
  • Proxy (HTTP, HTTPS, SOCKS4, SOCKS5) and Tor support
  • Three output formats: text, JSON, CSV — with per-username file output
  • Batch API for checking multiple usernames in one pass
  • Live progress callbacks for both single and batch checks
  • Cancellation via AbortSignal
  • Debug diagnostics for request and detection troubleshooting
  • Config file (.usernamerc) and environment variable support

Installation

Requires Node.js 20.10 or later.

Install locally:

npm install username-checker

Install globally:

npm install -g username-checker

After a global install, both commands are available:

username-checker --help
uc --help

CLI Quick Start

Check one username across all enabled sites:

uc octocat

This writes octocat.txt in the current directory. To print to stdout instead:

uc octocat --stdout --no-write

Check specific sites (case-insensitive names):

uc octocat -s github,gitlab,reddit

Check multiple usernames and write per-user reports into a folder:

uc octocat torvalds --output-dir reports

Show live progress during a long run:

uc octocat --verbose

Print machine-readable JSON without writing files:

uc octocat --format json --stdout --no-write

Common CLI Workflows

Developer handle discovery:

uc myhandle -s github,gitlab,stackoverflow,npm

Brand sweep with CSV output:

uc mybrand --format csv -o mybrand.csv

Batch check a team list with per-user reports:

uc alice bob charlie --output-dir team-results

Write sidecar JSON and CSV alongside the primary text report:

uc octocat --json results.json --csv results.csv

CI-friendly stdout JSON (available handles only):

uc candidate --format json --stdout --no-write --available-only

Targeted troubleshooting for one site:

uc octocat -s github --debug --debug-headers --debug-body --no-write

Filter results:

uc octocat --available-only   # show only available
uc octocat --taken-only       # show only taken

Use Tor or a custom proxy:

uc octocat --tor
uc octocat --proxy socks5://127.0.0.1:1080

Include sites normally excluded due to unreliable detection:

uc octocat --include-excluded

CLI Reference

Usage

uc <usernames...> [options]

Site Selection

| Flag | Short | Description | | -------------------- | ----- | ----------------------------------------------- | | --sites <sites> | -s | Comma-separated list of site names to check | | --nsfw | | Include NSFW sites | | --include-excluded | | Include sites marked unreliable in the manifest |

Output

| Flag | Short | Default | Description | | -------------------- | ----- | ------- | ---------------------------------------------------- | | --format <fmt> | -f | text | Primary output format: text, json, or csv | | --output <path> | -o | | Write an aggregate primary report to a specific path | | --output-dir <dir> | | | Write per-username primary reports into a directory | | --stdout | | false | Print the primary output to stdout | | --no-write | | | Disable all file writes | | --json <filename> | | | Write an additional JSON sidecar report | | --csv <filename> | | | Write an additional CSV sidecar report | | --available-only | | false | Only include available results in output | | --taken-only | | false | Only include taken results in output | | --verbose | -v | false | Print live progress to stderr |

Network and Execution

| Flag | Short | Default | Description | | ------------------- | ----- | ------- | ---------------------------------------------- | | --timeout <ms> | -t | 15000 | Request timeout in milliseconds | | --concurrency <n> | -c | 50 | Maximum concurrent requests | | --retries <n> | -r | 2 | Retry attempts on failure | | --proxy <url> | | | HTTP, HTTPS, SOCKS4, or SOCKS5 proxy URL | | --tor | | false | Route requests through Tor on localhost:9050 |

Cache

| Flag | Default | Description | | ------------------- | --------------------------- | ------------------------------------------------- | | --cache <type> | memory | Cache mode: none, memory, file, or hybrid | | --cache-dir <dir> | ./.username-checker-cache | Directory for file-based cache | | --cache-ttl <ms> | 3600000 | Cache time-to-live in milliseconds (1 hour) |

Debugging

| Flag | Default | Description | | ---------------------- | ------- | ----------------------------------------------- | | --debug | false | Print per-site debug details to stderr | | --debug-headers | false | Include response headers in debug output | | --debug-body | false | Include response bodies in debug output | | --debug-max-body <n> | 2000 | Maximum characters to capture per response body |

Configuration

| Flag | Description | | ------------- | ----------------------------------------------------------------- | | --no-config | Disable config file and environment variable loading for this run |

Output Behaviour

The CLI is file-oriented by default:

  • One username → one primary report file (<username>.<ext>)
  • Multiple usernames → one primary file per username, unless --output is given
  • --output-dir places per-username files inside that directory
  • --stdout prints the primary report to stdout; use with --no-write to suppress file creation
  • --json and --csv write sidecar reports alongside the primary output
  • Debug output always goes to stderr so stdout pipelines remain machine-readable

Configuration

Configuration is layered. Later sources override earlier ones for the same key:

  1. Built-in defaults
  2. Config file
  3. Environment variables
  4. Explicit CLI flags (highest priority)

Config File

Config files are searched in this order:

  1. Path in USERNAME_CHECKER_CONFIG env var
  2. ./.usernamerc
  3. ./.usernamerc.json
  4. ~/.usernamerc
  5. ~/.usernamerc.json
  6. $XDG_CONFIG_HOME/usernamerc.json
  7. $XDG_CONFIG_HOME/username-checker/usernamerc.json

Config files use JSON format:

{
  "timeout": 15000,
  "maxConcurrency": 50,
  "retries": 2,
  "includeNSFW": false,
  "includeExcluded": false,
  "format": "text",
  "defaultSites": ["GitHub", "GitLab", "Reddit"],
  "cache": {
    "type": "hybrid",
    "ttl": 3600000,
    "dir": "./.username-checker-cache"
  }
}

defaultSites restricts which sites are checked when --sites is omitted.

Environment Variables

| Variable | Description | | ---------------------------------- | --------------------------------------- | | USERNAME_CHECKER_TIMEOUT | Request timeout in ms | | USERNAME_CHECKER_CONCURRENCY | Alias for maxConcurrency | | USERNAME_CHECKER_MAX_CONCURRENCY | Maximum concurrent requests | | USERNAME_CHECKER_RETRIES | Retry attempts | | USERNAME_CHECKER_NSFW | true/false — include NSFW sites | | USERNAME_CHECKER_EXCLUDED | true/false — include excluded sites | | USERNAME_CHECKER_TOR | true/false — use Tor | | USERNAME_CHECKER_PROXY | Proxy URL | | USERNAME_CHECKER_FORMAT | Default output format | | USERNAME_CHECKER_CACHE_TYPE | Cache mode | | USERNAME_CHECKER_CACHE_TTL | Cache TTL in ms | | USERNAME_CHECKER_DEFAULT_SITES | Comma-separated default site list | | USERNAME_CHECKER_CONFIG | Explicit path to config file |

Boolean env vars accept true/1/yes or false/0/no.

Examples:

export USERNAME_CHECKER_TIMEOUT=10000
export USERNAME_CHECKER_DEFAULT_SITES=GitHub,GitLab,Reddit
uc octocat
USERNAME_CHECKER_PROXY=socks5://127.0.0.1:1080 uc octocat --format json --stdout --no-write

Library Quick Start

import { UsernameChecker } from 'username-checker';

const checker = new UsernameChecker();
const results = await checker.check('octocat');

const available = results.filter((r) => r.status === 'available');
console.log(`Available on ${available.length} sites`);

Library Examples

Check with site filter and custom options

import { UsernameChecker } from 'username-checker';

const checker = new UsernameChecker({ timeout: 12000, retries: 1 });
const results = await checker.check('octocat', {
  sites: ['GitHub', 'GitLab', 'npm'],
});

for (const result of results) {
  console.log(result.siteName, result.status, result.url);
}

Live progress during a single-username check

import { UsernameChecker } from 'username-checker';

const checker = new UsernameChecker();
const results = await checker.check('octocat', {
  onProgress: (p) => {
    process.stdout.write(`\r${p.completed}/${p.total} (${p.percentage}%)`);
  },
});
console.log('\nDone');

Batch check multiple usernames

import { UsernameChecker } from 'username-checker';

const checker = new UsernameChecker({ maxConcurrency: 25 });

const batch = await checker.checkBatch(['octocat', 'torvalds'], {
  sites: ['GitHub', 'GitLab'],
  onBatchProgress: (p) => {
    console.log(`${p.currentUsername} — ${p.currentUsernameIndex + 1}/${p.totalUsernames}`, `(${p.totalPercentage}%)`);
  },
});

for (const entry of batch) {
  console.log(entry.username, entry.summary);
}

Cancellation with AbortController

import { UsernameChecker } from 'username-checker';

const checker = new UsernameChecker();
const controller = new AbortController();

// Cancel after 10 seconds
setTimeout(() => controller.abort(), 10_000);

const results = await checker.check('octocat', {
  signal: controller.signal,
});

Proxy and Tor

import { UsernameChecker } from 'username-checker';

// HTTP/HTTPS/SOCKS proxy
const checker = new UsernameChecker({ proxy: 'socks5://127.0.0.1:1080' });

// Tor (requires Tor running on localhost:9050)
const torChecker = new UsernameChecker({ useTor: true });

Cache modes

import { UsernameChecker } from 'username-checker';

// Memory cache (default) — fast, not persistent across runs
const memChecker = new UsernameChecker({ cache: { type: 'memory', ttl: 60_000 } });

// File cache — persists to disk across runs
const fileChecker = new UsernameChecker({
  cache: { type: 'file', ttl: 3_600_000, dir: './.username-checker-cache' },
});

// Hybrid — memory for speed, file for persistence
const hybridChecker = new UsernameChecker({
  cache: { type: 'hybrid', ttl: 3_600_000 },
});

// Disable caching entirely
const noCache = new UsernameChecker({ cache: false });

Debug diagnostics

import { UsernameChecker } from 'username-checker';

const checker = new UsernameChecker();
const results = await checker.check('octocat', {
  sites: ['GitHub'],
  debug: {
    includeHeaders: true,
    includeBody: true,
    maxBodyLength: 1500,
  },
});

const r = results[0];
console.log(r.diagnostics); // probeUrl, requestMethod, detectionMethods, followRedirects, finalUrl
console.log(r.debug); // statusCode, responseHeaders, responseBody

Custom site repository

import { ManifestRepository, UsernameChecker } from 'username-checker';

const repository = ManifestRepository.fromRawData({
  Example: {
    name: 'Example',
    url: 'https://example.com/{}',
    urlMain: 'https://example.com/',
    errorType: 'status_code',
  },
});

const checker = new UsernameChecker({ repository });
const results = await checker.check('octocat');
console.log(results);

Error handling

import { UsernameChecker } from 'username-checker';

const checker = new UsernameChecker();

try {
  const results = await checker.check('octocat', {
    sites: ['GitHub', 'UnknownSite'],
  });
} catch (err) {
  // Thrown when a site name cannot be resolved.
  // The error message includes fuzzy suggestions:
  //   "Unknown site: "UnknownSite" (did you mean GitHub?)"
  console.error(err.message);
}

try {
  await checker.check(''); // throws: "Invalid username: Username cannot be empty"
} catch (err) {
  console.error(err.message);
}

TypeScript type imports

import type {
  CheckResult,
  CheckOptions,
  CheckProgress,
  BatchCheckResult,
  BatchCheckOptions,
  BatchCheckProgress,
  CacheOptions,
  DebugOptions,
  CheckDiagnostics,
  CheckDebugData,
  ErrorCategory,
  AvailabilityStatus,
} from 'username-checker';

Library Reference

UsernameChecker

new UsernameChecker(options?: CheckOptions)

Constructor options

| Option | Type | Default | Description | | ----------------- | ----------------------- | ---------------- | ------------------------------------------ | | timeout | number | 15000 | Request timeout in ms | | maxConcurrency | number | 50 | Maximum concurrent requests | | retries | number | 2 | Retry attempts on failure | | includeNSFW | boolean | false | Include NSFW sites by default | | includeExcluded | boolean | false | Include excluded sites by default | | useTor | boolean | false | Route requests through Tor | | proxy | string | | HTTP, HTTPS, SOCKS4, or SOCKS5 proxy URL | | cache | CacheOptions \| false | false | Cache configuration, or false to disable | | repository | SiteRepository | bundled manifest | Custom site repository |

checker.check(username, options?)

Check a single username across sites. Returns Promise<CheckResult[]>.

Throws if the username is invalid or if any specified site name cannot be resolved.

| Option | Type | Description | | ----------------- | ---------------------------- | -------------------------------------------- | | sites | string[] | Limit to these site names (case-insensitive) | | includeNSFW | boolean | Override instance-level setting | | includeExcluded | boolean | Override instance-level setting | | onProgress | (p: CheckProgress) => void | Called after each site completes | | signal | AbortSignal | Cancellation signal | | debug | DebugOptions | Enable diagnostic data on results |

checker.checkBatch(usernames, options?)

Check multiple usernames in sequence. Returns Promise<BatchCheckResult[]>.

Accepts all options from check(), plus:

| Option | Type | Description | | ----------------- | --------------------------------- | ----------------------------------------------------------------- | | onBatchProgress | (p: BatchCheckProgress) => void | Called at the start of each username and on every site completion |

checker.checkSite(username, siteKey, config?, signal?, debug?)

Check a single username against a single named site. Returns Promise<CheckResult>. Useful for targeted checks or custom retry loops.

CheckResult

interface CheckResult {
  site: string; // canonical site key
  siteName: string; // human-readable name
  url: string; // profile URL for this username
  status: AvailabilityStatus; // 'available' | 'taken' | 'invalid' | 'unknown' | 'error'
  httpStatus?: number; // raw HTTP status code
  responseTime: number; // request duration in ms
  errorCategory: ErrorCategory;
  errorMessage?: string;
  diagnostics?: CheckDiagnostics; // present when debug is enabled
  debug?: CheckDebugData; // present when debug is enabled
}

AvailabilityStatus values

| Value | Meaning | | ----------- | ---------------------------------------------------------- | | available | Username appears to be free on that site | | taken | Username appears to exist on that site | | invalid | Username does not match the site's own format constraints | | unknown | Request succeeded but detection was inconclusive | | error | Transport, rate-limit, block, timeout, or upstream failure |

CheckDiagnostics (when debug is enabled)

interface CheckDiagnostics {
  probeUrl: string;
  requestMethod: 'GET' | 'POST' | 'HEAD' | 'PUT';
  detectionMethods: DetectionMethod[];
  followRedirects: boolean;
  finalUrl?: string;
  errorCodes?: number[];
}

CheckDebugData (when debug is enabled)

interface CheckDebugData {
  statusCode?: number;
  responseHeaders?: Record<string, string>;
  responseBody?: string;
}

ErrorCategory

The errorCategory field distinguishes the root cause when status is error:

| Value | Meaning | | ------------------ | ------------------------------------------------------------------ | | timeout | Request exceeded the configured timeout | | rate_limited | Site returned HTTP 429 or equivalent | | blocked | WAF, Cloudflare challenge, or bot detection blocked the request | | server_error | Site returned a 5xx response | | connection_error | DNS, TCP, or TLS failure | | unknown | Error could not be classified | | none | No error — result is available, taken, invalid, or unknown |

BatchCheckResult

interface BatchCheckResult {
  username: string;
  normalizedUsername: string; // trimmed and lowercased form
  results: CheckResult[];
  summary: {
    total: number;
    available: number;
    taken: number;
    errors: number;
  };
}

BatchCheckProgress

interface BatchCheckProgress {
  currentUsername: string;
  currentUsernameIndex: number; // 0-based
  totalUsernames: number;
  usernamePercentage: number; // 0–100 for the current username's sites
  totalPercentage: number; // 0–100 across all usernames and sites
  siteProgress?: CheckProgress; // site-level progress for the current username
}

CheckResultCache

CheckResultCache is a standalone cache you can use independently of UsernameChecker, for example to pre-warm entries or share a single cache across multiple checker instances.

import { CheckResultCache } from 'username-checker';

const cache = new CheckResultCache({ type: 'hybrid', ttl: 3_600_000 });

// Query
const cached = cache.get('GitHub', 'octocat'); // CheckResult | null

// Store
cache.set('GitHub', 'octocat', result);

// Check existence without reading
cache.has('GitHub', 'octocat'); // boolean

// Inspect cache size
const { type, memorySize, fileSize } = cache.stats();

// Clear all entries
cache.clear();

CacheOptions

| Option | Type | Default | Description | | --------- | -------------------------------- | --------------------------- | ------------------------------------ | | type | 'memory' \| 'file' \| 'hybrid' | 'memory' | Cache backend | | ttl | number | 3600000 | Time-to-live in ms (1 hour) | | dir | string | ./.username-checker-cache | Directory for file-based caches | | maxSize | number | 1000 | Maximum entries for the memory cache |

ManifestRepository

ManifestRepository implements SiteRepository and provides case-insensitive site resolution with fuzzy suggestions.

import { ManifestRepository } from 'username-checker';

// Use the pre-built repository for the bundled manifest
import { defaultManifestRepository } from 'username-checker'; // re-exported from Sites.ts

Static factories

// From Sherlock-format raw JSON data
const repo = ManifestRepository.fromRawData({
  GitHub: {
    name: 'GitHub',
    url: 'https://github.com/{}',
    urlMain: 'https://github.com/',
    errorType: 'status_code',
  },
});

// From an array of SiteConfig objects
const repo = ManifestRepository.fromSiteConfigs([
  { name: 'GitHub', url: 'https://github.com/{}', urlMain: 'https://github.com/', errorType: 'status_code' },
]);

Instance methods

repo.has('GitHub'); // boolean — case-insensitive
repo.get('github'); // SiteConfig | undefined
repo.resolveKey('GITHUB'); // 'GitHub' | undefined (canonical key)
repo.suggestKeys('githb', 3); // string[] — fuzzy suggestions
repo.count(); // number of non-excluded sites
repo.count({ includeExcluded: true }); // total including excluded
repo.filter({ includeNSFW: true }); // SiteEntry[] — { key, config }
repo.resolveKeys(['GitHub', 'typo']); // { resolvedKeys: string[], missing: [...] }

ConfigLoader

ConfigLoader exposes the same configuration loading pipeline the CLI uses internally.

import { ConfigLoader } from 'username-checker';

const config = ConfigLoader.loadConfig();
// Returns a ConfigOptions object merged from config file + env vars.
// CLI flags are not included — apply those on top manually.

console.log(config.timeout);
console.log(config.defaultSites);

How Detection Works

Each site entry in the manifest specifies one of three detection strategies:

| Strategy | Description | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | status_code | The site returns a distinct HTTP status for missing profiles (typically 404). A 2xx indicates the username is taken. | | message | The site always returns 200. Detection compares the response body against a known error string present only when the profile is missing. If the string is absent, the username is taken. | | response_url | The site redirects missing profiles to a known URL. Detection checks the final URL after following redirects. |

Sites may declare multiple strategies as an array; all must pass for a taken determination. Sites marked isExcluded are in the manifest but excluded from default runs because their detection is known to produce false positives.

Site Catalog

The bundled manifest contains 478 sites (419 enabled, 38 excluded, 19 NSFW). See SITES.md for the full list with detection strategy and URL for each entry.

To regenerate the manifest from the upstream Sherlock project:

node scripts/sync-sites.mjs

Operational Notes

  • Site name matching is case-insensitive everywhere: --sites Github and --sites github are equivalent.
  • When a site name cannot be resolved, the error message includes fuzzy suggestions.
  • Multiple usernames are processed in order; onBatchProgress fires at the start of each username.
  • Runtime depends heavily on network conditions, site selection, timeout, retries, and concurrency.
  • The CLI's debug output always goes to stderr so stdout can remain machine-readable.
  • The --cache default is memory, which only persists for the lifetime of a single CLI invocation. Use file or hybrid for cross-run persistence.

Attribution

This project is independent software, but it builds on upstream site intelligence from the Sherlock project:

  • Sherlock repository: https://github.com/sherlock-project/sherlock
  • Published site manifest: https://data.sherlockproject.xyz

The bundled site catalog, schema checks, and false-positive exclusions are synced from Sherlock sources and adapted to this package's TypeScript runtime and CLI/library API. username-checker is not the Sherlock CLI, and it is not affiliated with or endorsed by the Sherlock maintainers.