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

@indiekitai/throttled

v0.1.0

Published

Rate limiting for TypeScript/Node.js - 5 algorithms, memory & Redis stores

Readme

@indiekit/throttled

npm version license tests

Lightweight, zero-dependency rate limiting for TypeScript/Node.js.

  • 5 algorithms — Token Bucket, Leaky Bucket, Fixed Window, Sliding Window, GCRA
  • 2 stores — Memory (built-in LRU) and Redis (via ioredis)
  • Express middleware — drop-in 429 handling with standard headers
  • MCP server — expose rate limiting as AI-agent tools
  • CLI — test rate limits from the terminal with JSON output
  • TypeScript-first — full type exports, JSDoc on all public APIs

Install

npm install @indiekit/throttled

For Redis support:

npm install @indiekit/throttled ioredis

Quick Start

import { Throttled } from '@indiekit/throttled';

const limiter = new Throttled({
  algorithm: 'token-bucket',
  limit: 10,
  period: '1s',
});

const result = limiter.allow('user:123');
if (result.limited) {
  console.log(`Rate limited. Retry after ${result.state.retryAfter}s`);
} else {
  console.log(`Allowed. ${result.state.remaining} remaining`);
}

Algorithms

Token Bucket

Tokens refill at a steady rate. Good for APIs allowing short bursts.

const limiter = new Throttled({
  algorithm: 'token-bucket',
  limit: 100,    // 100 tokens max
  period: '1m',  // refill 100 tokens per minute
  burst: 150,    // allow bursts up to 150
});

Use when: You want to allow short bursts while enforcing an average rate. Most common choice for API rate limiting.

Leaky Bucket

Requests drain at a fixed rate. Smooths out traffic spikes.

const limiter = new Throttled({
  algorithm: 'leaky-bucket',
  limit: 10,
  period: '1s',
});

Use when: You need smooth, steady throughput — e.g. sending messages to an external API that can't handle bursts.

Fixed Window

Counts requests in fixed time windows. Simple and predictable.

const limiter = new Throttled({
  algorithm: 'fixed-window',
  limit: 1000,
  period: '1h',
});

Use when: Simple counting is enough and you don't mind a burst at window boundaries. Good for billing/quota enforcement.

Sliding Window

Combines current and previous window with weighted counting. Eliminates boundary burst issues.

const limiter = new Throttled({
  algorithm: 'sliding-window',
  limit: 100,
  period: '1m',
});

Use when: You want fixed-window simplicity with better accuracy at window edges.

GCRA (Generic Cell Rate Algorithm)

Single-counter algorithm equivalent to a virtual scheduling approach. Memory-efficient.

const limiter = new Throttled({
  algorithm: 'gcra',
  limit: 10,
  period: '1s',
});

Use when: You want the most memory-efficient algorithm (1 value per key). Great for distributed systems.

Express Middleware

import express from 'express';
import { throttleMiddleware } from '@indiekit/throttled/express';

const app = express();

app.use(throttleMiddleware({
  algorithm: 'sliding-window',
  limit: 100,
  period: '1m',
  keyFn: (req) => req.ip,
}));

app.get('/api/data', (req, res) => {
  res.json({ ok: true });
});

Sets standard headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After (on 429).

Redis Store

import Redis from 'ioredis';
import { Throttled, RedisStore } from '@indiekit/throttled';

const redis = new Redis();
const limiter = new Throttled({
  algorithm: 'token-bucket',
  limit: 100,
  period: '1m',
  store: new RedisStore(redis),
});

CLI

Test rate limits from your terminal:

npx @indiekit/throttled test --algorithm token-bucket --limit 10 --period 1s

Options:

| Flag | Default | Description | |------|---------|-------------| | --algorithm | token-bucket | Algorithm to use | | --limit | 10 | Max requests per period | | --period | 1s | Time period | | --key | test | Rate limit key | | --requests | 1 | Number of requests to simulate | | --cost | 1 | Cost per request |

Output is JSON. Exit code 0 = allowed, 1 = rate limited.

{
  "limited": false,
  "state": {
    "limit": 10,
    "remaining": 9,
    "resetAfter": 1,
    "retryAfter": 0
  }
}

MCP Server

Expose rate limiting as AI-agent tools:

npx @indiekit/throttled mcp

Tools:

  • check_rate — Check if a key is rate limited
  • get_status — Get current state of a key
  • reset — Reset a key's rate limit state

API Reference

new Throttled(options?)

| Option | Type | Default | Description | |--------|------|---------|-------------| | algorithm | AlgorithmType | 'token-bucket' | Algorithm | | limit | number | 60 | Requests per period | | period | PeriodString | '1m' | Period ('10s', '1m', '1h', '1d') | | burst | number | limit | Burst capacity | | store | Store | MemoryStore | Storage backend | | key | string | — | Default key | | cost | number | 1 | Default cost per request |

throttled.allow(key?, cost?): RateLimitResult

Check if a request is allowed, consuming cost from the quota.

throttled.peek(key?): RateLimitState

Peek at current state without consuming quota.

RateLimitResult

interface RateLimitResult {
  limited: boolean;
  state: RateLimitState;
}

RateLimitState

interface RateLimitState {
  limit: number;       // Max requests
  remaining: number;   // Remaining in current window
  resetAfter: number;  // Seconds until full reset
  retryAfter: number;  // Seconds until retry (0 if allowed)
}

Quota Helpers

import { perSec, perMin, perHour, perDay, createQuota, parsePeriod } from '@indiekit/throttled';

const q1 = perSec(10);           // 10 req/sec
const q2 = perMin(100, 150);     // 100 req/min, burst 150
const q3 = perHour(1000);        // 1000 req/hour
const q4 = createQuota({ period: parsePeriod('30s'), limit: 50 });

Express Middleware

import { throttleMiddleware } from '@indiekit/throttled/express';

throttleMiddleware({
  ...throttledOptions,
  keyFn: (req) => req.ip,  // Custom key extraction
});

Comparison

| Feature | @indiekit/throttled | bottleneck | p-limit | rate-limiter-flexible | |---------|:------------------:|:----------:|:-------:|:---------------------:| | Algorithms | 5 | 1 | 0 | 5 | | TypeScript | ✅ native | ❌ @types | ✅ | ❌ @types | | Memory store | ✅ LRU | ✅ | N/A | ✅ | | Redis store | ✅ | ✅ | N/A | ✅ | | Express middleware | ✅ | ❌ | N/A | ✅ | | Zero deps | ✅ | ✅ | ✅ | ❌ | | MCP server | ✅ | ❌ | ❌ | ❌ | | CLI | ✅ | ❌ | ❌ | ❌ | | ESM + CJS | ✅ | ✅ | ✅ | ✅ | | Bundle size | ~5 KB | ~30 KB | ~1 KB | ~50 KB |

bottleneck — Job scheduler/queue with concurrency control. Different use case (job scheduling vs rate limiting).

p-limit — Concurrency limiter, not a rate limiter. Limits parallel execution, not requests per time window.

rate-limiter-flexible — Feature-rich but heavier. Good if you need Mongo/Memcached/Cluster stores. @indiekit/throttled is lighter with better TypeScript and agent tooling.

License

MIT