OpenAI 兼容 API 中转接入教程:只改 base_url 的迁移方法与常见坑
用 OpenAI 兼容 API 中转网关接入多模型的完整流程:SDK 只改 base_url 的迁移法,stream_options 拿真实用量的正确姿势,以及 401/402/404/429/5xx 常见错误码排查。
如果你的代码已经在用官方 openai SDK 调用 GPT 系列模型,接入一个 OpenAI 兼容的 API 中转网关,理论上只需要改一行代码:把 baseURL(或 base_url)指向网关地址,再把 key 换成网关发的 sk-…。但实际接入时,流式响应、错误码、鉴权头这几个地方最容易踩坑。这篇教程按真实调用形态走一遍完整流程。
为什么"OpenAI 兼容"能做到只改一行
OpenAI 兼容,指的是网关在协议层面复刻了 OpenAI 的请求/响应结构——路径是 /chat/completions,请求体里的 model/messages/stream 字段,响应体的 choices/usage 结构,错误体的 {"error": {"message", "type", "code"}} 结构,全部保持一致。这意味着任何"说 OpenAI 协议"的工具——官方 SDK、LangChain、LlamaIndex,甚至一个裸 fetch/requests 调用——都不需要改调用逻辑,只需要换两个配置项:
- base URL:从
https://api.openai.com/v1换成中转网关地址 - API Key:从 OpenAI 的 key 换成网关发的 key
要注意的是,这里说的"网关"是数据面(真正处理 /chat/completions 请求的那一层),和你注册、管理 Key、看账单的控制台站点不是同一个域名。控制台站点不代理 /v1 流量,你的所有请求应该直连网关主机。
三步接入:建 Key、换 base URL、调用模型
- 注册并创建 API Key——登录后进入控制台的 API Keys 页面创建一个新 Key。完整密钥只在创建那一刻显示一次,务必当场保存,后续无法再次查看明文。新账户注册即可自动获得试用额度,不需要绑卡就能先跑通流程。
- 把 base URL 指向网关——JavaScript SDK 用
baseURL,Python SDK 用base_url,裸 HTTP 调用则把所有路径拼在网关地址后面。 - 调用任意模型——把
model字段换成目录里的真实 id。具体有哪些模型可用、id 怎么拼,以模型目录当前展示的为准,不要凭记忆硬编。
裸 HTTP 调用长这样(把 {GATEWAY_BASE_URL} 换成你实际部署的网关地址,示例里用的是文档惯用的占位域名):
curl https://api.apiko.example/v1/chat/completions \
-H "Authorization: Bearer $APIKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "your-model-id",
"messages": [{"role": "user", "content": "Hello"}]
}'
JavaScript(官方 openai 包,只改 baseURL 这一个参数):
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.APIKO_API_KEY,
baseURL: 'https://api.apiko.example/v1',
});
const completion = await client.chat.completions.create({
model: 'your-model-id',
messages: [{ role: 'user', content: 'Hello' }],
});
Python 同理,只需要在实例化客户端时传入 base_url:
from openai import OpenAI
client = OpenAI(
api_key="APIKO_API_KEY",
base_url="https://api.apiko.example/v1",
)
completion = client.chat.completions.create(
model="your-model-id",
messages=[{"role": "user", "content": "Hello"}],
)
三个例子里,业务逻辑(构造 messages、解析 choices[0].message)完全没变——这就是"OpenAI 兼容"这四个字真正省下来的工作量:不用为每个供应商重写一套调用代码,也不用维护多份 SDK 依赖。
流式响应:stream_options.include_usage 是关键
把请求体里的 stream 设为 true,响应会变成 server-sent events:每一行以 data: 开头,携带一个 JSON chunk,最后以 data: [DONE] 结束。这部分和标准 OpenAI 流式协议一致,大多数人踩的坑不在这里,而在怎么拿到真实 token 用量。
普通的流式响应默认不会在中间 chunk 里带 usage 字段——如果你的计费或日志逻辑依赖 token 数,很容易发现流式场景下拿不到数字。解决方法是在请求体里显式加上:
{
"model": "your-model-id",
"messages": [{ "role": "user", "content": "Hello" }],
"stream": true,
"stream_options": { "include_usage": true }
}
加上这个参数后,流的最后一个 chunk会额外携带一个 usage 对象,里面是这次请求真实消耗的 prompt/completion token 数——这正是网关用来计费的同一份数字。也就是说,你在客户端拿到的 usage,和账单上扣的钱,口径是完全一致的。如果你在做用量统计或者按 token 做限流,一定要解析这最后一个 chunk,而不是自己用分词器去估算。
curl 版本的流式请求:
curl https://api.apiko.example/v1/chat/completions \
-H "Authorization: Bearer $APIKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "your-model-id",
"messages": [{"role": "user", "content": "Hello"}],
"stream": true,
"stream_options": {"include_usage": true}
}'
常见错误码:先查这张表再排查代码
接入过程里遇到非 200 响应,错误体结构和 OpenAI 一致,可以先按状态码定位问题,不用逐行翻代码:
| 状态码 | 常见原因 | 处理方式 |
|---|---|---|
| 401 | API Key 缺失或无效 | 检查 Authorization: Bearer 头是否带上了 key,确认这把 key 没有被吊销 |
| 402 / 403 | 余额不足(insufficient_user_quota) | 到控制台 Billing 页面充值后重试 |
| 404 | 模型或路径不存在(model_not_found) | 对照模型目录核对 model id 拼写,同时确认这把 key 有没有设置模型白名单 |
| 429 | 触发限流 | 用指数退避重试,避免瞬时并发过高再次撞限流 |
| 5xx | 上游故障 | 网关会自动跨渠道重试;如果最终仍然失败,这次请求不计费——预扣的额度会自动退款 |
最后一行的 5xx 处理值得展开说一下:请求发出时,网关会按估算金额做一次预扣,等上游真正返回结果后,再按实际 token 用量结算差额。如果上游最终失败(网关已经自动重试过跨渠道的备选路径),预扣金额会全额退回,你不会为一次没有拿到结果的请求付费。这个机制不需要你在客户端做任何额外处理,退款是自动发生的,可以在控制台的用量日志里核对每一笔请求的实际扣费和退款记录。
接下来
整个迁移的工作量,基本就是改一个 baseURL/base_url、换一把 key、按需加上 stream_options.include_usage。如果你想先验证流程再决定要不要长期使用,新账户注册即可获得试用额度,不需要绑卡,可以直接去注册拿一把 key 跑通这套代码;想先了解各模型的实时价格,可以看价格页——上面的单价就是账单实际使用的口径。