皓翰Agent
DEVELOPER DOCS

开发文档

OpenAI 兼容接口,一个密钥调用全部模型

— OpenAI SDK 兼容 支持流式输出
Node.js
import OpenAI from 'openai'

const client = new OpenAI({
  apiKey: "sk-ah-…",
  baseURL: "—"
})

const r = await client.chat.completions.create({
  model: "gem-3.7-flash",
  messages: [{ role: "user", content: "你好" }]
})
OpenAI SDK 兼容支持常见对话调用;具体参数按模型能力选择
支持流式输出SSE 输出;原始流可包含扩展计费消息
支持多模态模型对话、图片、视频、音频均可通过 API 调用
账单可追踪逐笔记录变动前后余额,随时可核对

快速开始

三步接完:创建密钥 → 换接口地址 → 调模型。

1 创建 API Key

登录后到工作台「开放 API」创建。密钥只在创建时显示一次,请立刻保存。

sk-ah-…
2 替换 base_url

设置本站接口地址和密钥,并选择实际可用的对话模型。

—
3 调用模型

模型名从模型与价格页复制,直接发起请求即可。

chat.completions.create
接口地址—
鉴权方式Authorization: Bearer sk-ah-…
可用模型—

鉴权

所有请求都要带上你的密钥,两种写法都支持:

HTTP Header
# 推荐
Authorization: Bearer sk-ah-xxxxxxxxxxxxxxxx

# 也支持
x-api-key: sk-ah-xxxxxxxxxxxxxxxx

密钥等同于账户余额,不要写进前端代码或提交到仓库。发现泄露请立刻在控制台删除重建。

对话接口

兼容 OpenAI Chat Completions 的常见请求与响应结构,具体能力由模型决定。常见文本结果读取:choices[0].message.content,usage 里的 token 数可用于对账。

curl — \
  -H "Authorization: Bearer $AI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gem-3.7-flash",
    "messages": [{"role": "user", "content": "你好"}]
  }'
from openai import OpenAI

client = OpenAI(api_key="sk-ah-…", base_url=—)
r = client.chat.completions.create(
    model="gem-3.7-flash",
    messages=[{"role": "user", "content": "你好"}])
print(r.choices[0].message.content)
import OpenAI from 'openai'

const client = new OpenAI({ apiKey: 'sk-ah-…', baseURL: — })

const r = await client.chat.completions.create({
  model: 'gem-3.7-flash',
  messages: [{ role: 'user', content: '你好' }]
})

流式输出

加 "stream": true 就是 SSE 流。本站会自动向模型请求用量信息,并在上游流结束后追加一条计费对账消息(标准 data: 行,多带一个 _billing 字段,需容忍没有 choices 的事件):

SSE
data: {"_billing":{"cost":0.0123,"balance":12.3456}}

如果模型本身没回 token 用量,会按字符数估算并在账单明细里标注「本地估算」,不会冒充精确值。

长思考模型建议一律用流式:非流式可能要等十几分钟,容易触发客户端超时。

模型列表

cURL
curl — \
  -H "Authorization: Bearer $AI_KEY"

返回 OpenAI 标准结构,可直接喂给 SDK。模型名、价格、计费方式以模型与价格页为准。

图片 / 视频 / 音频生成

本站 API Key 现在可以提交图片、视频、音频异步生成任务并查询结果。请阅读下方媒体生成 API 的接口、参数与轮询示例。媒体生成使用 HTTP 提交,不使用 chat.completions 调用。

官方 SDK 与客户端

官方 SDK 可用于常见对话调用,设置本站 base URL、API Key 与实际模型 ID。高级参数、返回格式和多模态能力需按所选模型调整。

Pythonclient = OpenAI(api_key=…, base_url=—)
Node.jsnew OpenAI({ apiKey: …, baseURL: — })
客户端Cherry Studio / LobeChat / NextChat / 沉浸式翻译

客户端需支持 Chat Completions、自定义接口地址及模型 ID。若客户端默认使用 Responses 或其他接口,请先切换协议;本站不保证所有客户端功能都兼容。

错误码

HTTP含义怎么办
401密钥无效或已删除检查密钥是否复制完整,或重新创建
402余额不足到工作台充值后再试
403账号停用或上游拒绝请求检查错误体中的 message 与 code
404模型名不存在用 /v1/models 拿准确名字
429请求太频繁降低并发,或稍后重试
503上游暂时不可用稍后重试;持续失败请联系客服

计费与余额

  • 按量计费:对话按 token,视频按秒,图片按次,音频按合成时长或 token。
  • 余额变动逐笔记录(含变动前后余额),工作台的「用量与余额」页随时可查、可逐笔核对。
  • 工作台媒体任务与对话 API 的结算方式不同;请以任务状态、用量记录和实际账单核对。
  • 价格及计费单位以本站价格页为准,不同模型可能按 token 或调用次数计费。

接口范围与接入准备

本文面向使用本站 API Key 的第三方开发者。接口根地址为 —;SDK 的 base URL 填到 /v1,直接使用 HTTP 时填写完整接口路径。所有示例中的模型名需要替换为本站实际可用的对话模型 ID。

方法路径用途鉴权
GET/v1/models查询模型 ID 与类型本站 API Key
POST/v1/chat/completions非流式 / 流式对话,按模型能力支持多模态输入本站 API Key

对话走 Chat Completions;图片、视频、音频走本站异步媒体 API,详见下方「媒体生成 API」。媒体接口不是 OpenAI 同步图片响应或音频二进制响应协议,需要使用 HTTP 客户端提交和轮询。当前未开放 Responses、文件管理、同步 audio/speech 或音频转写接口。

  1. 注册并登录工作台,确认账户有可用余额。
  2. 在「开放 API」创建本站密钥;使用创建时显示的完整明文密钥。
  3. 从模型列表选择 type: "chat" 的 ID,先发一个小请求确认接通。
  4. 在服务端环境变量保存密钥,再接入自己的业务。用户登录 Cookie、上游平台密钥和 OSS AccessKey 都不能替代本站 API Key。
准备环境变量 · Bash / macOS / Linux
export AI_KEY='替换为你的本站API密钥'
export AI_BASE_URL='—/v1'
export AI_MODEL='替换为实际对话模型ID'

curl -sS "$AI_BASE_URL/models" -H "Authorization: Bearer $AI_KEY"
PowerShell
$env:AI_KEY = '替换为你的本站API密钥'
$env:AI_BASE_URL = '—/v1'
$env:AI_MODEL = '替换为实际对话模型ID'
Invoke-RestMethod -Uri "$env:AI_BASE_URL/models" -Headers @{Authorization="Bearer $env:AI_KEY"}

Linux 示例中的反斜杠续行不能原样用于 PowerShell。Windows 可以使用 curl.exe,或上面的 Invoke-RestMethod。

请求参数与响应字段

字段必填说明
model是精确模型 ID,不是展示名称;从模型列表复制。
messages是消息数组;建议至少有一条 user 消息。
stream否布尔值,默认 false;必须传 JSON true 才开启流式。
temperature / top_p否采样参数,取值和支持情况取决于模型。
max_tokens / max_completion_tokens否输出长度限制;选择模型支持的参数,不保证两个名称均受支持。
tools / tool_choice / response_format否仅适用于支持对应能力的模型;本站不代替你的服务执行工具调用。
stream_options否流式时未提供则默认请求 include_usage: true;上游仍可能不返回用量。

常见消息角色包括 system、user、assistant 和工具消息 tool。具体角色、长上下文、思考参数、JSON 模式与工具调用由所选模型决定。本站会将请求交给对应上游,不能保证所有 OpenAI 参数在所有模型上通用。

多轮对话请求
{
  "model": "替换为实际对话模型ID",
  "messages": [
    {"role": "system", "content": "你是一位简洁的中文助手。"},
    {"role": "user", "content": "请解释浏览器缓存。"},
    {"role": "assistant", "content": "浏览器会保存部分资源,减少重复下载。"},
    {"role": "user", "content": "再举一个例子。"}
  ],
  "stream": false
}

API 不自动保存多轮上下文:每次请求需要由你的应用带上所需历史消息。上下文也会占用输入 token,建议在业务中限制历史长度。

非流式响应示意 · 数值仅作示例
{
  "id": "上游返回的响应ID",
  "object": "chat.completion",
  "choices": [{"index": 0, "message": {"role": "assistant", "content": "你好!"}, "finish_reason": "stop"}],
  "usage": {"prompt_tokens": 12, "completion_tokens": 3, "total_tokens": 15},
  "_billing": {"cost": 0.001, "balance": 10.999, "inputTokens": 12, "outputTokens": 3}
}

choices[0].message.content 是常见文本结果,但工具调用或特殊模型的 content 可能为空,需要检查 message 的其他字段。finish_reason、usage 和其他扩展字段以上游实际响应为准;_billing 是本站额外提供的计费信息,不是 OpenAI 标准字段。

可运行的服务端示例

先设置上一节的三个环境变量。Python 安装 python -m pip install openai;Node.js 安装 npm install openai 并保存为 example.mjs。使用与你安装的 SDK 兼容的 Python / Node.js 运行时。

Python · 非流式
import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["AI_KEY"],
                base_url=os.environ["AI_BASE_URL"],
                timeout=180.0, max_retries=0)
response = client.chat.completions.create(
    model=os.environ["AI_MODEL"],
    messages=[{"role": "user", "content": "你好,请简短介绍你自己。"}])
print(response.choices[0].message.content)
Node.js · 非流式
import OpenAI from 'openai';
const client = new OpenAI({
  apiKey: process.env.AI_KEY, baseURL: process.env.AI_BASE_URL,
  timeout: 180000, maxRetries: 0
});
const response = await client.chat.completions.create({
  model: process.env.AI_MODEL,
  messages: [{role: 'user', content: '你好,请简短介绍你自己。'}]
});
console.log(response.choices[0]?.message?.content);
cURL · 完整 HTTP 请求
curl -sS --max-time 180 "$AI_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $AI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"替换为实际对话模型ID","messages":[{"role":"user","content":"你好"}]}'

示例关闭 SDK 自动重试,以免在超时后不知情地重复提交生成请求。180 秒仅为接入示例,实际需要根据模型和业务调整;长输出建议使用流式。

流式接收与计费消息

Python · 逐段输出
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ["AI_KEY"], base_url=os.environ["AI_BASE_URL"],
                timeout=180.0, max_retries=0)
for chunk in client.chat.completions.create(
    model=os.environ["AI_MODEL"], stream=True,
    messages=[{"role": "user", "content": "写一段简短的欢迎词。"}]):
    if chunk.choices:
        text = chunk.choices[0].delta.content
        if text:
            print(text, end="", flush=True)
print()

原始 HTTP 客户端须按 SSE 空行分隔事件,读取 data: 内容;网络读取块不一定对应完整事件,不能对每个网络块直接 JSON.parse。choices 可能缺失或为空,须先判断再读取 delta。

原始 SSE 事件示意
data: {"choices":[{"index":0,"delta":{"content":"你好"}}]}

data: {"choices":[],"usage":{"prompt_tokens":12,"completion_tokens":3,"total_tokens":15}}

data: [DONE]

data: {"_billing":{"cost":0.001,"balance":10.999,"inputTokens":12,"outputTokens":3}}

当前实现会转发上游的 [DONE],并在上游流结束后追加本站 _billing。因此本站计费消息可能位于 [DONE] 之后;标准 SDK 可能在 [DONE] 停止读取而看不到它。如需读取该字段,用原始 SSE 客户端继续读到连接结束,或在工作台用量记录核对。不要假设每条事件都有 choices。

已开始 SSE 后发生错误,可能收到包含 error.message 的事件,或连接直接结束。此时 HTTP 可能仍是 200,业务需要区分正常完成和中途断开;缺少计费事件也不能直接认定未扣费。流式响应提供 X-Request-Id,排障时请记录。

视觉输入与素材地址

仅支持视觉理解的对话模型可在 messages 中接收图片。图片生成模型与视觉对话模型不是同一种调用方式。

图片理解请求
{
  "model": "替换为支持视觉的对话模型ID",
  "messages": [{"role": "user", "content": [
    {"type": "text", "text": "请描述这张图片。"},
    {"type": "image_url", "image_url": {"url": "https://你的素材域名/example.jpg"}}
  ]}]
}

素材 URL 必须能被上游服务器获取;需要登录 Cookie 的页面、本机地址、内网地址和浏览器 blob URL 不能作为远程素材地址。带签名的 OSS URL 应保留完整查询参数且在任务处理期间有效。支持 data URL 的模型可使用 data:image/png;base64,...,但体积、数量及格式限制以上游要求为准。

工作台中用户上传的图片、视频、音频可以由站点 OSS 保存七天;云端到期删除,签名地址也会过期。浏览器缓存不等于上游仍能读取。请提前下载长期需要的素材,不要把临时地址当成永久业务资产。上游生成结果由上游托管,保存期限以上游为准。

限流、超时与重复请求

  • 429 时降低并发并退避;错误体可能包含 error.retryAfter(秒)。本站未承诺固定并发额度,也未保证提供 Retry-After 响应头。
  • 建议退避间隔按 1、2、4、8 秒递增并增加随机抖动,设置最大尝试次数。鉴权、额度或参数错误先修正再提交。
  • 网络超时不等于生成未执行。当前没有对外提供幂等键去重或按客户端请求 ID 查询生成结果的接口;自动重试可能重复生成和计费。已取得媒体任务 ID 时只查询该任务,不要重复提交。
  • 对话 API 不返回媒体任务号;媒体 API 任务使用 /v1/tasks/{id} 查询。
  • 每个请求记录时间、模型 ID、HTTP 状态、错误码、可获得的响应 ID / X-Request-Id。日志不记录密钥和含隐私的完整消息。
错误响应示意
{
  "error": {
    "message": "请求过于频繁",
    "code": "rate_limited",
    "retryAfter": 3
  }
}
状态常见 code / 情况处理
400invalid_json / invalid_request / line_no_model / 上游参数错误检查 JSON、模型和 messages;按模型能力调整参数。
401missing_api_key / invalid_api_key / key_revoked / key_disabled检查本站密钥及请求头,不要使用上游密钥。
402insufficient_balance / key_quota_exceeded账户余额与单个密钥额度分别检查;充值未必解除密钥额度上限。
403account_banned / 上游拒绝检查账号状态或具体 error.message;不一定是模型授权问题。
404未开放的路径或上游资源不存在检查 /v1 是否重复、路径及上游模型 ID。
429rate_limited / 上游限流减少并发,按错误提示退避。
502upstream_error / upstream_unreachable记录错误信息,确认是否已经产生结果再决定重试。
503no_upstream_key / 服务不可用联系站点支持或稍后再试。

本站可能保留上游的其他 HTTP 状态及业务错误码,应同时查看状态、message 和 code,不要只凭状态码判断原因。

图片、视频、音频生成 API

使用与对话相同的本站 API Key。提交成功返回 HTTP 202 和本站任务 ID,表示已受理,不表示生成完成。选择模型列表中 type 为 image、video 或 audio 的模型。

方法接口说明
POST/v1/images/generations图片生成,模型类型必须是 image。
POST/v1/videos/generations视频生成,模型类型必须是 video。
POST/v1/audio/generations音频生成,模型类型必须是 audio。
POST/v1/media/generations通用媒体入口,自动识别上述三种类型。
GET/v1/media/parameters?model=模型ID查询模型可用参数说明;模型 ID 应 URL 编码。
GET/v1/tasks/{id}查询自己账户的单个任务;不可查询其他用户任务。
GET/v1/tasks?limit=20自己账户最近的任务,最多 100 条;可包含工作台提交的任务。

提交字段:model 必填字符串;prompt 为提示词或音频生成文本;params 为参数对象,默认空对象。prompt 与 params 至少有一个非空。尺寸、比例、时长、音色、参考图等参数名和值因模型而异,先查参数说明,不能把一套参数用于所有模型。本站对外入口自动选线路,不开放上游回调或任意线路覆盖参数。

cURL · 提交图片任务
curl -sS "$AI_BASE_URL/images/generations" \
  -H "Authorization: Bearer $AI_KEY" -H "Content-Type: application/json" \
  -d '{"model":"替换为图片模型ID","prompt":"清晨的海边,写实摄影","params":{}}'
cURL · 视频 / 音频请求体示例
# POST /v1/videos/generations
{"model":"替换为视频模型ID","prompt":"镜头缓慢推进,海浪拍打岸边","params":{}}

# POST /v1/audio/generations
{"model":"替换为音频模型ID","prompt":"你好,欢迎使用本站。","params":{}}
提交 / 查询响应示意
{
  "task": {
    "id": "本站任务ID", "object": "media.task",
    "model": "实际模型ID", "type": "image",
    "state": "submitted", "status": "已提交", "progress": "0",
    "is_final": false, "result_url": "", "result_type": "",
    "error": null, "created_at": "时间", "completed_at": null,
    "_billing": {"settled": false, "cost": 0}
  }
}

拿 task.id 请求 GET /v1/tasks/{id},建议每 5~10 秒一次。is_final: true 表示终态,仍需检查 error 和 result_url 区分成功与失败;state、status 和 progress 内容以上游结果为准,不依赖固定状态字符串。任务未完成时 result_url 为空、cost 为 0 都是正常情况。

Python · 图片生成与轮询(标准库,无需 SDK)
import os, json, time
from urllib.request import Request, urlopen
from urllib.error import HTTPError

base = os.environ["AI_BASE_URL"].rstrip("/")
key = os.environ["AI_KEY"]
def request(path, body=None):
    payload = None if body is None else json.dumps(body).encode()
    req = Request(base + path, data=payload, headers={
        "Authorization": "Bearer " + key, "Content-Type": "application/json"})
    try:
        with urlopen(req, timeout=120) as response:
            return json.load(response)
    except HTTPError as e:
        raise RuntimeError(str(e.code) + " " + e.read().decode()) from e

# AI_MEDIA_MODEL 设置为实际图片模型 ID;视频/音频替换对应提交路径和模型。
task = request("/images/generations", {
    "model": os.environ["AI_MEDIA_MODEL"], "prompt": "清晨的海边,写实摄影", "params": {}
})["task"]
task_id = task["id"]
deadline = time.monotonic() + 1800
while not task["is_final"]:
    if time.monotonic() > deadline:
        raise TimeoutError("客户端停止等待,请保留任务ID后续查询:" + task_id)
    time.sleep(5)
    task = request("/tasks/" + task_id)["task"]
if task.get("error") or not task.get("result_url"):
    raise RuntimeError(str(task))
print(task["result_url"])
print(task["_billing"])

媒体任务沿用工作台结算机制,终态后 _billing.cost 为本站结算金额,按对应账户和提交时使用的密钥记录用量。余额或密钥额度在提交时检查;异步并发任务不预冻结预算,不能把密钥额度当作并发消费的严格预留上限。

result_url 由上游托管,本站不把生成文件复制到服务器或 OSS。需要长期保存请自行下载。参考素材请使用上游可访问的完整 URL,并确保任务处理期间未过期。可通过 JSON 提供有效素材 URL,也可使用下方说明的 multipart 请求一并提交参考文件。

随生成请求提交参考素材

API 不开放独立上传凭证或通用文件存储接口。需要传图片、视频或音频参考文件时,向媒体生成接口发送 multipart/form-data,将模型、提示词、参数和文件一次提交。服务器临时接收后保存到 OSS,并把素材地址加入模型参数,再提交生成任务;不长期保存到本站磁盘。

model、prompt 为普通表单字段;params 为 JSON 对象字符串。文件字段名必须等于该模型的参考素材参数名(通过 /v1/media/parameters 查询),上传地址会覆盖 params 中同名字段。单文件用普通字段名;多文件用 images[] 等实际参数名加 [],每个文件重复该字段,转成 URL 数组。参考素材数量和格式应遵守所选模型的参数说明。已有有效远程 URL 时仍可使用 JSON 请求,不必重新上传。

Python · 素材与生成一并提交(pip install requests)
import os, json
from pathlib import Path
import requests

path = Path("reference.png")
with path.open("rb") as source:
    response = requests.post(os.environ["AI_BASE_URL"].rstrip("/") + "/images/generations",
        headers={"Authorization": "Bearer " + os.environ["AI_KEY"]},
        data={"model": os.environ["AI_MEDIA_MODEL"],
              "prompt": "根据参考素材生成画面", "params": json.dumps({})},
        # 将 reference_parameter 替换为所选模型实际接收的参考素材参数名。
        files={"reference_parameter": (path.name, source, "image/png")}, timeout=180)
response.raise_for_status()
task_id = response.json()["task"]["id"]
print(task_id)  # 使用 /tasks/{id} 查询结果

视频和音频使用对应生成路径与实际参考参数名。文件仅接受已列出的图片、视频和音频格式,单个文件上限 30 MB,保留请求频率限制;不另设每请求三个文件或每日 300 MB 的固定限制。账户余额、密钥状态、额度及模型类型先校验再写入 OSS。模型本身及 PHP 的 max_file_uploads、post_max_size 等配置仍可能限制一次上传数量和总大小。

此流程仍会消耗服务器接收和转传带宽,PHP 会产生请求临时文件,结束后自动清理。上传前的网络接收无法完全避免;实际文件大小还受宝塔 PHP 的 upload_max_filesize、post_max_size 和网站请求体上限限制。OSS 未启用或失败时明确报错,不回退到站点持久磁盘。

OSS 素材按用户目录隔离,保留七天,使用私有签名地址。生成结果仍由上游托管。若素材已保存而上游拒绝生成,素材会保留到生命周期删除,失败不会立即清空缓存或已保存的对象。

接入检查与常见问题

  • 模型列表能查到,为什么对话失败?检查 type 是否为 chat、账户余额、模型当前可用性和输入能力;目录存在不保证每次上游调用成功。
  • 404 或返回 HTML?确认访问本站 API 域名,SDK base URL 只包含一次 /v1;浏览器页面地址 /index.html 和 /admin 不是 API 根地址。
  • SDK 类型报错?本站有扩展字段,且模型能力存在差异;不要强制要求所有事件都与单一模型返回完全一致。
  • 如何查余额和账单?登录工作台查看用量与余额;对外 API 当前没有独立余额查询接口。非流式 _billing 是本次结果的计费补充信息。
  • 工具调用为什么没有执行?模型只返回调用建议,你的应用应验证参数、执行自己的工具并追加 tool 消息。
  • 可以直接放在网页里吗?不要向终端用户暴露你的账户密钥。通过自己的后端转发,并对用户鉴权、限制频率和消费。
  • 如何寻求支持?提供调用时间、模型、HTTP 状态、脱敏错误体和响应 ID。不要发送密钥、Cookie、OSS Secret 或完整签名素材链接。

文档更新:2026-10-10。示例返回值仅说明结构,不代表固定价格、余额、用量或可用模型。实际价格以价格页及工作台账单为准。

文档随站点更新。有问题先看价格页确认模型是否可用,再到工作台「用量与余额」核对账单。