统一 New API 网关 · 接口文档
对外只用 /v1 前缀 · Authorization: Bearer <key> · 错误统一 {success:false,error:{code,message,type,request_id}}
1. 认证
所有业务接口都要在请求头带 API Key(同时兼容 api-key / x-api-key 头):
Authorization: Bearer sk-gw-xxxxxxxx
Key 在服务端只保存 SHA-256 哈希,签发时的明文只显示一次。Key 可绑定模型白名单、IP 白名单、RPM、并发数、过期时间、计费倍率。要是明文弄丢了,只能重新签发或轮换(POST /admin/api/keys/{id}/rotate),服务端变不出来。
2. 接口清单
| 方法 | 路径 | 说明 |
| GET | /v1/models | 聚合可用模型列表 |
| POST | /v1/chat/completions | 对话(支持 stream) |
| POST | /v1/responses | OpenAI Responses 兼容 |
| POST | /v1/messages | Anthropic Messages 原生 |
| POST | /v1beta/models/{模型}:generateContent | Gemini 原生(含 :streamGenerateContent) |
| POST | /v1/images/generations · /v1/images/edits | 生图 / 改图(走号池) |
| POST | /v1/embeddings | 向量化 |
| GET | /v1/me | 我的 Key 状态(额度 / 限速 / 模型 / 计费倍率) |
| GET | /v1/usage?days=7 | 我的用量(按天 / 按模型,totals 为全量口径) |
| GET | /v1/pricing | 价目表:带 Key 访问会直接给出「你的实际单价」 |
| GET | /v1/statement.csv?days=30 | 自助下载账单明细(只有自己的数据,可直接拿去报销) |
| GET | /v1/topups | 我的充值流水 |
公开无鉴权接口:GET /api/pricing(对外挂牌价,已按 settings.publicPriceMultiplier 加价,不含内部渠道名)与 /healthz。
3. 快速开始
你的接入地址是 (加载中)——下面所有示例都会按你当前打开的站点自动填好地址,直接复制就能跑,不用再手动改 IP 和端口。 (本页正文目前只有中文,导航与标题已随语言切换)
curl
curl http://127.0.0.1:3100/v1/chat/completions \
-H "Authorization: Bearer sk-gw-xxx" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"你好"}]}'
Python(官方 openai 包,只改两个参数)
pip install openai
# 唯一的区别就是 base_url 和 api_key,其余代码原样搬过来
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:3100/v1", api_key="sk-gw-xxx")
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "你好"}],
)
print(resp.choices[0].message.content)
Node / TypeScript
npm i openai
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "http://127.0.0.1:3100/v1", apiKey: "sk-gw-xxx" });
const r = await client.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "hi" }] });
console.log(r.choices[0].message.content);
Anthropic 官方 SDK(原生 Messages 协议)
pip install anthropic
import anthropic
cli = anthropic.Anthropic(base_url="http://127.0.0.1:3100", api_key="sk-gw-xxx")
msg = cli.messages.create(model="claude-sonnet-4-5", max_tokens=64, messages=[{"role": "user", "content": "hi"}])
print(msg.content[0].text)
4. 流式(SSE)
with client.chat.completions.create(model="gpt-4o-mini", messages=[...], stream=True) as s:
for chunk in s:
if not chunk.choices: # 最后一帧可能只带 usage,没有 choices
continue
print(chunk.choices[0].delta.content or "", end="", flush=True)
裸 HTTP 看是这样(每帧以 data: 开头,收尾是 data: [DONE]):
data: {"id":"chatcmpl-x","choices":[{"index":0,"delta":{"content":"你"},"finish_reason":null}]}
data: {"id":"chatcmpl-x","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
流式的费用怎么算:token 数写在流的最后几帧里,所以网关要等客户收完整条流才结算,
这时响应头会标记 x-gateway-billing: deferred——不代表不收费,只是金额延后计入。
网关会自动向上游索取 stream_options.include_usage 拿精确用量;万一上游不给,会按正文字符数折算,
并在这条调用的日志里打上 usageEstimated: true,账单请你安心,大意是会估得略保守而不是不收。
客户端中途断开也算用量:已经吐出去的内容照常计费(这是所有云厂商的通用做法)。
5. 响应头(每次调用都会带)
| 头 | 含义 |
x-request-id | 全链路请求 ID,拿它可以到后台「日志检索」里原样回放 |
x-gateway-channel | 本次命中的渠道 |
x-gateway-account | 命中的号池账号(有号池时) |
x-gateway-revenue-usd | 本次向你收取的金额(美元)。对账请以它为准 |
x-gateway-billing | 流式时为 deferred(延后结算),此时上面这个头还没算出真实金额,别当成免费,实际金额以账单/用量接口为准 |
x-gateway-cost-usd | 上游采购成本,仅管理员可见(普通调用不返回,避免泄露底价) |
x-quota-limit-usd / x-quota-remaining-usd | 我的额度上限 / 剩余;不限额度时给 -1,客户端一套解析逻辑走天下 |
x-ratelimit-limit-requests / -remaining-requests | 本 Key 每分钟额度与剩余 |
x-ratelimit-reset-seconds | 距离额度重置还有几秒 |
x-gateway-attempts | 这一发总共试了几次(>1 表示中途换过渠道或号池账号) |
x-gateway-retry-trace | 每次重试的原因(URL 编码,解码后形如 ch_x/acc_2@401:号池账号故障,换号重试) |
retry-after | 被限流时建议等待秒数 |
6. 计费与额度
实际扣费 = 模型基准单价 × 你的计费倍率。倍率就写在 GET /v1/me 的 billing.priceMultiplier 里,
带 Key 访问 /v1/pricing 会直接列出你的实际单价,不用自己乘。
| 我要干嘛 | 怎么查 |
| 还剩多少钱 | GET /v1/me → key.remainingUSD,或看每个响应的 x-quota-remaining-usd |
| 这个月花了多少 | GET /v1/usage?days=30 → totals.billedUSD(全量口径,不受分页限制) |
| 导出明细去报销 | GET /v1/statement.csv?days=30(CSV 带 UTF-8 BOM,Excel 直接打开不乱码) |
| 我充过几次值 | GET /v1/topups |
| 凌晨脚本跑崩 | 先看响应头是不是 x-quota-remaining-usd: 0,再看是不是 429 insufficient_quota |
对账口径提醒:/v1/usage 的 totals 是全量汇总,detail 才是分页明细(带 truncated 标记)。别拿 detail 当全量去核对,否则数字一定对不上。
7. 错误码与排查动作
| HTTP | code | 含义 | 你该做什么 |
| 400 | invalid_request | 参数缺失 / JSON 非法 / 上游地址被 SSRF 策略拦下 | 先检查 JSON 能否解析;管理端配渠道时确认 baseUrl 是公网可解析地址 |
| 401 | invalid_api_key | Key 缺失、错误、被吊销或过期 | 确认请求头是 Bearer sk-gw-…;Key 被吊销/过期只能找管理员重发 |
| 403 | permission_denied | 模型不在白名单 / IP 不在白名单 | 核对 /v1/me 里的 models;换了出口 IP 要找管理员改白名单 |
| 404 | model_not_found | 没有渠道支持该模型 | GET /v1/models 看可用清单;模型名别混用厂商前缀 |
| 413 | payload_too_large | 请求体超过 16MB | 图片先压缩或用 URL 传 |
| 429 | rate_limit_exceeded | RPM / 并发触顶 | 读 retry-after 退避重试;平时盯 x-ratelimit-remaining-requests 主动降速 |
| 429 | insufficient_quota | Key 额度用尽 | 找管理员充值(加法入账,有流水可查) |
| 429 | daily_budget_exceeded | 网关今日全局预算已到顶 | 明天再来;管理员可在 /admin/api/budget 看是几点烧穿的 |
| 502 | upstream_error | 上游返回错误 | 拿 x-request-id 让管理员查日志;连续失败会临时熔断该渠道 |
| 503 | channel_not_available | 无可用渠道 / 号池耗尽 | 等渠道冷却(默认 60s)结束;管理员可在号池页「复活全部」 |
| 504 | upstream_timeout | 上游超时 | 换个模型或稍后重试;默认超时 120s |
上游返回的原始错误我们会原样透传(官方 SDK 照常能读),
并额外塞进两个字段:request_id(和响应头同值)与 gateway_hint(告诉你拿它去哪儿查)。
只加字段、不改字段,所以不会破坏任何客户端的解析。
觉得网关「时快时慢」时,先看 x-gateway-attempts:
大于 1 说明这一发中途换过渠道或号池账号,耗时是被重试吃掉的,不是网关抽风。
排障口诀:先抄下响应里的 x-request-id,再到控制台「日志检索」粘贴查询,
那一条记录带有渠道、账号、模型、token、成本、耗时、错误码,基本一步定位。
8. 从官方迁移
| 原先 | 改成 | 还需要动什么 |
https://api.openai.com/v1 | http://网关地址/v1 | 只改 base_url + api_key,其余代码不动 |
https://api.anthropic.com | http://网关地址(不带 /v1) | Anthropic SDK 的 base_url 不要加版本段 |
Gemini generativelanguage.googleapis.com | 网关的 /v1beta/models/… | 路径中的模型名与 :generateContent 保持一致 |
已验证可直接对接的客户端:Cherry Studio / CC Switch / Claude Code Router(控制台「资产」页一键导出配置文件)。
这三家的配置要求 本次签发 的密钥明文——旧密钥的明文服务端无法还原,所以导出前需要「轮换」或新建。
9. 兼容性自检
每次改动后跑 npm run verify-sdk,用官方 SDK 端到端验收,当前 6/6 全绿:
| 用例 | 结果 |
openai.models.list() | 通过 |
openai.chat.completions.create() 非流式 | 通过(含 usage 12/8) |
openai.chat.completions.create(stream) | 通过(收到 [DONE] 收尾帧) |
错误 Key → 官方 SDK 抛 AuthenticationError | 通过 |
anthropic.messages.create() 非流式(透传) | 通过 |
anthropic.messages.stream() 流式(透传) | 通过 |
10. 运维接口
| 方法 | 路径 | 说明 |
| GET | /healthz · /readyz | 存活 / 就绪探针 |
| GET | /admin/api/metrics | Prometheus 文本指标 |
| POST | /admin/api/keys | 签发密钥(返回一次性明文) |
| POST | /admin/api/keys/batch | 批量签发试用 Key(≤50) |
| POST | /admin/api/keys/{id}/topup | 充值(在原额度上做加法并留流水) |
| POST | /admin/api/keys/{id}/rotate | 轮换密钥,旧 Key 立即失效 |
| GET | /admin/api/keys/{id}/statement.csv | 客户版账单(脱敏,可直接发客户) |
| GET | /admin/api/budget | 今日预算账本:已用 / 剩余 / 预计烧穿时间 |
| GET | /admin/api/billing?days=30 | 成本 / 收入 / 毛利 |
| GET | /admin/api/channels | 渠道清单,含「未配价」白送提醒 |
| GET | /admin/api/usage?requestId=... | 按 request_id 查日志 |
| GET | /admin/api/health/deep | 存储/限流/号池/锁定 IP 深度体检 |
管理接口全部需要 Authorization: Bearer <ADMIN_KEY>,连续失败 10 次锁定该 IP 15 分钟。
Admin Key 不允许使用占位密钥启动(检测到 change-me 之类直接拒绝启动),本机的随机密钥用 node scripts/adminkey.mjs internal 取。← 返回控制台