JuCode 文档

限流与并发

并发限制的语义、模型列表端点的限流,以及计费口径。

生成类端点没有 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

并发额度从哪来

按优先级取第一个命中的:

  1. 账号上的并发覆盖值(管理员单独配置)
  2. 套餐或用户组自带的并发额度

任一层配成 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/models240 次 / 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 的错误解析 可能无法识别该响应体。

On this page