Algorithms¶
Token Bucket¶
The default algorithm. A bucket holds tokens that refill at a steady rate. Each request consumes tokens. Bursts are allowed up to the bucket capacity.
graph LR
A[Refill: limit/window per sec] --> B[Bucket]
B --> C{Tokens available?}
C -->|Yes| D[Allow, consume tokens]
C -->|No| E[Deny or wait]
Storage format: (remaining_tokens: float, last_refill_time: float)
Use when: Your API allows occasional bursts but enforces an average rate over time.
Sliding Window Counter¶
Estimates the count in the current window by combining the previous and current window counts with a weighted formula.
Formula:
Use when: You need accurate rate limiting without boundary burst issues.
Fixed Window Counter¶
Counts requests in fixed time windows. Simplest algorithm.
Use when: You need simplicity and low memory, and can tolerate the boundary burst issue.
Boundary burst: Fixed windows can allow up to 2x the limit at window edges.
Comparison¶
| Feature | Token Bucket | Sliding Window | Fixed Window |
|---|---|---|---|
| Bursts | Yes | No | At boundaries |
| Accuracy | Good | Best | Good (except boundaries) |
| Memory | 2 values/key | 2 counters/key | 1 counter/key |
Cost-aware limiting¶
@app.post("/api/upload")
@limiter.limit("1000/minute")
async def upload_file(request: Request):
size = len(await request.body())
return {"uploaded": size}
Blocking mode¶
Wait for a slot instead of denying immediately: