machine coding

API Rate Limiter

Two policies behind one interface: a sliding-window log and a token bucket, per client.

Medium90 minutesJava · class RateLimiter

the brief

What you are building

A payments API gets traffic from thousands of apps. One buggy app retries in a loop and sends 5,000 requests a second. Without a limit, everyone's payments slow down.

You will build the rate limiter that sits in front of the API. For every request it answers one question: let it in, or reject it?

The interviewer wants two policies, so you can show that a new policy plugs in without touching the rest.

must have

Requirements

  1. Every client (clientId) has its own limit. One client's traffic never affects another.
  2. Sliding window ("sliding-window"): allow a request at time now only if this client had fewer than limit allowed requests in the last windowMs ms — that is, at times t with t > now - windowMs.
  3. Rejected requests are not counted.
  4. Token bucket ("token-bucket"): each client's bucket holds up to limit tokens and starts full. A request uses one token; with no token it is rejected.
  5. Tokens drip back one at a time, every interval = windowMs / limit ms, counted from the client's first call (allow or remaining). A drip into a full bucket is wasted.
  6. remaining(clientId, now) says how many requests the client could make right now, without using one.
  7. Any other policy name makes the constructor throw IllegalArgumentException.

the contract

The Java API: class RateLimiter

Time is passed in (nowMs) instead of read from the clock, so tests are exact and repeatable.

public RateLimiter(String policy, int limit, long windowMs)
policy is "sliding-window" or "token-bucket".
public boolean allow(String clientId, long nowMs)
true (and the request counts) or false.
public int remaining(String clientId, long nowMs)
How many more requests would be allowed right now.
  • 1 ≤ limit ≤ 1,000 and limit ≤ windowMs ≤ 10⁹
  • for the token bucket, windowMs is a multiple of limit
  • nowMs never goes backwards, and may be as large as 2 × 10¹²
  • up to 20,000 calls

example session

Calls in, answers out

Session 1: sliding window, 3 requests per 1,000 ms. Session 2: token bucket, 2 tokens, so one drip every 500 ms.

callreturnswhy
new RateLimiter("sliding-window", 3, 1000)—
allow("rahul", 0)true
allow("rahul", 100)true
allow("rahul", 200)true
allow("rahul", 300)false3 allowed in (−700, 300] already
allow("priya", 300)truepriya has her own window
remaining("rahul", 999)0
allow("rahul", 1000)truethe request at 0 left the window: 0 > 1000 − 1000 is false
remaining("rahul", 1150)1only 200 and 1000 are inside (150, 1150]
new RateLimiter("token-bucket", 2, 1000)—
allow("app", 0)truethe bucket starts full: 2 → 1
allow("app", 10)true1 → 0
allow("app", 20)falseempty
remaining("app", 499)0the first drip is at 0 + 500
allow("app", 500)trueone drip, used at once
remaining("app", 2000)2drips at 1000, 1500, 2000 — but the bucket holds only 2

time plan

Your 90 minutes

  1. 10 minclarify — Per client or global? Do rejected requests count? When exactly does a request leave the window? When does the first drip happen?
  2. 15 mindesign — One Policy interface, one class per policy, one map from client to its policy object. Draw the sliding window on a time line.
  3. 35 mincore code — Sliding window first (run session 1), then the token bucket (run session 2).
  4. 20 minedge cases — limit 1, exact window boundary, a full bucket wasting drips, an unknown client, huge timestamps.
  5. 10 minpolish — Be ready to compare the two policies and to talk about many servers sharing one limit.

Practise under real pressure: a 90-minute clock with the phases above. It keeps running if you reload the page.

your turn

Build it here

Write the whole class in the editor. Run replays the example session; Submit adds the hidden tests (edge cases and large random sessions). Stuck for 20 minutes? Open one hint at a time.

Build RateLimiter in the browser

Design: implement every operation. Compiled by the real javac (Java 17) and judged right here — nothing is sent anywhere.

    The reference, the data-structure reasoning and the rubric are hidden so you can build it first. Even a half-working attempt teaches you more than reading a perfect answer.

    Opening all three hints also shows it.

    revise

    Chapters that teach these structures