HTTP 429 Too Many Requests

HTTP 429 indicates that a client has exceeded a server-defined request rate or quota. It tells the client to slow down or otherwise reduce requests according to the server's limiting policy.

HTTP 429 is a client error status defined for situations where a server or intermediary considers too many requests to have been sent within a given period. The exact limit is controlled by the service and may be based on requests per second, minute, account, API key, IP address, or another quota dimension.

A 429 response may include a `Retry-After` header telling the client how long to wait before retrying. A client should respect that guidance when present rather than immediately sending another request. If the header is absent, the client may use a bounded backoff strategy appropriate to the API.

Rate limiting is commonly implemented with algorithms such as token bucket, leaky bucket, fixed window, or sliding window. These mechanisms differ in how they account for requests and bursts, but their shared goal is controlling traffic so a service can protect capacity and enforce usage policies.

For a Java backend service, handling 429 usually means distinguishing it from other failures, applying retry or backoff behavior only when appropriate, and avoiding synchronized retry storms. When multiple application instances retry the same dependency, jittered exponential backoff can reduce the chance that all instances send requests again simultaneously.

Key points

Interview questions

What does HTTP 429 mean?

HTTP 429 means the server is limiting the client because it has sent too many requests according to some rate or quota policy. The restriction can be associated with an IP address, user, API key, or another identity. The response may include `Retry-After`, which provides retry timing information. A client should avoid immediately repeating the request.

How should a Java service handle a 429 response from a downstream API?

The service should recognize 429 as a potentially temporary rate-limit response and inspect `Retry-After` when available. If retrying is appropriate, it should use bounded backoff and preferably add jitter to avoid synchronized retries. It should also limit the number of attempts and avoid retrying non-idempotent operations blindly. Metrics should record the rate-limit responses so the underlying traffic pattern can be investigated.

What are common algorithms for implementing rate limiting?

Common approaches include fixed window, sliding window, token bucket, and leaky bucket. Fixed windows are simple but can allow bursts around window boundaries. Token bucket supports controlled bursts while replenishing capacity over time. Sliding-window approaches provide a more continuous view of recent request volume.

Why can naive retries make a 429 situation worse?

If many clients receive 429 and immediately retry, they can all become active at the same time and generate another burst. This creates a feedback loop in which the service continues rejecting requests while clients consume resources performing retries. Exponential backoff with jitter spreads retry attempts over time. A bounded retry policy prevents an overloaded dependency from consuming unlimited application capacity.

How would you design rate limiting for a distributed Java backend?

I would first define the limiting dimension, such as user, API key, tenant, endpoint, or a combination. For multiple application instances, the limiter generally needs shared state or a coordinated mechanism so each instance does not independently grant the full allowance. The design should specify burst behavior, fairness, expiration, and what happens when the limiter's backing store is unavailable. I would also expose metrics for rejected requests, remaining capacity where available, and limiter latency.