OpenAI API 国内怎么调用:只改一行 base_url
不重写业务逻辑、不装代理客户端、不需要海外信用卡。三段可直接复制的前后对照,外加一张能自证「路由是真的」的实测表。
实测时间 2026-08-20 · 网关地址 https://api.tutujin.com
把 SDK 的 base_url 改成 https://api.tutujin.com/v1,把 key 换成控制台里生成的令牌,其余代码一行不动——这就是全部。
之所以能这么干,是因为服务端实现的是 OpenAI 协议本身,不是某个 SDK 的包装层:/v1/chat/completions、/v1/images/generations、/v1/embeddings、/v1/messages 都是真实存在的路由(下面第五节有可复现的验证方法)。
一、一分钟版:三段前后对照
Python(openai SDK v1.x)
from openai import OpenAI
client = OpenAI(
- api_key="sk-你的-openai-key",
- # base_url 默认 https://api.openai.com/v1
+ api_key="你的土土金令牌",
+ base_url="https://api.tutujin.com/v1",
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "你好"}],
)
print(resp.choices[0].message.content)
Node.js(openai v4/v5)
import OpenAI from "openai";
const client = new OpenAI({
- apiKey: process.env.OPENAI_API_KEY,
+ apiKey: process.env.TUTUJIN_KEY,
+ baseURL: "https://api.tutujin.com/v1",
});
const r = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "你好" }],
});
console.log(r.choices[0].message.content);
curl
curl https://api.tutujin.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TUTUJIN_KEY" \
-d '{
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "你好"}]
}'
二、base_url 到底该不该带 /v1(最高频踩坑)
这一条踩的人最多,因为不同工具的约定正好相反:
| 场景 | 正确写法 | 说明 |
|---|---|---|
| OpenAI 官方 SDK(Python / Node) | https://api.tutujin.com/v1 | 要带 /v1,SDK 会在其后拼 /chat/completions |
| curl / 自己写 HTTP 请求 | https://api.tutujin.com/v1/chat/completions | 写完整路径 |
| 多数第三方客户端(LobeChat 等) | https://api.tutujin.com/v1 | 同官方 SDK 约定 |
Claude Code 的 ANTHROPIC_BASE_URL | https://api.tutujin.com | 不要带 /v1,它自己会拼 /v1/messages,详见 Claude Code 那页 |
三种典型错法:写成 https://api.tutujin.com(少了 /v1,SDK 会请求到 /chat/completions)→ 404;写成 https://api.tutujin.com/v1/chat/completions 却又用 SDK(SDK 再拼一次)→ 404;结尾多写一个斜杠 /v1/,部分 SDK 会拼出 //chat/completions → 404。凡是 404,先怀疑 base_url,不要怀疑 key。
三、openai SDK v0.x 与 v1.x 的写法差异
很多老项目还停在 v0.28 的写法,迁移方式不一样:
| v0.x(≤0.28) | v1.x(≥1.0) | |
|---|---|---|
| 配置方式 | 模块级全局变量 | 实例化 OpenAI() 客户端 |
| 地址字段 | openai.api_base | base_url |
| key 字段 | openai.api_key | api_key |
| 调用 | openai.ChatCompletion.create(...) | client.chat.completions.create(...) |
| 取结果 | resp["choices"][0]["message"]["content"] | resp.choices[0].message.content |
# v0.x 老项目的最小改法(不升级 SDK 也能切过来)
import openai
openai.api_key = "你的土土金令牌"
openai.api_base = "https://api.tutujin.com/v1"
r = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "你好"}],
)
print(r["choices"][0]["message"]["content"])
注意字段名是 api_base 不是 base_url——把 v1.x 的字段名抄进 v0.x 代码里,是另一个常见的「改了没生效」原因。
四、怎么拿到 key
- 注册:打开 https://api.tutujin.com/,用邮箱注册。当前只开放邮箱注册——微信、GitHub、Telegram、Discord、OIDC、Passkey 登录在服务端配置里全部为关闭状态,所以不必去找「微信一键登录」入口。
- 验证邮箱:站点开启了邮箱验证(
email_verification: true),收验证码填入即可。 - 创建令牌:控制台 → 令牌 → 添加新令牌。可以给单个令牌设置额度上限,也可以设为无限;建议按项目分别建令牌,方便按项目看用量。
- 充值:按人民币充值额度,500000 quota = 1 美元额度。具体可用的支付渠道与当时单价,以控制台充值页显示为准。
额度换算与倍率的完整算法,见计费拆解那一页。
五、路由实证:怎么确认「协议兼容」不是嘴上说说
这一点你可以自己验,不需要有效 key。用一个故意写错的 key 去打各个路径,看返回码:
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
https://api.tutujin.com/v1/chat/completions \
-H "Authorization: Bearer sk-invalid-probe-000" \
-H "Content-Type: application/json" -d '{}'
2026-08-20 实测结果:
| 路径 | 无效 key 的返回 | 说明 |
|---|---|---|
/v1/chat/completions | 401 new_api_error | 真实路由(对话) |
/v1/images/generations | 401 new_api_error | 真实路由(生图) |
/v1/embeddings | 401 new_api_error | 真实路由(向量) |
/v1/messages | 401 new_api_error | 真实路由(Anthropic 原生协议) |
/v1beta/models | 404 Invalid URL | 对照组:不存在的路由长这样 |
差别在于:真实路由会先做鉴权再拒绝你(401),不存在的路由直接告诉你 URL 无效(404)。这是判断「一个网关到底实现了哪些接口」最省事的办法,对任何中转站都适用。
六、流式、函数调用与其它字段
网关按 OpenAI 协议转发请求体,stream、tools、tool_choice、response_format、temperature 等字段照常传递,SSE 流式响应结构不变:
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "写一首俳句"}],
stream=True,
)
for chunk in r:
print(chunk.choices[0].delta.content or "", end="")
但要说清楚边界:某个字段最终是否生效,取决于该模型自身与其上游通道,不是网关能替所有模型打包票的事。尤其是按次计费(quota_type=1)的聚合型条目,行为可能与官方直连不同。接入生产前,请用你的真实请求先跑一次。
七、报错速查表
| 现象 | 最可能的原因 | 怎么修 |
|---|---|---|
401 Invalid token | 令牌错、被删、额度耗尽,或请求头格式不对 | 确认 Authorization: Bearer <令牌>;到控制台确认令牌状态与余额 |
404 Invalid URL | base_url 路径拼错(多写 / 少写 /v1) | 对照上面第二节的写法表 |
| 提示模型不存在 / 无权限 | 模型名拼错,或该模型不在你的分组 | 查 模型与分组对照表;模型名要写全(含日期后缀) |
429 | 触发限流 | 降低并发、加退避重试;并发上限相关问题请联系客服确认 |
| 响应是 HTML 而不是 JSON | 请求打到了网页而不是 API(通常还是 base_url 问题) | 确认路径以 /v1 开头 |
八、不想写代码:现成客户端一键接入
服务端配置里内置了几个客户端的一键导入模板,把 {key} 换成你的令牌、{address} 换成 https://api.tutujin.com 即可:
| 客户端 | 接入方式 |
|---|---|
| ChatGPT Next Web | URL 参数导入 settings={"key":"...","url":"..."} |
| LobeChat | keyVaults.openai.apiKey + baseURL(记得带 /v1) |
| AI as Workspace | provider=openai,compatibility: strict |
| AMA 问天 | ama://set-api-key?server=...&key=... |
| OpenCat | opencat://team/join?domain=...&token=... |
编辑器场景另有两条路:Cursor 自定义 Base URL(注意它有覆盖不到的功能)与 Claude Code 走 ANTHROPIC_BASE_URL。
常见问题
base_url 要不要带 /v1?
OpenAI SDK 要带(https://api.tutujin.com/v1);curl 写完整路径;Claude Code 的 ANTHROPIC_BASE_URL 相反,不要带 /v1。这是两类工具最容易互相串味的地方。
业务代码要改吗?
不用改。请求体与响应体结构不变,只替换 base_url 与 api_key 两个值。模型名建议同时确认一遍——部分模型名带日期后缀,写短了会报模型不存在。
支持流式输出和 function calling 吗?
协议层按 OpenAI 规范透传。是否真正生效取决于你选的那个模型与其通道,建议接入后用真实请求自测一次,我们不替所有模型做统一承诺。
没有海外账号和信用卡能注册吗?
可以。注册只需要能收验证码的邮箱,国内可直连访问,按人民币充值额度。具体支付渠道以充值页显示为准。
报 401 先查什么?
先看响应体的 type 字段:出现 new_api_error 说明请求已经到达网关、是鉴权被拒(key 的问题),而不是网络或域名的问题。然后依次查:请求头格式 → 令牌是否有效 → 余额 → 模型是否在你的分组。