Build a rate limiter and API gateway that holds up
Put a gateway in front of a service that checks the API key, forwards the request, and counts how fast each caller is going. Add a token-bucket limiter backed by Redis, load-test it, and watch the excess requests come back as a real 429 with a Retry-After header. Then run two gateways against one Redis and see the limit still hold, which is the whole reason the counter lives in Redis and not in memory.
- Level 0 done: Install anything from GitHub, safely and The terminal survival kit.
- Build your own API in Python, or equivalent comfort with FastAPI,
uv/pipand running a local server. This mission builds a gateway in the same style and assumes you have met FastAPI, dependencies and JSON responses before. - Docker without fear for Docker and
docker compose. Redis runs in a container; you do not install it on your machine. - Some comfort with the terminal and three windows open at once. A code editor. About 400MB of disk for the Redis image and the Python packages.
What a gateway does, and why rate limit at all
Before any code, get the shape of the problem clear: what sits where, and what you are protecting against.
An API gateway is a single front door. Every request to your service goes through it first, and it decides what happens next: is the caller allowed in, which backend should answer, and has this caller had more than its share. Because it forwards the request on to another server and returns that server's reply, it is also a reverse proxy (a proxy that stands in front of your own servers, as opposed to one that stands in front of a browser). The backend behind it can then stay simple and private: it trusts that anything reaching it has already been checked.
Rate limiting is capping how many requests a caller may make in a window of time. Without it, one script in a loop, one scraper, or one bug that retries forever can use all your capacity, and everyone else gets slow answers or none. A limit protects the backend from being overwhelmed, keeps one noisy caller from starving the rest, and puts a ceiling on cost when each request does real work.
When a caller is over the limit, the gateway does not forward the request. It answers straight away with 429 Too Many Requests, the HTTP status that means "slow down". A good 429 carries a Retry-After header saying how many seconds to wait. This is backpressure: the server telling the client to ease off, instead of quietly falling over. A well-behaved client reads Retry-After and waits; a badly-behaved one at least does no more damage, because its requests are refused cheaply at the door.
- The caller sends a request with an API key.
- The gateway checks the key. No key or a wrong one gets 401 and stops here.
- The gateway checks the limit for that key. Over the limit gets 429 with Retry-After and stops here.
- Otherwise the gateway forwards the request to the backend and returns the backend's reply.
You will build exactly that, smallest piece first: the backend, then the gateway with the key check and routing, then the limiter, then the load test that proves it works, then a second gateway to show the limit holds across processes.