ai-rate-limiting
Limits requests to an LLM provider by tokens or count.
ai-rate-limitingPriority 1030What it does
ai-rate-limiting holds the requests of a route to a budget of language model tokens, not of requests. It counts what ai-proxy or ai-proxy-multi on the same route reads from the model's answer. The Limit is a number of tokens for each Time window.
Each answer within the budget carries the limit and what remains of it, under headers named after the instance that answered. With a limit of 10000, one answer that used 30 tokens leaves 9970 in X-Ai-Ratelimit-Remaining-Ai-Proxy-Openai-Compatible.
When to use it
- A cap of 10000 tokens a minute on an assistant route, so one busy client cannot spend the provider budget of all the others.
- A budget of tokens an hour for each consumer an authentication plugin names, kept apart with a rule keyed by
$consumer_name. - Output tokens that cost more than input tokens, counted three times as much with a Cost expression.
- One budget that several gateway nodes count together, kept in Redis, a Redis cluster or Redis Sentinel.
- A different budget for each model behind ai-proxy-multi, set per instance under Instances.
Not for counting requests rather than model tokens — see limit-count (Limits the number of requests in a time window, per key) or sending the request to the model in the first place — see ai-proxy (Proxies requests to an LLM provider with a unified API).
Guides
Pages that walk through a job with ai-rate-limiting, step by step.
- Put a language model behind the gatewaySend your apps' chat requests to one language model through a route, with the model's key on the gateway, a token budget, a prompt guard and a cache.
Set it up
Share the token budget across gateway nodes through Redis
Counts the tokens ai-proxy or ai-proxy-multi on the same route report from the model's answer.
Under Basics choose One budget of tokens, set Limit to 10000 and set Time window to 60 seconds. Under Behavior set Policy to redis, set Redis host to cache.example.com, leave Redis port at 6379 and set Redis password to <secret>.
{
"limit": 10000,
"time_window": 60,
"policy": "redis",
"redis_host": "cache.example.com",
"redis_port": 6379,
"redis_password": "<secret>"
}
More scenarios
Share the token budget across gateway nodes through a Redis cluster
Counts the tokens ai-proxy or ai-proxy-multi on the same route report from the model's answer.
Under Basics choose One budget of tokens, set Limit to 10000 and set Time window to 60 seconds. Under Behavior set Policy to redis-cluster, set Redis cluster name to ai-limits, fill Redis cluster nodes as in the JSON below and set Redis password to <secret>.
{
"limit": 10000,
"time_window": 60,
"policy": "redis-cluster",
"redis_cluster_name": "ai-limits",
"redis_cluster_nodes": [
"10.0.3.10:6379",
"10.0.3.11:6379",
"10.0.3.12:6379"
],
"redis_password": "<secret>"
}
Share the token budget across gateway nodes through Redis Sentinel
Counts the tokens ai-proxy or ai-proxy-multi on the same route report from the model's answer.
Under Basics choose One budget of tokens, set Limit to 10000 and set Time window to 60 seconds. Under Behavior set Policy to redis-sentinel, set Redis master name to ai-limits, fill Redis sentinels as in the JSON below and set Redis password to <secret>.
{
"limit": 10000,
"time_window": 60,
"policy": "redis-sentinel",
"redis_master_name": "ai-limits",
"redis_sentinels": [
{
"host": "10.0.3.20",
"port": 26379
},
{
"host": "10.0.3.21",
"port": 26379
}
],
"redis_password": "<secret>"
}
Allow 10000 tokens a minute on a route
Counts the tokens ai-proxy or ai-proxy-multi on the same route report from the model's answer.
Under Basics choose One budget of tokens, set Limit to 10000 and set Time window to 60 seconds. Under Behavior set Rejected code to 429.
{
"limit": 10000,
"time_window": 60,
"rejected_code": 429
}
What you will see
- A prompt is passed to the model and answered
200; the answer carriesX-Ai-Ratelimit-Limit-Ai-Proxy-Openai-Compatible: 10000, the budget of the window. - The same answer carries
X-Ai-Ratelimit-Remaining-Ai-Proxy-Openai-Compatible: 9970: the tokens the model reported for this answer are already taken from the budget. - Behind ai-proxy-multi the header is named after the instance that answered,
X-Ai-Ratelimit-Limit-Primary: 10000.
Count an output token three times as much as an input token
The names are the ones the model's answer carries: prompt_tokens and completion_tokens for a chat completion, input_tokens and output_tokens for a Messages or Responses answer; a name the answer lacks counts as zero.
Under Basics choose One budget of tokens, set Limit to 20000, set Time window to 60 seconds, set Limit strategy to expression and set Cost expression to prompt_tokens + 3 * completion_tokens.
{
"limit": 20000,
"time_window": 60,
"limit_strategy": "expression",
"cost_expr": "prompt_tokens + 3 * completion_tokens"
}
Give each consumer its own token budget an hour
Needs an authentication plugin on the same route, which sets the consumer name, and ai-proxy or ai-proxy-multi, which report the tokens the model used.
Under Basics choose A budget for each key and fill Rules as in the JSON below.
{
"rules": [
{
"count": 50000,
"time_window": 3600,
"key": "$consumer_name"
}
]
}
What you will see
- A prompt sent with the key of mobile-app is answered
200; the answer carriesX-Ai-1-Ratelimit-Limit: 50000, that consumer's budget for the hour. - The same answer carries
X-Ai-1-Ratelimit-Remaining: 49970: the tokens of this answer are taken from that consumer's budget alone. - A further prompt from the same consumer, here behind ai-proxy-multi, is answered with
X-Ai-1-Ratelimit-Remaining: 49940; the count carries on across the two plugins. - A prompt without a key is answered
401by the authentication plugin, so no budget is touched.
Things to know
- Limit — Behind ai-proxy the headers name the plugin and its provider. A first answer carries
X-Ai-Ratelimit-Limit-Ai-Proxy-Openai-Compatiblewith10000, and after an answer of 30 tokens the remaining count is9970. - Limit — Text is read on each request as a $variable, ${name}, $env:// or $secret:// reference. Any other text is saved and then fails every request with 500.
- Time window — A Limit without a Time window is refused when you save.
- Time window — Text is read on each request as a $variable, ${name}, $env:// or $secret:// reference. Any other text is saved and then fails every request with 500.
- Instances — Behind ai-proxy-multi the headers carry the instance's own name. An instance named
primarygivesX-Ai-Ratelimit-Limit-Primary. - Instances — Each row's name must match the instance the AI proxy picks; the plain ai-proxy is named ai-proxy-<provider>. A row no request picks is never counted, and a limit set outside this list counts every instance without a row.
- Key — A rule keyed by
$consumer_namekeeps one budget per consumer, whichever proxy plugin answers. The consumer's answers carryX-Ai-1-Ratelimit-Remainingwith49970, then49940, and a request without a key is answered401by key-auth. - Key — A rule whose key resolves to nothing is skipped, and with no rule left the request is answered
500. - Header prefix — Without a Header prefix, the headers of the first rule are
X-Ai-1-Ratelimit-LimitandX-Ai-1-Ratelimit-Remaining. - Rules — A Limit beside Rules is refused when you save, so a configuration takes one shape or the other.
- Cost expression — With
prompt_tokens + 3 * completion_tokens, an answer of 10 prompt and 20 completion tokens takes 10 plus three times 20 from the budget, and 20000 becomes19930. - Cost expression — A name the model's answer does not carry counts as zero.
- Cost expression — An expression that does not compile is refused when you save.
- Redis host — With Policy
redis, a configuration without a Redis host is refused when you save. - Rejected code — A code outside
200to599,600for example, is refused when you save. - 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.
- AdviceSet a Rejected code,
429for example, so a client over its budget can tell it apart from a fault of the model.
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 budget of tokens
limitinteger or stringRequired unless Instances is setThe number of tokens allowed in each window, as a number or a request variable such as $http_x_token_limit. Needs Time window beside it.
pattern
^([0-9]*[1-9][0-9]*|[$](env|secret)://.+|[$][{][^}]*[}]|[$][A-Za-z_][A-Za-z0-9_.]*)$time_windowinteger or stringRequired unless Instances is setHow long each window of Limit lasts: a number, or a $variable read from every request, such as $http_x_window. Needs Limit beside it.
pattern
^([0-9]*[1-9][0-9]*|[$](env|secret)://.+|[$][{][^}]*[}]|[$][A-Za-z_][A-Za-z0-9_.]*)$instancesarray of objectsRequired unless Limit with Time window is setinstances[].limitinteger or stringRequiredThe tokens this instance may use in each window: 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_.]*)$instances[].namestringRequiredinstances[].time_windowinteger or stringRequiredHow long each window of this instance 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_.]*)$A budget for each key
rulesarray of objectsRequiredBudgets per key, each keyed by a request variable written with its dollar sign, such as $remote_addr. The header prefix names the X-AI-<prefix>-RateLimit-* headers of that rule.
rules[].countinteger or stringRequiredThe tokens one key may use 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[].keystringRequiredrules[].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.
limit_strategystringdefault total_tokensWhich count the limit is measured on: total tokens, prompt tokens, completion tokens, or an expression of your own.
When Limit strategy is expression
cost_exprstringRequiredAn arithmetic expression over the usage the model reports, such as input_tokens + output_tokens. A name the answer does not carry counts as zero.
input_tokens + cache_creation_input_tokens + output_tokensBehavior
policystringdefault localWhen Policy is redis
redis_hoststringRequiredredis_portintegerdefault 6379redis_databaseintegerdefault 0redis_usernamestringredis_passwordstringStored on the engine and shown only to roles that manage this object.
redis_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 1000redis_keepalive_poolintegerdefault 100redis_keepalive_timeoutintegerdefault 10000When Policy is redis-cluster· 8 fields
redis_cluster_namestringRequiredredis_cluster_nodesarray of stringsRequiredredis_cluster_sslbooleandefault falseredis_cluster_ssl_verifybooleandefault falseredis_passwordstringStored on the engine and shown only to roles that manage this object.
redis_timeoutintegerdefault 1000redis_keepalive_poolintegerdefault 100redis_keepalive_timeoutintegerdefault 10000When Policy is redis-sentinel· 13 fields
redis_master_namestringRequiredredis_sentinelsarray of objectsRequiredredis_sentinels[].hoststringRequiredredis_sentinels[].portintegerRequiredredis_rolestringdefault masterredis_databaseintegerdefault 0redis_usernamestringredis_passwordstringStored on the engine and shown only to roles that manage this object.
sentinel_usernamestringsentinel_passwordstringStored on the engine and shown only to roles that manage this object.
redis_connect_timeoutintegerdefault 1000redis_read_timeoutintegerdefault 1000redis_keepalive_timeoutintegerdefault 60000rejected_codeintegerdefault 503rejected_msgstringshow_limit_quota_headerbooleandefault trueallow_degradationbooleandefault false