limit-count
Limits the number of requests in a time window, per key.
limit-countPriority 1002What it does
limit-count counts the requests that reach the route or other object it is added to, under a key. The key is the value that tells callers apart: by default the client address, remote_addr, or another request variable like the name of the consumer an authentication plugin found, consumer_name. The key is what requests are counted by, not an API key. While a key is within its Count for the Time window, requests pass, and every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.
The first request over the count is answered with the Rejected code, 503 unless you set another, and with your Rejected message in a JSON body if you wrote one. Each gateway node keeps its own count unless Policy points at Redis, where the nodes share one. With Policy local, once one node has refused an address, another node still lets it through.
When to use it
- A quota per client address or per consumer, for example 100 requests a minute.
- Several limits on one object at once, each counted under its own key, for example one per consumer and one per client address. A request has to stay within every limit that applies to it.
- A quota that several gateway nodes count together, through Redis.
- One quota shared by several routes or services: give them the same Group.
Not for smoothing a burst to a steady rate — see limit-req (Limits the request rate with a leaky bucket) or capping how many requests are in flight at once — see limit-conn (Limits concurrent connections per key).
Guides
Pages that walk through a job with limit-count, step by step.
- Limit one client wherever it callsGive one consumer its own limit, counted over every route that asks for its key.
- Limit requests on a routeAllow each client address a number of requests a minute.
Set it up
Allow 100 requests a minute for each client address
Under Basics choose One limit for everything here, set Count to 100, set Time window to 60 seconds and leave Key at remote_addr. Under Behavior set Rejected code to 429 and set Rejected message to Too many requests, try again in a minute.
{
"count": 100,
"time_window": 60,
"key": "remote_addr",
"rejected_code": 429,
"rejected_msg": "Too many requests, try again in a minute"
}
What you will see
- A request within the count reaches your backend, and its answer carries
X-RateLimit-Limit: 100,X-RateLimit-Remainingwith the requests left in this window, andX-RateLimit-Resetwith the seconds left in it. - The window opens with an address's first request, not at a round minute on the clock: that first answer carries
X-RateLimit-Reset: 60, the seconds until the window ends. - The 101st request in a window from one address is answered
429with{"error_msg":"Too many requests, try again in a minute"}; its headers showX-RateLimit-Remaining: 0andX-RateLimit-Reset, the seconds until the window ends. - With the default Policy each gateway node counts on its own: once one node has refused an address, the same request to another node is answered with
X-RateLimit-Remaining: 99.
More scenarios
Allow 100 requests a minute for each consumer
Needs an authentication plugin on the same route, such as key-auth, which names the consumer a request belongs to.
Under Basics choose One limit for everything here, set Count to 100, set Time window to 60 seconds and set Key to consumer_name. Under Behavior set Rejected code to 429 and set Rejected message to Too many requests, try again in a minute.
{
"count": 100,
"time_window": 60,
"key": "consumer_name",
"rejected_code": 429,
"rejected_msg": "Too many requests, try again in a minute"
}
What you will see
- Each consumer has 100 requests a minute of its own: once one has used them, its next request is answered
429with{"error_msg":"Too many requests, try again in a minute"}, while another consumer calling from the same address still gets200withX-RateLimit-Remaining: 99.
Give each consumer a limit of its own and each client address another
Needs an authentication plugin on the same route, which sets the consumer name. Every rule whose key the request carries counts it, so a consumer is held to its address limit too, and a request without a consumer is counted by its address alone.
Under Basics choose A limit for each key and fill Rules as in the JSON below.
{
"rules": [
{
"count": 1000,
"time_window": 60,
"key": "$consumer_name",
"header_prefix": "Consumer"
},
{
"count": 100,
"time_window": 60,
"key": "$remote_addr",
"header_prefix": "Address"
}
]
}
What you will see
- Every answer carries
X-Consumer-RateLimit-Limit: 1000andX-Address-RateLimit-Limit: 100: one set of headers for each rule, named by its Header prefix.
Name the headers that tell clients how much of their quota is leftenvironment-wide settings
Set Limit header to RateLimit-Limit, set Remaining header to RateLimit-Remaining and set Reset header to RateLimit-Reset.
{
"limit_header": "RateLimit-Limit",
"remaining_header": "RateLimit-Remaining",
"reset_header": "RateLimit-Reset"
}
What you will see
- Every answer carries the quota under
RateLimit-Limit, what is left of it underRateLimit-Remainingand the seconds until the window ends underRateLimit-Reset, the names set here.
Things to know
- Rejected code — Set a Rejected code. Unset, a refused request is answered
503with an HTML error page rather than JSON, the answer of a service that is unavailable.429tells the client it sent too many requests. - Rejected message — With a Rejected message, the body of a refused request is JSON with one field,
error_msg, for example{"error_msg":"Too many requests, try again in a minute"}. - Key — The client address is the address a request comes from, so callers behind one proxy or load balancer share a single count. A request that names another client in
X-Forwarded-Foris still counted under the address it comes from. Count per consumer, or add real-ip with Trusted addresses set to the load balancer's address, to tell those callers apart. - Key — A limit per consumer needs an authentication plugin on the same route; it is the one that sets
consumer_name. - Key — A variable the request does not carry, or a name with a dollar sign under Variable, is saved without a word, and every request is then counted by client address. So is a combination in which no $name resolves.
- Window type — With Window type
fixed, the default, a key's window opens with its first request, whatever the time on the clock: that answer carriesX-RateLimit-Reset: 60, the seconds until the window ends. - Count — Count and Time window take a whole number, a $variable like $http_x_quota, or a
$env://or$secret://reference. Any other text is refused when you save. - Count — A header variable like $http_x_quota carries whatever the client sends, so a client could choose its own limit.
- Rules — A rule's Key is a text with at least one $variable:
$remote_addr, notremote_addr. A key that names none is refused when you save. - Rules — Two rules with the same Key are refused when you save.
- Group — Group cannot be set beside Rules; the save is refused.
- Group — To change a group’s settings, give it a new name: changed settings under the same name may be refused when you save, or accepted while part of the gateway goes on counting with the old ones.
- Sync interval — With a Redis policy, Sync interval must be smaller than Time window, or the save is refused.
- Allow degradation — When the limit cannot be counted, because the store is unreachable or a value resolves to nothing, off answers 500 and on lets every request through unlimited.
Settings
The fields as the console shows them, section by section. A field under “When … is …” is there only while that value is set.
Basics
Choose one:
One limit for everything here
countinteger or stringRequiredHow many requests one key may make in each window: a number, or a $variable read from every request, such as $http_x_quota. A header variable such as $http_x_quota carries whatever the client sends, so a client could choose its own limit.
pattern
^([0-9]*[1-9][0-9]*|[$](env|secret)://.+|[$][{][^}]*[}]|[$][A-Za-z_][A-Za-z0-9_.]*)$time_windowinteger or stringRequiredHow long each window lasts: a number, or a $variable read from every request, such as $http_x_window. A header variable such as $http_x_window carries whatever the client sends, so a client could choose its own window.
pattern
^([0-9]*[1-9][0-9]*|[$](env|secret)://.+|[$][{][^}]*[}]|[$][A-Za-z_][A-Za-z0-9_.]*)$A limit for each key
rulesarray of objectsRequiredrules[].countinteger or stringRequiredHow many requests one key may make in each window of this rule: a number, or a $variable read from every request.
pattern
^([0-9]*[1-9][0-9]*|[$](env|secret)://.+|[$][{][^}]*[}]|[$][A-Za-z_][A-Za-z0-9_.]*)$rules[].keystringRequiredpattern
[$]([{][^}]+[}]|[A-Za-z_][A-Za-z0-9_.]*)rules[].time_windowinteger or stringRequiredHow long each window of this rule lasts: a number, or a $variable read from every request.
pattern
^([0-9]*[1-9][0-9]*|[$](env|secret)://.+|[$][{][^}]*[}]|[$][A-Za-z_][A-Za-z0-9_.]*)$rules[].header_prefixstringThe prefix the rate-limit headers of this rule carry.
key_typestringdefault varVariable reads one request variable by name, Variable combination expands every $name in the text into one key, and Constant counts every request together.
keystringdefault remote_addrThe request variable whose value tells callers apart, such as remote_addr or consumer_name, written without its dollar sign; under Variable combination, text with $names.
Behavior
policystringdefault localWhen Policy is redis
redis_databaseintegerdefault 0redis_hoststringRequiredredis_keepalive_poolintegerdefault 100redis_keepalive_timeoutintegerdefault 10000redis_usernamestringredis_passwordstringStored on the engine and shown only to roles that manage this object.
redis_portintegerdefault 6379redis_sslbooleandefault falseredis_ssl_verifybooleandefault falseredis_server_namestringThe server name sent in the TLS handshake and, with verification on, checked against the certificate; unset, the host is sent, and an address never is.
redis_timeoutintegerdefault 1000When Policy is redis-cluster· 8 fields
redis_cluster_namestringRequiredredis_cluster_nodesarray of stringsRequiredredis_cluster_sslbooleandefault falseredis_cluster_ssl_verifybooleandefault falseredis_keepalive_poolintegerdefault 100redis_keepalive_timeoutintegerdefault 10000redis_passwordstringStored on the engine and shown only to roles that manage this object.
redis_timeoutintegerdefault 1000When Policy is redis-sentinel· 13 fields
redis_connect_timeoutintegerdefault 1000redis_databaseintegerdefault 0redis_keepalive_timeoutintegerdefault 60000redis_master_namestringRequiredredis_usernamestringredis_passwordstringStored on the engine and shown only to roles that manage this object.
redis_read_timeoutintegerdefault 1000redis_rolestringdefault masterredis_sentinelsarray of objectsRequiredredis_sentinels[].hoststringRequiredredis_sentinels[].portintegerRequiredsentinel_usernamestringsentinel_passwordstringStored on the engine and shown only to roles that manage this object.
window_typestringdefault fixedrejected_codeintegerdefault 503rejected_msgstringshow_limit_quota_headerbooleandefault trueallow_degradationbooleandefault falseAdvanced
groupstringRoutes and services that carry the same group share one counter, so their limit settings must be the same. To change a group’s settings, give it a new name: changed settings under the same name may be refused when you save, or accepted while part of the gateway goes on counting with the old ones.
sync_intervalnumberdefault -1Environment-wide settings
Set once per environment, under Plugins › limit-count. Every limit-count in the environment uses these.
limit_headerstringdefault X-RateLimit-Limitremaining_headerstringdefault X-RateLimit-Remainingreset_headerstringdefault X-RateLimit-Reset