Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

System Design (High-Level Design)  Module 19 – Building blocks: traffic, storage and messaging

Design an API gateway

Design an API gateway fleet with route matching, a filter chain per route, token checks, rate limits across instances and configuration you can roll back.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free intro, Pro: 5 of 25 minutes free
Free: Requirements and estimate: the loss of a zone sizes the fleet. With a pass: the other 13 sections and 3 interview answers, about 20 minutes.

What you will learn

  • Estimate the instances, token-check CPU and log volume of an API gateway fleet
  • Design a filter chain per route for authentication, limits, validation and routing
  • Implement route matching with path normalisation and match precedence
  • Limit requests and quotas across many instances, and compute the error of each method
  • Release configuration in stages, with validation and automatic rollback

Before you start

What you’ll learn · Before you start

What you will learn

  • Estimate the instances, token-check CPU and log volume of an API gateway fleet
  • Design a filter chain per route for authentication, limits, validation and routing
  • Implement route matching with path normalisation and match precedence
  • Limit requests and quotas across many instances, and compute the error of each method
  • Release configuration in stages, with validation and automatic rollback

Before you start

On this page

Every request to a company’s public API, 60,000 a second at peak across 900 routes, must have its caller checked, its limit counted and its path matched before any of 120 services sees it. An API gateway is the single front door that does this: a fleet of identical, stateless reverse proxy: A server that clients reach as if it were the site itself. It passes each request to one of the servers behind it, often ending TLS, routing by path, balancing load and caching on the way. More in “Forward proxies, reverse proxies and gateways” that run every request through the filter chain of the route it matches (TLS, authentication, rate limits and quotas, request validation, routing to a service, response shaping and logging), so that services do not each implement them.

Two things make it a design problem rather than a product to install. Every request pays for the chain, so the gateway must stay thin and fast; and the route table and its policies are configuration that changes many times a day, so a bad change must never reach every instance at once.

Requirements and estimate: the loss of a zone sizes the fleet

The company’s 120 services are used by its mobile apps, its website and about 2,000 partners. Today every team runs its own token checks and limits, and they disagree. Ask first:

Question Assumed here
How many requests a second at peak, how large, and across how many routes? 60,000 a second at peak across 900 routes
Who calls: users with tokens, partners with API keys, or both? User tokens from an identity provider, and partner API keys
Which limits: per user, per partner, per route or per plan, and how exact must they be? Per-partner quotas and per-route limits, accurate to a few per cent
How much latency may the gateway add, and what must it never do when a dependency fails? A few milliseconds at p99 at most, and no outage when a dependency fails
How often do routes and policies change, and who changes them? About 200 configuration changes a day from 30 teams

Functional requirements: route by host, path, method and headers; check tokens and API keys; rate limits and quotas; request size limits and schema validation; per-route timeouts and retries; access logs and traces. Non-functional requirements: a few milliseconds of overhead; no outage when a zone or any dependency fails; configuration changes without restarts; and a bad change contained to a small share of the traffic.

The first estimate, to change and run again:

Instances, limits, token checks and logs Python · plan/gateway_estimate.py
# The first estimate for an API gateway fleet. Every input is an assumption, written down so that you can change it
# and run the script again.
import math

PEAK = 60_000              # requests a second at peak, every API together
PER_INSTANCE = 4_000       # requests a second one instance handles at its target CPU (TLS, filters, proxying)
ZONES = 3                  # instances spread evenly over 3 zones; the fleet must survive the loss of one
JWT_VERIFY_MS = 0.1        # CPU time to check one token's signature
CACHE_HIT = 0.9            # share of requests whose token this instance verified a moment ago
SYNC_MS = 100              # each instance reports its rate-limit counts this often, in one batch
LOG_BYTES = 1_000          # one access-log record
ROUTES, ROUTE_BYTES = 900, 2_000

load = math.ceil(PEAK / PER_INSTANCE)
zone_safe = math.ceil(load / ((ZONES - 1) / ZONES))
fleet = math.ceil(zone_safe / ZONES) * ZONES
print("Instances")
print(f"  for the load: {PEAK:,} / {PER_INSTANCE:,} = {load}")
print(f"  to survive losing 1 of {ZONES} zones: {load} / ({ZONES - 1}/{ZONES}) = {load * ZONES / (ZONES - 1):.1f}, "
      f"rounded up to {zone_safe}, and to {fleet} for the same number in every zone ({fleet // ZONES})")

print("Rate-limit decisions")
print(f"  a central counter asked on every request: {PEAK:,} calls a second, each a network round trip")
print(f"  local token buckets, counts sent every {SYNC_MS} ms in one batch: {fleet} x {1000 // SYNC_MS} "
      f"= {fleet * 1000 // SYNC_MS} messages a second")

print("Token checks")
print(f"  every request verified: {PEAK * JWT_VERIFY_MS / 1000:.1f} CPU cores")
print(f"  with {CACHE_HIT:.0%} of tokens verified a moment ago: {PEAK * (1 - CACHE_HIT) * JWT_VERIFY_MS / 1000:.1f} cores")

print("Logs and configuration")
print(f"  access logs: {PEAK * LOG_BYTES / 1e6:.0f} MB a second, {PEAK * LOG_BYTES * 86400 / 1e12:.2f} TB a day")
print(f"  route table: {ROUTES} routes x {ROUTE_BYTES:,} bytes = {ROUTES * ROUTE_BYTES / 1e6:.1f} MB per version, "
      f"sent to {fleet} instances")

Output

Instances
  for the load: 60,000 / 4,000 = 15
  to survive losing 1 of 3 zones: 15 / (2/3) = 22.5, rounded up to 23, and to 24 for the same number in every zone (8)
Rate-limit decisions
  a central counter asked on every request: 60,000 calls a second, each a network round trip
  local token buckets, counts sent every 100 ms in one batch: 24 x 10 = 240 messages a second
Token checks
  every request verified: 6.0 CPU cores
  with 90% of tokens verified a moment ago: 0.6 cores
Logs and configuration
  access logs: 60 MB a second, 5.18 TB a day
  route table: 900 routes x 2,000 bytes = 1.8 MB per version, sent to 24 instances

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 gateway_estimate.py

Three numbers decide the design. The fleet is sized for the loss of a zone, not for the load. A central counter on every request is 60,000 calls a second, while batched local counts are 240 messages. And caching recently verified tokens saves nine signature checks in ten.

Checkpoint

Check the estimate

  1. Question 1 of 3 Peak traffic grows to 90,000 requests a second, at 4,000 per instance, and the fleet must still survive the loss of one of its 3 zones. How many instances, with the same number in every zone?

    Type a number.

    A hint

    Size for the load first, then for two zones carrying all of it, then round up to a multiple of 3.

    Show the answer: Question 1 of 3

    Answer: 36 instances

    90,000 ÷ 4,000 = 22.5, so 23 instances carry the load. Two zones must hold them all: 23 ÷ (2/3) = 34.5, so 35, and 36 to put 12 in each zone.

  2. Question 2 of 3 At 100,000 requests a second and one 1 KB access-log record per request, how many terabytes of logs a day?

    Type a number.

    Show the answer: Question 2 of 3

    Answer: 8.64 TB a day (anything from 8.54 to 8.74 counts)

    100,000 × 1,000 bytes = 100 MB a second, × 86,400 seconds = 8.64 TB a day. The estimate's 60,000 requests a second gave 5.18 TB.

  3. Question 3 of 3 At 60,000 requests a second, a signature check costs 0.1 ms of CPU, and 80 % of requests carry a token the instance verified a moment ago. How many CPU cores do token checks need?

    Type a number.

    A hint

    Only the requests whose token was not verified a moment ago need a full check.

    Show the answer: Question 3 of 3

    Answer: 1.2 cores (anything from 1.15 to 1.25 counts)

    Only 20 % of requests need a full check: 60,000 × 0.2 × 0.1 ms = 1,200 ms of CPU a second, 1.2 cores. With the estimate's 90 % cache it is 0.6 cores, and with no cache 6.

The first design: a filter chain per route, cheapest refusals first

Clients reach stateless gateway instances through a load balancer; instances proxy to services, with a control plane and a rate limiter.Clientsmobile apps, web, partnersLoad balancerspreads connectionsControl planeversioned route tables:validate, stage, roll backGateway instances, statelessServicesRate-limit serviceglobal counts,batched updatesInstance 1route table v41,a filter chainper routeInstance 24route table v41,a filter chainper routeOrdersPayoutsHTTPSconnectionsroute tableproxied requestsbatched counts

The first design of the API gateway

Text description of the diagram

The diagram shows the first design from top to bottom.

  • Clients, such as mobile apps, the web app and partners, connect over HTTPS to a load balancer, which spreads their connections over the gateway instances.
  • The gateway instances are stateless, from instance 1 to instance 24. Each holds route table v41 and runs the filter chain of the route a request matches.
  • A dashed arrow shows the control plane, which publishes route tables as versioned snapshots that it validates, stages and can roll back.
  • The instances proxy each request to a service, such as orders or payouts.
  • Another dashed arrow shows the instances sending batched counts to a rate-limit service, which keeps the global counts.

Each part of the design has one job:

  • Route table: maps host, path, method and headers to a route, with its backend, timeouts and filters.
  • Filter chain: the ordered steps a route’s requests pass through, cheapest refusals first.
  • Authentication filter: checks a token’s signature and claims, or an API key, before anything uses the identity.
  • Rate limits and quotas: A rate limiter that holds up to a set number of tokens and refills them at the allowed rate: each request takes one token, and a request that finds the bucket empty is refused or made to wait. per key and route, enforced locally and shared through a limit service.
  • Request validation: size limits and the route’s schema, so malformed requests never reach a service.
  • Control plane: stores routes and policies as versioned snapshots, validates them and rolls them out in stages.
  • Backend for frontend: a client team’s own service for aggregation and shaping, outside the shared gateway.

Follow one partner request through its route’s filters:

Interactive · stage by stage One partner request through its route's filter chain Step through one partner request from the moment it reaches an instance, then the two refusals the chain makes early.
One partner request through its route's filter chain, step 8 of 8. Showing End TLS, check the size, Check the API key, Partner's rate limit, Validate the schema, Send to the backend, Write the access log. In focus: unknown-key, over-limit.401: unknown key429: over its limitEnd TLS, check the sizeinstance 7 of 24Check the API keythe key names a partnerPartner's rate limitone token taken, no callValidate the schemathe route's own schemaSend to the backendpayouts, its timeoutWrite the access logpartner, route, status

The drawing shows the whole scene; its 8 steps are listed below.

Every step as text
  1. The request arrives. A partner's request for /v2/payouts reaches instance 7 of the 24, which the load balancer picked. The instance has matched it to its route, and the route's filters run in order.
  2. TLS and the size limit. The instance ends TLS and checks the request's size first. Both are cheap, and an oversized request is refused before any other filter spends work on it.
  3. Who is calling. The authentication filter checks the API key and finds the partner it belongs to. Nothing later may use an identity that this filter has not proven.
  4. The partner's limit. The rate limit is counted for the proven partner, in a token bucket on this instance, so the request takes one token without a call to another service.
  5. The route's schema. The request is checked against the route's schema, so a malformed request never reaches the service.
  6. To the service. The instance sends the request to the payouts backend with the route's timeout and waits for the answer.
  7. Logged last. The access log is written last, when the outcome is known, with the partner, the route and the status.
  8. Two early refusals. A request whose key is unknown stops at the second filter with 401, and a partner past its limit stops at the third with 429. Each refusal is logged, and neither reaches the service.

The deep dives that decide whether the design works:

  1. How is a request matched to a route, quickly and without being fooled by its path?
  2. In what order do the filters run, and what does each do when its dependency fails?
  3. How do limits stay accurate across 24 instances without a call on every request?
  4. How does a configuration change reach the fleet without putting all of it at risk?
  5. How do the issuer’s signing keys rotate without an outage?
  6. What stays out of the gateway?

An answer is judged on requirements and estimates, routing, the filter chain, limits, configuration safety, failure handling and operations.

Checkpoint

Check the first design

  1. Question 1 of 3 Which step does a partner's request meet first on its route's filter chain?

    Choose one answer.

    Show the answer: Question 1 of 3

    Answer: Ending TLS and checking the request's size

    The chain puts the cheapest refusals first: TLS and the size limit, then the key, the partner's limit, the schema, the backend, and the log last.

  2. Question 2 of 3 Why does the partner's rate limit run after the API key check, not before it?

    Choose one answer.

    Show the answer: Question 2 of 3

    Answer: A limit counted by an identity nobody has checked would let anyone spend another partner's allowance

    The authentication filter proves who is calling, and every filter that counts or decides by that caller runs after it. A limit keyed by an unchecked claim could be charged to someone else.

  3. Question 3 of 3 The mobile team wants one endpoint that combines a user's orders and payouts for its home screen. Where does that logic go in the first design?

    Choose one answer.

    Show the answer: Question 3 of 3

    Answer: A backend for frontend that the mobile team owns, behind the gateway

    The gateway keeps what every API shares; a client team's own aggregation lives in its backend for frontend, which the gateway routes to like any other service.

The API, and route tables swapped whole Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Normalise the path once, then match by precedence
  • Authentication fails closed, every other filter fails open
  • Limits across a fleet: local buckets with batched counts
  • A bad configuration push, reproduced and contained
  • A thin gateway: client logic in backends for frontends
  • At 10× handshakes and token checks dominate, at 100× the configuration
  • Five alternatives, and why each loses
  • What each level is expected to cover
  • A sample answer and a 45-minute self-mock
  • Practise: pick the route
    • Pick the route for a request, by match precedence
  • Interview questions
    • When does logic belong in a backend for frontend rather than in the shared API gateway?
    • A routing change containing one malformed route was pushed to every gateway instance at once, and all of them began to crash and restart. The whole API was down for ten minutes, although the route carried 0.5 % of the traffic. Design the configuration pipeline and the instances so that this cannot take the API down.
    • Partners are allowed 1,000 requests a minute each, and 30 gateway instances enforce it by giving each instance a thirtieth of the limit. Some partners get only a fraction of their limit, while others exceed it after a scale-up. Explain both, and design a limiter that stays within a few per cent.
    • A security review finds that a request for /public/..%2Fadmin/users reaches the admin API without a token through your gateway, although /admin requires one. Explain how, and fix it in the gateway and behind it.
  • Key takeaways

3 diagrams · 3 runnable examples (JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 49 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Design a unique ID generator

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.