The project is in a healthy, maintained state
A zero-dependency Ruby gem for rate limiting with sliding window and token bucket algorithms, per-key tracking, and thread safety.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

philiprehberger-rate_limiter

Tests Gem Version License

In-memory rate limiter with sliding window and token bucket algorithms, per-key tracking, and thread safety.

Note: This is a single-process, in-memory rate limiter. It does not share state across processes or servers. For distributed rate limiting, use a centralized store like Redis.

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-rate_limiter"

Then run:

bundle install

Or install directly:

gem install philiprehberger-rate_limiter

Usage

require "philiprehberger/rate_limiter"

Sliding Window

Limits the number of requests within a rolling time window.

limiter = Philiprehberger::RateLimiter.sliding_window(limit: 100, window: 60)

if limiter.allow?("user:123")
  # Request is allowed
else
  # Rate limit exceeded
end

Token Bucket

Allows bursts up to a capacity, refilling at a steady rate.

limiter = Philiprehberger::RateLimiter.token_bucket(rate: 10, capacity: 50)

if limiter.allow?("api:key")
  # Request is allowed
else
  # Rate limit exceeded
end

Peeking Without Consuming

limiter.peek("user:123")      # => true/false (does not consume)
limiter.remaining("user:123") # => number of remaining requests/tokens

Weighted Requests

Consume multiple tokens per request for expensive operations:

limiter.allow?("user:123", weight: 5) # consumes 5 tokens
limiter.allow?("user:123", weight: 1) # consumes 1 token (default)

Inspecting Usage

info = limiter.info("user:123")
# Sliding window:
# => { remaining: 98, reset_at: 1710000060.5, limit: 100, window: 60, used: 2 }
# Token bucket:
# => { remaining: 48, reset_at: 1710000000.2, capacity: 50, rate: 10.0, tokens: 48.3 }

The reset_at value is a monotonic timestamp suitable for computing X-RateLimit-Reset headers. It is nil when the key has no usage or is at full capacity.

Per-Key Stats

Track allowed and rejected request counts:

limiter.stats("user:123")
# => { allowed: 42, rejected: 3 }

Quota Refund

Return tokens when a downstream operation fails (so the failed request does not count):

if limiter.allow?("user:123")
  begin
    make_api_call
  rescue ApiError
    limiter.refund("user:123", amount: 1)
  end
end

On-Reject Callback

Register a hook for logging or alerting when requests are rejected:

limiter.on_reject do |key|
  logger.warn("Rate limit exceeded for #{key}")
end

The method returns self for chaining:

limiter = Philiprehberger::RateLimiter
  .sliding_window(limit: 100, window: 60)
  .on_reject { |key| logger.warn("Rejected: #{key}") }

Resetting a Key

limiter.reset("user:123")

Sliding Window vs Token Bucket

Feature SlidingWindow TokenBucket
Best for Fixed request counts per window Allowing bursts with steady refill
Parameters limit, window (seconds) rate (tokens/sec), capacity
Burst behavior No bursting beyond limit Allows bursts up to capacity
Memory Stores timestamps per request Stores one float + timestamp per key

API

Method Description
RateLimiter.sliding_window(limit:, window:) Create a sliding window limiter
RateLimiter.token_bucket(rate:, capacity:) Create a token bucket limiter
#allow?(key, weight: 1) Check and consume token(s); returns true/false
#peek(key) Check availability without consuming
#remaining(key) Return remaining request/token count
#reset(key) Clear all state for a key
#info(key) Return usage info hash (remaining, reset_at, limit/capacity, used/tokens)
#stats(key) Return { allowed:, rejected: } counters for a key
#refund(key, amount: 1) Return tokens/slots on error
#on_reject { |key| } Register a callback for rejected requests
SlidingWindow#limit Return the configured request limit
SlidingWindow#window Return the configured window duration (seconds)
TokenBucket#rate Return the configured refill rate (tokens/sec)
TokenBucket#capacity Return the configured token capacity

Development

bundle install
bundle exec rspec      # Run tests
bundle exec rubocop    # Check code style

License

MIT