限制与错误码
一页看完所有会挡住你的东西:约定、超时、限流、以及 14 个错误码分别该怎么处理。
约定
| 项 | 约定 |
|---|---|
| 幂等 | 带 Idempotency-Key 头的请求,7 天内同键重放返回首发结果、不重复扣费;同键仍在进行中返回 REQUEST_IN_PROGRESS(409,带 Retry-After)。图片任务和 Gemini 原生接口必须带这个头 |
| 关联编号 | 带 X-Request-ID 头(≤ 64 字符),响应回 X-Client-Request-Id,之后可用 GET /v1/requests/by-client/{id} 找回这一笔。它只是个标签,不产生幂等 |
| 超时 | 单次请求总预算 300 秒,首 token 60 秒;超过返回 UPSTREAM_TIMEOUT(504) |
| 断开 | 客户端中途断开会取消模型服务方那一侧的调用,已产生的用量照常结算 |
| 白名单 | key 可以限定可用别名和来源 IP,越界返回 PERMISSION_DENIED(403);不在白名单里的别名在 /v1/models 里也看不到 |
| 追踪 | 每个响应带 X-Request-Id(同响应体 request_id)和 X-Route-Id。出问题时把这两个值一起报过来 |
| 隐私 | 请求与响应正文默认不留存 |
频率限制
每把 key 有自己的每秒请求数上限(一秒固定窗口)。平台默认每 key 30 次/秒;在控制台的 API Key 页可以给某把 key 设更严的 rps_limit(1 到平台默认之间),留空就用默认。
每一个被限流管着的响应都带用量头,不只是被拒的那个——好让你在被拒之前就慢下来:
HTTP/1.1 200 OK
X-RateLimit-Limit: 1
X-RateLimit-Remaining: 0
超了返回 429,多一个 Retry-After:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-RateLimit-Limit: 1
X-RateLimit-Remaining: 0
{"error":{"code":"RATE_LIMITED","message":"rate limit exceeded, retry shortly","request_id":"req_01M2M96HPG0DWFMKZQ5HN9JT9P"}}
窗口就是一秒宽,所以 Retry-After 永远是 1——等一秒再发,别退避成指数,也别立刻重试。
| 头 | 含义 |
|---|---|
X-RateLimit-Limit |
这把 key 当前生效的每秒上限 |
X-RateLimit-Remaining |
这一秒还剩多少次 |
Retry-After |
等几秒再试,恒为 1 |
被限流的请求根本没发出去,不扣钱。
另外两种 429 不是频率问题,重试策略不同:
BUDGET_EXCEEDED:账户的日 / 月预算用完了,重试没有意义,去控制台调预算或充值。MODEL_RATE_LIMITED:这个别名当前拿不到配额,带Retry-After时照它退避重试通常能过。
服务状态
怀疑是平台的问题而不是你的代码时,先看 GET /status。免鉴权、故障时也能读:
curl https://api.apipace.com/status
{
"window_minutes": 15,
"updated_at": "2026-09-16T04:55:06.073713Z",
"models": [
{"id":"claude-sonnet-4.6","requests":128,"upstream_error_rate_bp":0,"p50_latency_ms":1840},
{"id":"kimi-k3","requests":12,"upstream_error_rate_bp":833,"p50_latency_ms":940}
],
"overall": {"requests":140,"upstream_error_rate_bp":71}
}
最近 15 分钟的对话别名健康度,30 秒更新一次(Cache-Control: public, max-age=30)。
upstream_error_rate_bp 是基点:833 = 8.33%。它只统计平台该负责的失败——模型服务方报错、超时、限流,以及平台内部错误;参数写错、鉴权失败、余额不足这类由你造成的 4xx 计进 requests 但不计进错误率,所以这个数字不会因为你自己调错而变难看。p50_latency_ms 是端到端总耗时的中位数。
错误信封
所有错误一个形状,四百到五百全都一样:
{"error":{"code":"AUTH_FAILED","message":"missing credentials: send Authorization: Bearer ak-...","request_id":"req_01M2M6Q5AE31PQTE9AJHGRJPVD"}}
message 是给人看的说明,不含服务方原文、凭据或任何供应商身份。程序请判 code,不要判 message。
14 个错误码
这 14 个码是冻结的,不会悄悄增删。
| code | HTTP | 什么情况 | 怎么办 |
|---|---|---|---|
INVALID_REQUEST |
400 | 参数或请求体不合法 | 改请求,重试无用 |
AUTH_FAILED |
401 | key 缺失、无效、已禁用或过期 | 检查 Authorization / x-api-key 是否带上、是否被截断 |
INSUFFICIENT_BALANCE |
402 | 可用余额不够预占 | 充值,或把 max_tokens 调小 |
PERMISSION_DENIED |
403 | 别名或来源 IP 不在这把 key 的白名单里 | 去控制台改白名单 |
MODEL_NOT_FOUND |
404 | 别名不存在,或对这把 key 不可见 | 用 GET /v1/models 核对拼写 |
NOT_FOUND |
404 | 资源不存在,包括查别人的 request_id |
核对编号和账户 |
REQUEST_IN_PROGRESS |
409 | 同一个 Idempotency-Key 还在进行中 |
按 Retry-After 等一会儿再查 |
CONTENT_BLOCKED |
422 | 安全拦截 | 改内容,重试无用 |
BUDGET_EXCEEDED |
429 | 账户日 / 月预算用完 | 去控制台调预算 |
RATE_LIMITED |
429 | 这把 key 的每秒请求数超限 | 按 Retry-After 退避 |
MODEL_RATE_LIMITED |
429 | 这个别名当前拿不到配额 | 按 Retry-After 退避,或换个别名 |
UPSTREAM_ERROR |
502 | 模型服务方返回失败(已脱敏) | 可重试;反复出现请带 X-Request-Id 和 X-Route-Id 找我们 |
UPSTREAM_TIMEOUT |
504 | 超过 300 秒总预算或 60 秒首 token | 缩短输入、或改用流式 |
INTERNAL_ERROR |
500 | 平台内部错误 | 带上两个追踪号找我们 |
哪些会扣钱:402 / 403 / 404 / 429 一律不扣(请求根本没发出去);502 / 504 不扣;200 按实际用量扣。
几个容易踩的点
响应里没有钱。 对话接口的响应体只有 token,sales_amount 在 GET /v1/requests/{id} 里。这是有意的:扣费以账本为准,不以响应体为准。
model 填的是别名。 填厂商原始模型名(gpt-4o、claude-3-5-sonnet-20241022 这种)会直接 404:
{"error":{"code":"MODEL_NOT_FOUND","message":"unknown model \"gpt-4o\"","request_id":"req_01M2M6Q6N5W1R2J26QB7RGVKX3"}}
Gemini 系列别名只认原生协议。 拿 gemini-* 去打 /v1/chat/completions 会被拒。见Gemini 原生接口。
图片不是对话接口。 gpt-image-2-official 走 POST /v1/image-tasks 的异步任务,不能填进 messages 请求里。见图片生成。