On this page

Configuration Options

Complete reference for all hitlimit configuration options.

Basic Options

OptionTypeDefaultDescription
limitnumber100Maximum requests per window
windowstring | number'1m'Time window (e.g., '1m', '1h', '1d')
keyfunctionreq.ipFunction to extract rate limit key
storeStorememoryStore()Storage backend

Advanced Options

OptionTypeDefaultDescription
skipfunctionundefinedSkip rate limiting for certain requests
headersbooleantrueInclude rate limit headers
statusCodenumber429HTTP status when rate limited
responseobject | function-Custom response body

Ban Options

Automatically ban clients that repeatedly exceed rate limits. After the configured number of violations, the client is blocked for the ban duration without consuming store hits.

OptionTypeDefaultDescription
ban.thresholdnumber-Number of rate limit violations before triggering a ban
ban.durationstring | number-How long the ban lasts (e.g., '1h', '30m', or milliseconds)
hitlimit({
  limit: 10,
  window: '1m',
  ban: {
    threshold: 5,   // Ban after 5 violations
    duration: '1h'    // Ban lasts 1 hour
  }
})

When a client is banned, the response includes X-RateLimit-Ban: true and X-RateLimit-Ban-Expires headers, plus banned: true in the response body.

Group Options

Group multiple clients under a shared rate limit key prefix. Useful for per-API-key limits, per-tenant throttling, or per-organization quotas.

OptionTypeDefaultDescription
groupstring | functionundefinedStatic group name or function returning a group ID per request
// Static group — all keys prefixed with "api:"
hitlimit({ group: 'api', limit: 100, window: '1m' })

// Dynamic group — per API key
hitlimit({
  limit: 1000,
  window: '1h',
  group: (req) => req.headers['x-api-key'] || 'anonymous'
})

Store Configuration

hitlimit supports four storage backends. Memory is the default and fastest option:

StoreBest ForThroughput
memoryStore()Single-process, highest performance4.08M ops/s
sqliteStore()Persistence across restarts404K ops/s
mongoStore()Teams already using MongoDB2.2K ops/s
redisStore()Distributed / multi-server deploymentsNetwork-bound
postgresStore()Teams already using PostgresNetwork-bound
StoreBest ForThroughput
memoryStore()Single-process, highest performance5.57M ops/s
sqliteStore()Persistence across restarts (bun:sqlite)372K ops/s
redisStore()Distributed / multi-server deploymentsNetwork-bound
postgresStore()Teams already using PostgresNetwork-bound
import { hitlimit, memoryStore } from '@joint-ops/hitlimit'
import { sqliteStore } from '@joint-ops/hitlimit/stores/sqlite'
import { redisStore } from '@joint-ops/hitlimit/stores/redis'

// Memory (default) — no config needed
hitlimit({ limit: 100, window: '1m' })

// SQLite — persistent storage
hitlimit({
  limit: 100,
  window: '1m',
  store: sqliteStore({ path: './limits.db' })
})

// Redis — distributed
hitlimit({
  limit: 100,
  window: '1m',
  store: redisStore({ url: 'redis://localhost:6379' })
})
import { hitlimit, sqliteStore, redisStore } from '@joint-ops/hitlimit-bun'

// Memory (default) — no config needed
hitlimit({ limit: 100, window: '1m' })

// SQLite — persistent storage via bun:sqlite
hitlimit({
  limit: 100,
  window: '1m',
  store: sqliteStore({ path: './limits.db' })
})

// Redis — distributed
hitlimit({
  limit: 100,
  window: '1m',
  store: redisStore({ url: 'redis://localhost:6379' })
})

Full Example

hitlimit({
  limit: 100,
  window: '1m',
  key: (req) => req.headers['x-api-key'] || req.ip,
  store: sqliteStore({ path: './limits.db' }),
  skip: (req) => req.path === '/health',
  headers: true,
  statusCode: 429,
  response: { error: 'Too many requests' },
  ban: { threshold: 5, duration: '1h' },
  group: (req) => req.headers['x-api-key'] || 'default'
})