OpenAI 完全兼容
把
base_url 换成 你的域名/v1、api_key 换成我们发的密钥,
客户端代码几乎不用改。响应结构、错误结构、用量字段全部对齐。
快速开始
Python(OpenAI SDK)
pip install openai
from openai import OpenAI
client = OpenAI(
api_key="sk-relay-你的密钥",
base_url="https://你的域名/v1",
)
resp = client.chat.completions.create(
model="gpt-5.6-terra",
messages=[
{"role": "system", "content": "你是一个助手"},
{"role": "user", "content": "用一句话介绍你自己"},
],
max_tokens=500,
)
print(resp.choices[0].message.content)
print("本次消费:", resp.model_extra.get("relay_usage"))
Node.js
const OpenAI = require('openai');
const client = new OpenAI({
apiKey: 'sk-relay-你的密钥',
baseURL: 'https://你的域名/v1',
});
const resp = await client.chat.completions.create({
model: 'gpt-5.6-terra',
messages: [{ role: 'user', content: '你好' }],
});
console.log(resp.choices[0].message.content);
curl
curl -X POST https://你的域名/v1/chat/completions \
-H "Authorization: Bearer sk-relay-你的密钥" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-terra",
"messages": [{"role": "user", "content": "你好"}]
}'
鉴权
有两种传法,任选其一:
Authorization: Bearer sk-relay-xxx(OpenAI 标准,推荐)Authorization: sk-relay-xxx或X-API-Key: sk-relay-xxx
密钥只在创建时显示一次
为保障安全,平台不保存密钥原文,关闭后无法再次查看。丢失请删除重建。
对话补全
POST
/v1/chat/completions
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| model | string | 是 | 模型名,取自 /v1/models |
| messages | array | 是 | 标准 OpenAI 消息格式 |
| max_tokens | int | 否 | 最大生成 token 数 |
| temperature | number | 否 | 0~2,部分模型不支持 |
| stream | bool | 否 | 流式输出 |
响应中的计费信息
响应体额外带一个 relay_usage 字段,让你能实时看到本次调用花了多少钱:
{
"id": "chatcmpl-xxx",
"model": "gpt-5.6-terra",
"choices": [{ "message": { "role": "assistant", "content": "你好" }, "finish_reason": "stop" }],
"usage": { "prompt_tokens": 8, "completion_tokens": 2, "total_tokens": 10 },
// 平台追加字段
"relay_usage": {
"request_id": "req_a1b2c3",
"sale": "0.00000100", // 从你余额扣除的金额(= 你实际付的钱)
"unit": "per_1m_token"
}
}
模型列表
GET/v1/models
返回当前所有可调用模型及其售价,格式与 OpenAI 一致,额外携带计费信息:
{
"object": "list",
"data": [{
"id": "gpt-5.6-terra",
"object": "model",
"owned_by": "tugou-ai",
"relay": {
"billing_unit": "per_1m_token",
"sale_price": "0.00000840"
}
}]
}
售价以本接口返回为准
平台价格可能因供应方调价而调整。生产环境建议启动时拉一次本接口缓存模型与价格,
避免用户看到的价格与你本地硬编码的不一致。
同一模型名只会出现一次
平台会自动为每个模型选择最优线路,你看到的名称与价格就是最终计费口径,
不存在同一个名字两个价格的情况。
异步任务(视频 / 图片生成)
视频、音乐、数字人等生成任务耗时较长,走异步协议:提交拿任务号 → 轮询查状态 → 取结果。
POST/v1/async/submit
POST /v1/async/submit
{
"model": "可灵文生视频",
"params": {
"prompt": "一只猫在草地上奔跑",
"duration": 5
}
}
// 响应
{
"request_id": "req_xxx",
"status": "submitted",
"upstream_task_id": "task_abc123"
}
资金时点
提交时按最大可能用量预扣(如按秒计费的模型按 60 秒封顶预扣);
成功时按实际用量结算并退回差额;失败则全额退回,你不会为失败的任务付费。
GET/v1/async/{request_id}
{
"request_id": "req_xxx",
"status": "success", // submitted / running / success / failed / timeout
"progress": 100,
"result_url": "https://.../output.mp4",
"reserved_amount": "3.00", // 预占
"settled_amount": "0.60", // 实际结算
"refunded": "2.40" // 已退回
}
轮询建议
- 视频类任务建议每 5~10 秒轮询一次,不要秒级轮询
- 轮询到
success/failed/timeout即停止 - 建议加指数退避:5s → 10s → 20s → 30s(上限 30s)
错误码
错误响应统一为 OpenAI 格式,客户端 SDK 能直接解析:
{
"error": {
"message": "余额不足,当前可用 ¥0.50,本次需要 ¥1.20",
"type": "insufficient_balance",
"code": "insufficient_balance",
"request_id": "req_xxx"
}
}
| HTTP | code | 含义与处理 |
|---|---|---|
| 400 | invalid_request | 参数缺失或非法,检查请求体 |
| 401 | invalid_api_key | 密钥错误、已禁用或已过期 |
| 402 | insufficient_balance | 余额不足。充值后重试 —— 这类请求不会产生任何调用,你不会为失败的调用付费 |
| 403 | model_not_allowed | 该密钥无权调用此模型(检查密钥的模型白名单) |
| 404 | model_not_found | 模型不存在或未上架 |
| 429 | rate_limited | 触发限流,指数退避后重试 |
| 502 | upstream_error | 模型服务异常,费用已退回,请稍后重试 |
| 503 | upstream_unavailable | 模型服务暂不可用,费用已退回 |
429 / 502 / 503 都可以安全重试
这三类错误不会扣你的钱(预占已释放)。建议重试策略:最多 3 次,间隔 1s / 3s / 9s。
计费与余额
- 先充值后调用:余额不足时会拒绝请求,不会产生调用与费用
- 最低充值 ¥1,人民币现金计价,没有平台虚拟币
- 扣费时机:调用前按预估量预扣,返回后按实际用量结算并退差额
- 单次调用可低至万分之一分钱,控制台可查看每一笔明细
计费单位有三种:per_1m_token(每百万 token)、
per_call(每次)、per_second(每秒)。
具体每个模型的计费单位见 /v1/models 返回的 relay.billing_unit。
幂等与重试
网络超时后重试,最怕的是重复扣费。加一个 Idempotency-Key 头即可:
headers = {
"Authorization": "Bearer sk-relay-xxx",
"Idempotency-Key": "order-20240101-001", # 你的业务唯一标识
}
# 带相同 Idempotency-Key 重试时:
# - 不会重复调用(不会二次消耗算力额度)
# - 不会重复扣费
# - 直接返回首次的结果
务必使用业务维度的唯一标识
比如订单号 + 接口名。不要用随机 UUID —— 那样每次重试都会被当成新请求,
反而绕过了幂等保护。
常见问题
为什么余额明明够,却提示 402?
可动用金额 = 账户余额 − 在途占用(已提交但尚未返回的请求)。 如果有并发的长耗时请求(尤其视频生成),它们会占用一部分额度。 控制台「钱包」页会单独显示"在途占用",等任务完成就会释放。
调用失败了会扣钱吗?
不会。余额不足(402)不会产生调用;服务异常(502/503)会退回全部预扣。 唯一会扣钱的是成功返回了结果的调用。控制台的用量明细里, 失败记录的"消费"列都是 0。
支持哪些模型?
见首页模型广场或调用 GET /v1/models。
模型名沿用原始名称,没有做别名映射 —— 你在控制台看到的名字就是调用时该用的名字。
可以开发票吗?
充值后联系客服即可。请注意余额按实际消耗扣除,未消费部分可申请退回。
模型名怎么查?
curl https://你的域名/v1/models \ -H "Authorization: Bearer sk-relay-你的密钥"
还有疑问?
带上你的
request_id(响应头 X-Request-Id 里也有)联系客服,
我们能精确定位到这一笔调用的完整链路。