Flexible rate limiting for your Rack apps
Latest commit af8bdac Jun 1, 2016 @jeremy 1.1.2: fix remaining count



  • Run multiple rate limiters in a single app
  • Scope each rate limit to certain requests: API, files, GET vs POST, etc.
  • Apply each rate limit by request characteristics: IP, subdomain, OAuth2 token, etc.
  • Flexible time window to limit burst traffic vs hourly or daily traffic: 100 requests per 10 sec, 500 req/minute, 10000 req/hour, etc.
  • Fast, low-overhead implementation in memcache using counters for discrete timeslices: timeslice = window * ceiling(current time / window) memcache.incr(counter for timeslice)


Takes a block that classifies requests for rate limiting. Given a Rack env, return a string such as IP address, API token, etc. If the block returns nil, the request won't be rate-limited. If a block is not given, all requests get the same limits.

Required configuration:

  • rate: an array of [max requests, period in seconds]: [500, 5.minutes]

and one of

  • cache: a Dalli::Client instance
  • redis: a Redis instance
  • counter: Your own custom counter. Must respond to #increment(classification_string, end_of_time_window_timestamp) and return the counter value after increment.

Optional configuration:

  • name: name of the rate limiter. Defaults to 'HTTP'. Used in messages.
  • conditions: array of procs that take a rack env, all of which must return true to rate-limit the request.
  • exceptions: array of procs that take a rack env, any of which may return true to exclude the request from rate limiting.
  • logger: responds to #info(message). If provided, the rate limiter logs the first request that hits the rate limit, but none of the subsequently blocked requests.
  • error_message: the message returned in the response body when the rate limit is exceeded. Defaults to " rate limit exceeded. Please wait seconds then retry your request."


Rate-limit bursts of POST/PUT/DELETE requests by IP address

use(Rack::Ratelimit, name: 'POST',
  exceptions: ->(env) { env['REQUEST_METHOD'] == 'GET' },
  rate:   [50, 10.seconds],
  cache:  Dalli::Client.new,
  logger: Rails.logger) { |env| Rack::Request.new(env).ip }

Rate-limit API traffic by user (set by Rack::Auth::Basic)

use(Rack::Ratelimit, name: 'API',
  conditions: ->(env) { env['REMOTE_USER'] },
  rate:   [1000, 1.hour],
  redis:  Redis.new(ratelimit_redis_config),
  logger: Rails.logger) { |env| env['REMOTE_USER'] }