速率限制
每个 key 的每分钟请求数限制、每个 key 的消费上限、组织余额、容量限制、会返回的响应头,以及如何退避和重试。
何时阅读本页
你收到了 429、402、529 或 503,或者正在编写一个需要妥善重试的客户端。所有错误类型见错误。
各项限制
| 限制 | 作用对象 | 默认值 | 达到限制时 |
|---|---|---|---|
| 每分钟请求数 | 每个 API key | 1,000 | 429 rate_limit_error,retry-after: 30 |
| 消费上限 | 每个 API key(如果设置了) | 无 | 402 billing_error |
| 余额 | 组织 | 余额为零或负数时返回 402 billing_error | |
| 容量 | 每个模型 | 529 overloaded_error(兼容 OpenAI 的 API 上为 503),retry-after: 5;或 429 rate_limit_error,retry-after: 10 |
在兼容 OpenAI 的 API 上,同样的限制以 OpenAI 格式返回:429 rate_limit_exceeded、402 insufficient_quota 和 503 overloaded。
每分钟请求数
- 每个 key 每分钟可以发送 1,000 个请求,除非 EZAT Router 为它设置了其他限制。各个 key 不共享这个限制。
POST /v1/messages、POST /v1/messages/count_tokens和POST /openai/v1/chat/completions计入限制,模型列表不计入。- 计数在每分钟开始时重置。被拒绝的请求也会计入,所以在紧密循环中重试会让 key 一直超限,直到这一分钟结束。
- 超出限制时,网关返回 429,带
retry-after: 30和消息“This API key exceeded its requests-per-minute limit”。这个请求不计费。
控制台无法修改 key 的这项限制。如需调整,请发邮件至 support@ezat.io,附上 key 的名称,以及控制台中显示的 key 开头部分(ezat_sk_…)。
消费上限
消费上限限制一个 key 在一个周期内能花多少钱。在创建 key 时设置:API Key → 创建 key → 消费上限,填写 金额(美元) 并选择 周期。
| 周期 | 重置时间 |
|---|---|
| 天 | 每天 00:00 UTC |
| 月 | 每月 1 日 00:00 UTC |
| 总计 | 从不重置:涵盖这个 key 的全部消费 |
- 网关在每个请求之前检查上限。key 在当前周期内的消费一旦达到上限,请求就会收到 402,消息类似“This API key reached its monthly spend limit of …”。
- 在上限以内开始的请求会执行完,所以消费可能略微超过上限。
- key 创建后,无法在控制台修改它的上限。请用想要的上限创建一个新 key,把应用切换过去,然后吊销旧 key。
key 的其他设置见 API key。
组织余额
组织内的所有 key 共用一个预付余额。余额为零或负数时,所有 key 都会收到 402,直到有人充值。见计费方式。
容量
每个模型由一个或多个提供方部署提供服务。网关为每个请求选择一个部署;如果它在响应开始前失败,网关最多再尝试另外两个。
- 没有任何部署能接下请求时,网关返回 529
overloaded_error,带retry-after: 5(兼容 OpenAI 的 API 上为 503overloaded)。 - 提供方在限流时,网关返回 429“Rate limited upstream. Please retry shortly.”,带
retry-after: 10。 - 两者都不计费。等待
retry-after之后重试。
完全没有容量的模型在 GET https://api.router.ezat.io/api/models 中显示为 "available": false。见模型。
响应头
| 响应头 | 出现在 | 值 |
|---|---|---|
retry-after | 429、529 和 503 | 重试前需要等待的秒数 |
request-id | 到达模型的请求 | 这个请求在用量中的 id |
x-request-id | 每个响应 | 本次 HTTP 调用的 id |
没有表示剩余配额的响应头(没有 x-ratelimit-* 或 anthropic-ratelimit-*)。
重试
官方 Anthropic 和 OpenAI SDK(Python 和 Node.js)已经会对连接错误、408、409、429 和所有 5xx(包括 529 和 503)进行重试,默认两次,采用指数退避,并在有 retry-after 响应头时等待它给出的时间。可以在客户端或单个请求上用 max_retries(Python)或 maxRetries(Node.js)修改。
如果你自己编写客户端:
- 只重试 429、500、529、503 和连接错误。不要重试 400、401、402、403、404 或 413。
- 有
retry-after响应头时,等待它给出的秒数。 - 否则每次等待的时间按指数增长,并加入随机抖动:例如 0.5 秒、1 秒、2 秒、4 秒,最多约 8 秒,每次再乘以 0.75 到 1 之间的随机系数。
- 尝试几次后停止,报告错误并附上
request-id和x-request-id。 - 重试时按原样发送请求;如果使用了
x-ezat-session请求头(或兼容 OpenAI 的 API 上的prompt_cache_key),保持相同的值。网关会让同一个对话留在持有其提示缓存的提供方上,所以原样重试可以保住缓存,费用更低。
不要原样重试 504 timeout_error:它表示一个非流式请求运行了 590 秒,而 SDK 会把它完整地再发送一次。长请求请改用 "stream": true。
import random
import time
def with_retries(send, attempts=5):
"""send() returns (status, headers, body). Retries 429, 500, 503 and 529."""
for attempt in range(attempts):
status, headers, body = send()
if status not in (429, 500, 503, 529) or attempt == attempts - 1:
return status, headers, body
retry_after = headers.get("retry-after")
if retry_after:
delay = float(retry_after)
else:
delay = min(0.5 * 2**attempt, 8) * random.uniform(0.75, 1.0)
time.sleep(delay)给 AI agent 的说明
- 每个 key 的限制:默认每分钟 1,000 个请求;超出时返回 429,带
retry-after: 30。 - 收到 429、529 或 503 时,等待
retry-after秒,然后用指数退避加抖动重试。官方 SDK 已经这样做了(默认重试 2 次;批处理任务可以调高max_retries/maxRetries)。 - 收到 402 时停止:余额已用完,或 key 已达到消费上限。告诉用户在 https://router.ezat.io/zh/billing 充值,或创建一个上限更高的 key。
- 没有表示剩余配额的响应头。如果需要保持在每分钟限制以内,请自行控制请求速度。
- 重试时保持相同的
x-ezat-session值(或prompt_cache_key),让提示缓存继续生效。 - 长请求使用
"stream": true:非流式请求在 590 秒后以 504 结束。
错误
网关以 Anthropic 和 OpenAI 格式返回的所有 HTTP 状态码和错误类型、各自的原因以及是否应该重试、流式输出中的错误,以及联系支持时需要提供的请求 id。
Create a message POST
Sends a conversation to a model and returns its reply, in Anthropic's Messages format. Set `"stream": true` to receive the reply as Server-Sent Events. The gateway checks `model`, `max_tokens`, `messages` and `stream`. Every other field (`system`, `tools`, `thinking`, `output_config`, `cache_control` and the rest) is passed to the model unchanged, and the model checks it as Anthropic's API would. Two exceptions: - The gateway removes prompt-cache breakpoints (`cache_control`) for models whose `capabilities.promptCaching` is `false` in `GET /api/models`, because their cache is never read back. Those requests are billed as plain input instead of a cache write. - The `anthropic-version` header is accepted but not used. The response's `model` (and `message.model` in `message_start`) is the name you sent, not the canonical id.