Coding problem · Medium · Design & Data Structures

Token Bucket Rate Limiter

Medium Practical tasks Seen at Stripe, Cloudflare, Amazon, Uber

Implement a token bucket rate limiter for one client.

  • TokenBucket(capacity, refillPerSecond): the bucket holds at most capacity tokens and gains refillPerSecond tokens per second (continuously, fractions allowed). It starts full at time 0.
  • allow(nowMs): called with a non-decreasing timestamp in milliseconds. Refill based on elapsed time, then, if at least one whole token is available, consume one and return true; otherwise return false.

Time is passed in rather than read from a clock so the limiter is deterministic and testable — a common interview follow-up.

Example 1

Input: ["TokenBucket","allow","allow","allow","allow","allow"]
[[2,1],[0],[0],[0],[500],[1000]]
Output: [null,true,true,false,false,true]
Explanation: Two burst requests pass, the third fails; after 1 s one token has refilled.

Hints

Hint 1

Store tokens (a float) and lastRefillMs.

Hint 2

On each call: tokens = min(capacity, tokens + (now - last) / 1000 * rate).

Write and run your solution

Loading the editor… (JavaScript is required to run code in your browser.)

Solution and explanation

Show the solution

Time O(1) per call · Space O(1)

Refill lazily on each call instead of running a timer: compute elapsed time since the last call, add elapsed × rate tokens, cap at capacity, then try to spend one token. This is O(1) per call and is how most API gateways implement token buckets (often in Redis with a Lua script so the read-modify-write is atomic).

Reference solution

class TokenBucket {
  constructor(capacity, refillPerSecond) {
    this.capacity = capacity;
    this.rate = refillPerSecond;
    this.tokens = capacity;
    this.last = 0;
  }

  allow(nowMs) {
    this.tokens = Math.min(this.capacity, this.tokens + ((nowMs - this.last) / 1000) * this.rate);
    this.last = nowMs;
    if (this.tokens < 1) return false;
    this.tokens -= 1;
    return true;
  }
}

Common mistakes

  • Letting tokens exceed capacity after a long idle period.
  • Using wall-clock time inside the class — hard to test and non-deterministic.
  • Distributed version: non-atomic read-then-write races between instances.

Follow-up questions

  • Rate-limit per API key (map of buckets + eviction).
  • Return Retry-After: how long until the next token?
  • Compare with fixed window, sliding window and GCRA.