node-rate-limiter

A dependency-free rate limiter for Node.js and the browser built on a hierarchical token bucket.

Library
npm
v3.0.0
1,564stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
35/100Needs Attention
Development Activity0
Maintenance0
Community52
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
73/100Good
Architecture75
Code Quality78
Innovation55
Learning Curve85

limiter is a small, dependency-free rate-limiting library for Node.js and the browser, published on npm as limiter. It exposes two classes: TokenBucket, a low-level primitive that drips tokens into a bucket at a configurable fill rate up to a configurable burst size, and RateLimiter, a higher-level wrapper built on top of it that adds a hard cap on how many tokens can be spent per interval — the shape needed to comply with API rules like “150 requests per hour.”

Both classes expose an async removeTokens() that resolves once enough capacity exists (optionally with fireImmediately to return -1 immediately instead of waiting), plus a synchronous tryRemoveTokens() for callers that want an immediate true/false without waiting. Token buckets can be chained through a parentBucket option, letting an application enforce a global ceiling and a per-resource ceiling with two bucket instances instead of hand-rolled bookkeeping.

The library has been rewritten several times over its life — a 2.0 move to TypeScript and promises, and a 3.0 rewrite that dropped a small external timing dependency in favor of built-in performance.now()/process.hrtime and shipped separate CJS and ESM builds. It has no runtime dependencies, is widely used to throttle outbound API clients, web crawlers, and queue consumers, and works identically in Node and browser bundlers.

What You Get

  • RateLimiter class - interval-based limiting (tokensPerInterval + interval) matching typical “N requests per hour/minute” API quotas.
  • TokenBucket class - a lower-level, hierarchical bucket primitive with independent bucketSize (burst) and tokensPerInterval (drip rate) controls.
  • Async and sync removal APIs - removeTokens() returns a promise that resolves when capacity is available (or -1 immediately with fireImmediately: true); tryRemoveTokens() returns a boolean synchronously with no waiting.
  • Parent/child bucket chaining - a parentBucket option lets one bucket draw from another, enabling global-plus-per-resource limiting without extra coordination code.
  • Dual CJS/ESM builds with zero runtime dependencies - published with separate require/import entry points and no third-party packages to audit or update.

Common Use Cases

  • Throttling outbound API clients - wrapping calls to third-party APIs (e.g. search or social APIs with hourly quotas) so a client never exceeds the documented rate limit.
  • Web crawling - pacing requests to a target site to avoid tripping its own rate limiting or getting IP-banned.
  • Inbound request throttling - rejecting or delaying requests in a server handler once a per-IP or per-key quota is exhausted, using tryRemoveTokens() to return 429 responses immediately.
  • Byte-level throughput limiting - using TokenBucket directly (removing byte counts instead of request counts) to cap sustained bandwidth on a data stream while allowing short bursts.

Under The Hood

Architecture The library is a thin two-class hierarchy in src/: TokenBucket.ts implements the core drip/remove primitive plus optional parent-bucket delegation, RateLimiter.ts composes a single internal TokenBucket and layers an interval-boundary check (curIntervalStart/tokensThisInterval) on top of it to bound total spend per interval, and clock.ts centralizes timestamp generation and a promise-based wait() helper so both classes share one timing source. index.ts re-exports both classes as the package’s public surface — there is no internal state beyond these three files, so a consumer’s mental model maps directly onto the two exported classes.

Tech Stack Written in TypeScript targeting ES2019, built with tsc into separate dist/cjs and dist/esm outputs (selected via package.json exports/main/module/browser fields), with a small create-package-json.js script writing the per-format package.json markers needed for Node’s dual-package resolution. Tests run under Jest with Babel-based transforms (babel.config.cjs, @babel/preset-typescript) rather than ts-jest. Linting is ESLint 9 (flat config) with typescript-eslint and eslint-plugin-prettier; Yarn is the package manager, pinned via packageManager. No runtime dependencies are declared.

Code Quality Both core classes have dedicated Jest test suites (TokenBucket.test.ts, RateLimiter.test.ts) that assert on real elapsed time with a small epsilon tolerance, covering interval-string validation, burst behavior, and drip timing rather than just mocking the clock. Types are explicit throughout (RateLimiterOpts, TokenBucketOpts, the Interval union), constructors validate inputs and throw on invalid intervals or oversized token requests, and CI runs the lint and test suite across three Node major versions via GitHub Actions.

API Design The public surface is deliberately small — two classes, one shared Interval type — and every constructor takes a single options object rather than positional arguments, which keeps call sites self-documenting. Both async (removeTokens) and sync (tryRemoveTokens) variants are offered on both classes so callers can pick blocking-wait or immediate-check semantics without wrapping the library themselves, and the README leads with runnable examples for each of the four common patterns (interval limiting, fixed-delay limiting, fire-immediately/429 handling, and raw byte-level throttling).

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search