Open Source API Gateway

429 Too Many Requests: Rate Limit Is Exceeded

Membrane API Gateway returns 429 Too Many Requests with the title Rate limit is exceeded. when the rateLimiter plugin rejects a request because the client has used up its quota for the current time window.

The Error Response

The response contains the status 429 Too Many Requests, the title Rate limit is exceeded., the detail The quota of the rate limit is exceeded. Try again in 42 seconds., the configured limit and duration, the problem type https://membrane-api.io/problems/user/rate-limit and a link to this page. The type user/rate-limit means that the client exceeded a limit of the gateway. The response also carries X-RateLimit-* headers.

How the rateLimiter Counts Requests

The rateLimiter allows requestLimit requests per key within each window of requestLimitDuration, an ISO 8601 duration such as PT1M for one minute. By default, the key is the client IP address. With keyExpression, you can count by another value, for example an API key or a user from a token.

api:
port: 2000
flow:
- rateLimiter:
requestLimit: 100
requestLimitDuration: PT1M
target:
url: https://api.predic8.de
api:
  port: 2000
  flow:
    - rateLimiter:
        requestLimit: 100
        requestLimitDuration: PT1M
  target:
    url: https://api.predic8.de

Common Causes

The Client Sends Too Many Requests

The client exceeds the configured number of requests per window, for example because of a retry loop, polling at short intervals or a batch job without throttling.

All Clients Share the IP Address of a Proxy

Membrane runs behind a reverse proxy or load balancer, and trustForwardedFor is off. Membrane then sees the IP address of the proxy for every request, so all clients share one quota.

The keyExpression Yields No Value

If the keyExpression cannot be evaluated or yields an empty value, Membrane counts all such requests under the common key unknown and logs a warning. These clients then share one quota.

How to Fix It

  1. As a client, wait until the current window has ended before sending more requests, and spread requests over time.
  2. As an operator, adjust requestLimit and requestLimitDuration to the expected traffic.
  3. Behind a proxy, set trustForwardedFor: true together with trustedProxyList or trustedProxyCount so that the real client IP from the X-Forwarded-For header is used:
api:
port: 2000
flow:
- rateLimiter:
requestLimit: 100
requestLimitDuration: PT1M
trustForwardedFor: true
trustedProxyCount: 1
target:
url: https://api.predic8.de
api:
  port: 2000
  flow:
    - rateLimiter:
        requestLimit: 100
        requestLimitDuration: PT1M
        trustForwardedFor: true
        trustedProxyCount: 1
  target:
    url: https://api.predic8.de

If you use keyExpression, make sure it always yields a value, and check the log for Error evaluating expression warnings.

When a keyExpression is set, the number of seconds in the detail may not match the window of the key that was exceeded. Treat it as an approximation.

Details in Production Mode

Unlike most errors, this response is not shortened in production mode. It contains the detail and the link to this page even with production: true. The response only describes the quota and when to retry, which well-behaved clients need to back off.

Related Documentation

Configuration reference:

Tutorials with runnable examples:

← All Membrane error messages

Still stuck? See the troubleshooting guide or ask on GitHub Issues. For commercial support, contact info@predic8.de.