限流与并发
并发限制的语义、模型列表端点的限流,以及计费口径。
生成类端点没有 QPS 限制
/v1/chat/completions、/v1/images/generations、/v1/videos 这些生成类端点
不受每分钟请求数限制。约束它们的是并发数——同一时刻你能有多少个请求在途。
这两者的区别很重要。QPS 限制下,你应该在收到 429 后退避一段时间;而并发限制下, 正确的做法是等已有请求返回。盲目退避会让吞吐白白降低。
超限时返回:
{
"error": {
"type": "rate_limit_error",
"code": "concurrency_limit",
"message": "concurrency limit reached",
"request_id": "..."
},
"request_id": "..."
}HTTP 状态 429,且不带 Retry-After。
并发额度从哪来
按优先级取第一个命中的:
- 账号上的并发覆盖值(管理员单独配置)
- 套餐或用户组自带的并发额度
任一层配成 0 或未配置,即为不限并发。具体额度在控制台可以看到。
重试跨上游只占一个名额
一次请求内部如果发生了上游故障转移(网关自动换一家上游重试),整个过程只占用一个 并发名额,不会因为内部重试而多扣。
建议的客户端做法
用信号量控制并发,而不是靠捕获 429:
import asyncio
sem = asyncio.Semaphore(8) # 设为你的并发额度,或略低
async def call(payload):
async with sem:
return await client.chat.completions.create(**payload)这样把并发控制在客户端做掉,基本不会触发 concurrency_limit。真的收到了,短暂等待
(1 秒左右)后重试即可,不需要指数退避。
模型列表端点有 QPS 限制
/v1/models 和 /anthropic/v1/models 受 240 次 / 60 秒的用户级限流,固定窗口。
响应头:
| 头 | 说明 |
|---|---|
X-RateLimit-Limit | 恒为 240 |
X-RateLimit-Remaining | 本窗口剩余次数 |
Remaining 可能是负数
它是 限额 - 已用计数,而计数在判断之前就已递增,超限后会继续往下减。所以你可能看到
-3 这样的值。判断时用 <= 0 而不是 == 0。
超限响应体格式特殊(不是统一信封):
{"error": "rate limit exceeded", "scope": "user"}实践上这个额度足够启动时拉一次并缓存。不建议每次业务请求前都查模型列表。
请求体大小
| 端点类型 | 上限 |
|---|---|
对话类(/v1/chat/completions、/v1/responses) | 100 MiB |
| multipart 类(图像编辑、音频上传) | 32 MiB |
超限返回 413 + request_body_too_large。
传大图做视觉理解时注意:base64 编码会让体积膨胀约 33%,一张 30 MB 的原图编码后接近 40 MB。
超时
| 场景 | 上限 |
|---|---|
| 非流式请求 | 120 秒 |
| 流式请求 | 不受上面这条限制 |
| 图像端点内部轮询异步上游 | 120 秒 |
流式请求不受 120 秒总时长限制。 如果你的任务本来就慢(长文生成、深度推理), 用流式能绕开这个上限,顺便还能更早拿到首字。
流式另有几条独立的超时保护:首字超时、总时长超时、中途静默超时,触发时返回 504
并带对应的 code。详见错误码。
计费口径
按用量计费
计费维度取决于端点:
| 端点 | 计费依据 |
|---|---|
| 对话、嵌入 | 输入 / 输出 token |
| 图像 | 输出图片张数 |
| 视频 | 上游返回的实际时长(秒) |
| 语音合成 | 请求中的输入字符数 |
语音合成这条值得单独注意:它按你发出去的字符数算,而不是上游报告的用量。所以在 发请求前你就能准确预估成本。
视频完成时才计费
POST /v1/videos 提交时不计费,只做一次余额预检。等任务成功完成后,按上游返回的
实际秒数结算。
任务失败不计费,无需申请退款。
上游 4xx 不计费
上游返回 4xx 时,网关透传错误但不产生费用。
服务维护期
实例进入排空(drain)状态时,所有非健康检查路径返回:
{"error": "instance draining"}HTTP 状态 503。这是短暂的滚动更新窗口,重试即可恢复。注意这个响应体也不遵循统一 错误信封。
错误码
完整错误码表,以及哪些该重试、哪些不该。
创建消息(Anthropic 兼容) POST
与 Anthropic Messages API 兼容。把官方 SDK 的 `base_url` 指向 `https://api.jucode.cn/anthropic`,或给 Claude Code 设置 `ANTHROPIC_BASE_URL`,即可直接使用。 注意路径**不在** `/v1` 下——完整路径是 `/anthropic/v1/messages`。 ### 两条执行路径,行为不同 **原生路径**——当路由到的上游本身就是 Anthropic 时,你的原始请求字节会被 直接转发(仅改写 `model`)。所有字段,包括本文档未列出的 `top_k`、`thinking`、`cache_control` 等,都会保留。 **桥接路径**——当路由到的是非 Anthropic 上游(如 OpenAI 兼容厂商)时,网关 会把请求降级翻译成 Chat Completions,再把响应还原成 Anthropic 格式。此时 **只有下列字段会保留**,其余字段被静默丢弃: `model` `max_tokens` `system` `messages` `stream` `temperature` `top_p` `stop_sequences` `tools` `tool_choice` `metadata` 你无法预先知道会走哪条路径——它取决于后台的模型路由配置。如果你依赖某个 Anthropic 专有参数,请确认该模型确实路由到 Anthropic 上游。 ### 与官方 API 的差异 - `max_tokens` 在官方 API 中是必填的,本网关**不强制**。仅当 `> 0` 时才转发。 - 桥接路径下 `message_start` 事件的 `usage` 字段恒为 `0`,真实用量在结尾的 `message_delta` 中给出。 - 桥接路径的 SSE **没有 `[DONE]` 哨兵**,以 `event: message_stop` 结束。 - 上游返回的 `reasoning_content`(DeepSeek 系非标准字段)会被转换成 Anthropic 的 `thinking` 块。 - **认证失败(401)返回的是 OpenAI 格式的错误体**,不是 Anthropic 格式。这是 因为认证中间件在进入本路由的处理器之前就已拒绝。Anthropic SDK 的错误解析 可能无法识别该响应体。