> For the complete documentation index, see [llms.txt](https://docs.otari.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otari.ai/api-reference/rate-limits.md).

# Rate limits

## List Rate Limit Rules

> List every rule in effect: the config.yml rules, then the ones stored here.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"RateLimitRulesPublic":{"description":"Every rule in effect: config-file rules first, then stored ones, each in name order.","properties":{"rules":{"items":{"$ref":"#/components/schemas/RateLimitRulePublic"},"title":"Rules","type":"array"}},"required":["rules"],"title":"RateLimitRulesPublic","type":"object"},"RateLimitRulePublic":{"additionalProperties":false,"description":"One rule in effect, and where it is defined.","properties":{"budgets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The budget IDs a `per: budget` rule counts, each on its own; unset counts every budget. Refused on any other rule.","title":"Budgets"},"lease_sec":{"default":900,"description":"How long a max_concurrent slot is held at most. A slot is given back when its response ends; this bounds what a process that dies mid-request keeps.","exclusiveMinimum":0,"title":"Lease Sec","type":"number"},"max_concurrent":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests in flight at once.","title":"Max Concurrent"},"mode":{"default":"enforce","description":"'enforce' (the default) refuses a request over the limit with 429. 'report' counts the same way but serves the request, naming the rule in the Otari-Rate-Limit-Exceeded response header, so a limit can be watched before it is enforced.","enum":["enforce","report"],"title":"Mode","type":"string"},"models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The models a `per: model` rule limits, each as instance:model (the provider instance the model is called through, then the model), each counted on its own. Required there and refused on any other rule.","title":"Models"},"name":{"description":"Names the rule in a 429's detail and in the counter's key. Unique across rate_limits.","pattern":"^[A-Za-z0-9_.-]+$","title":"Name","type":"string"},"per":{"description":"What one count is shared by.","enum":["deployment","key","user","model","budget"],"title":"Per","type":"string"},"rpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests per minute.","title":"Rpm"},"source":{"description":"'config' for a rule from config.yml, which is read-only here; 'dashboard' for a stored one.","enum":["config","dashboard"],"title":"Source","type":"string"},"tpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Tokens per minute, counted on what each request used; see tpm_admission for how a request is admitted.","title":"Tpm"},"tpm_admission":{"default":"used","description":"How a tpm limit admits a request. 'used' (the default) admits a request while the minute's tokens are under the limit and counts what it used once it completes, as LiteLLM does. 'estimate' holds the request's estimate (prompt plus max output, or budget_estimate_default_output_tokens) while it runs and refuses it when that does not fit, which is how providers count their own quotas: use it for a limit meant to stay under one.","enum":["used","estimate"],"title":"Tpm Admission","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"When a stored rule last changed.","title":"Updated At"}},"required":["name","per","source"],"title":"RateLimitRulePublic","type":"object"}}},"paths":{"/api/v1/rate-limits":{"get":{"description":"List every rule in effect: the config.yml rules, then the ones stored here.","operationId":"rate-limits-list_rate_limit_rules","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitRulesPublic"}}},"description":"Successful Response"}},"summary":"List Rate Limit Rules","tags":["rate-limits"]}}}}
```

## Create Rate Limit Rule

> Add a rule. It applies from the next request on this replica, and on every replica within 30 seconds.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"RateLimitRuleCreate":{"additionalProperties":false,"description":"A rule to add. The same fields, limits and validation as a ``rate_limits`` entry in config.yml.","properties":{"budgets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The budget IDs a `per: budget` rule counts, each on its own; unset counts every budget. Refused on any other rule.","title":"Budgets"},"lease_sec":{"default":900,"description":"How long a max_concurrent slot is held at most. A slot is given back when its response ends; this bounds what a process that dies mid-request keeps.","exclusiveMinimum":0,"title":"Lease Sec","type":"number"},"max_concurrent":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests in flight at once.","title":"Max Concurrent"},"mode":{"default":"enforce","description":"'enforce' (the default) refuses a request over the limit with 429. 'report' counts the same way but serves the request, naming the rule in the Otari-Rate-Limit-Exceeded response header, so a limit can be watched before it is enforced.","enum":["enforce","report"],"title":"Mode","type":"string"},"models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The models a `per: model` rule limits, each as instance:model (the provider instance the model is called through, then the model), each counted on its own. Required there and refused on any other rule.","title":"Models"},"name":{"description":"Names the rule in a 429's detail and in the counter's key. Unique across rate_limits.","pattern":"^[A-Za-z0-9_.-]+$","title":"Name","type":"string"},"per":{"description":"What one count is shared by.","enum":["deployment","key","user","model","budget"],"title":"Per","type":"string"},"rpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests per minute.","title":"Rpm"},"tpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Tokens per minute, counted on what each request used; see tpm_admission for how a request is admitted.","title":"Tpm"},"tpm_admission":{"default":"used","description":"How a tpm limit admits a request. 'used' (the default) admits a request while the minute's tokens are under the limit and counts what it used once it completes, as LiteLLM does. 'estimate' holds the request's estimate (prompt plus max output, or budget_estimate_default_output_tokens) while it runs and refuses it when that does not fit, which is how providers count their own quotas: use it for a limit meant to stay under one.","enum":["used","estimate"],"title":"Tpm Admission","type":"string"}},"required":["name","per"],"title":"RateLimitRuleCreate","type":"object"},"RateLimitRulePublic":{"additionalProperties":false,"description":"One rule in effect, and where it is defined.","properties":{"budgets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The budget IDs a `per: budget` rule counts, each on its own; unset counts every budget. Refused on any other rule.","title":"Budgets"},"lease_sec":{"default":900,"description":"How long a max_concurrent slot is held at most. A slot is given back when its response ends; this bounds what a process that dies mid-request keeps.","exclusiveMinimum":0,"title":"Lease Sec","type":"number"},"max_concurrent":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests in flight at once.","title":"Max Concurrent"},"mode":{"default":"enforce","description":"'enforce' (the default) refuses a request over the limit with 429. 'report' counts the same way but serves the request, naming the rule in the Otari-Rate-Limit-Exceeded response header, so a limit can be watched before it is enforced.","enum":["enforce","report"],"title":"Mode","type":"string"},"models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The models a `per: model` rule limits, each as instance:model (the provider instance the model is called through, then the model), each counted on its own. Required there and refused on any other rule.","title":"Models"},"name":{"description":"Names the rule in a 429's detail and in the counter's key. Unique across rate_limits.","pattern":"^[A-Za-z0-9_.-]+$","title":"Name","type":"string"},"per":{"description":"What one count is shared by.","enum":["deployment","key","user","model","budget"],"title":"Per","type":"string"},"rpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests per minute.","title":"Rpm"},"source":{"description":"'config' for a rule from config.yml, which is read-only here; 'dashboard' for a stored one.","enum":["config","dashboard"],"title":"Source","type":"string"},"tpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Tokens per minute, counted on what each request used; see tpm_admission for how a request is admitted.","title":"Tpm"},"tpm_admission":{"default":"used","description":"How a tpm limit admits a request. 'used' (the default) admits a request while the minute's tokens are under the limit and counts what it used once it completes, as LiteLLM does. 'estimate' holds the request's estimate (prompt plus max output, or budget_estimate_default_output_tokens) while it runs and refuses it when that does not fit, which is how providers count their own quotas: use it for a limit meant to stay under one.","enum":["used","estimate"],"title":"Tpm Admission","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"When a stored rule last changed.","title":"Updated At"}},"required":["name","per","source"],"title":"RateLimitRulePublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/rate-limits":{"post":{"description":"Add a rule. It applies from the next request on this replica, and on every replica within 30 seconds.","operationId":"rate-limits-create_rate_limit_rule","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitRuleCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitRulePublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Rate Limit Rule","tags":["rate-limits"]}}}}
```

## Update Rate Limit Rule

> Change a stored rule. Requests it already counted stay counted. A config.yml rule answers 409.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"RateLimitRuleUpdate":{"additionalProperties":false,"description":"Fields to change on a stored rule. An omitted field keeps its value; ``null`` clears a limit.\n\nThe merged rule must still set at least one of rpm, tpm or max_concurrent.","properties":{"budgets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The budget IDs a per: budget rule counts; null for every budget, and on any other rule.","title":"Budgets"},"lease_sec":{"anyOf":[{"exclusiveMinimum":0,"type":"number"},{"type":"null"}],"description":"How long a max_concurrent slot is held at most.","title":"Lease Sec"},"max_concurrent":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests in flight at once.","title":"Max Concurrent"},"mode":{"anyOf":[{"enum":["enforce","report"],"type":"string"},{"type":"null"}],"description":"'enforce' refuses a request over the rule; 'report' serves it and names the rule.","title":"Mode"},"models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The instance:model names a per: model rule limits; null for any other rule.","title":"Models"},"per":{"anyOf":[{"enum":["deployment","key","user","model","budget"],"type":"string"},{"type":"null"}],"description":"What one count is shared by.","title":"Per"},"rpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests per minute.","title":"Rpm"},"tpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Tokens per minute.","title":"Tpm"},"tpm_admission":{"anyOf":[{"enum":["used","estimate"],"type":"string"},{"type":"null"}],"description":"'used' counts only what a request used; 'estimate' holds its estimate.","title":"Tpm Admission"}},"title":"RateLimitRuleUpdate","type":"object"},"RateLimitRulePublic":{"additionalProperties":false,"description":"One rule in effect, and where it is defined.","properties":{"budgets":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The budget IDs a `per: budget` rule counts, each on its own; unset counts every budget. Refused on any other rule.","title":"Budgets"},"lease_sec":{"default":900,"description":"How long a max_concurrent slot is held at most. A slot is given back when its response ends; this bounds what a process that dies mid-request keeps.","exclusiveMinimum":0,"title":"Lease Sec","type":"number"},"max_concurrent":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests in flight at once.","title":"Max Concurrent"},"mode":{"default":"enforce","description":"'enforce' (the default) refuses a request over the limit with 429. 'report' counts the same way but serves the request, naming the rule in the Otari-Rate-Limit-Exceeded response header, so a limit can be watched before it is enforced.","enum":["enforce","report"],"title":"Mode","type":"string"},"models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The models a `per: model` rule limits, each as instance:model (the provider instance the model is called through, then the model), each counted on its own. Required there and refused on any other rule.","title":"Models"},"name":{"description":"Names the rule in a 429's detail and in the counter's key. Unique across rate_limits.","pattern":"^[A-Za-z0-9_.-]+$","title":"Name","type":"string"},"per":{"description":"What one count is shared by.","enum":["deployment","key","user","model","budget"],"title":"Per","type":"string"},"rpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Requests per minute.","title":"Rpm"},"source":{"description":"'config' for a rule from config.yml, which is read-only here; 'dashboard' for a stored one.","enum":["config","dashboard"],"title":"Source","type":"string"},"tpm":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"description":"Tokens per minute, counted on what each request used; see tpm_admission for how a request is admitted.","title":"Tpm"},"tpm_admission":{"default":"used","description":"How a tpm limit admits a request. 'used' (the default) admits a request while the minute's tokens are under the limit and counts what it used once it completes, as LiteLLM does. 'estimate' holds the request's estimate (prompt plus max output, or budget_estimate_default_output_tokens) while it runs and refuses it when that does not fit, which is how providers count their own quotas: use it for a limit meant to stay under one.","enum":["used","estimate"],"title":"Tpm Admission","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"When a stored rule last changed.","title":"Updated At"}},"required":["name","per","source"],"title":"RateLimitRulePublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/rate-limits/{name}":{"patch":{"description":"Change a stored rule. Requests it already counted stay counted. A config.yml rule answers 409.","operationId":"rate-limits-update_rate_limit_rule","parameters":[{"in":"path","name":"name","required":true,"schema":{"title":"Name","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitRuleUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitRulePublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Rate Limit Rule","tags":["rate-limits"]}}}}
```

## Delete Rate Limit Rule

> Remove a stored rule. A config.yml rule answers 409.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/rate-limits/{name}":{"delete":{"description":"Remove a stored rule. A config.yml rule answers 409.","operationId":"rate-limits-delete_rate_limit_rule","parameters":[{"in":"path","name":"name","required":true,"schema":{"title":"Name","type":"string"}}],"responses":{"204":{"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Delete Rate Limit Rule","tags":["rate-limits"]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.otari.ai/api-reference/rate-limits.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
