API 参考
通用约定
Base URL
所有端点均提供在 /v1 前缀下。
认证
对 /v1/* 端点的调用使用 API Key 认证(非 JWT)。API Key 通过以下请求头传递:
| 请求头 | 格式 | 说明 |
|---|---|---|
Authorization | Bearer <api_key> | OpenAI 风格。Anthropic 兼容端点也接受 x-api-key 加 anthropic-version: 2023-06-01。 |
缺少或无效的密钥返回 401。
余额预检
所有调用模型的端点在执行前都会进行余额预检:
- 余额不足返回
Insufficient credit,映射为:- OpenAI 协议:HTTP
400,code = insufficient_quota - Anthropic 协议:HTTP
402,type = billing_error
- OpenAI 协议:HTTP
- 部分端点还会按模型估算最低成本以进行第二次预检。
Create Chat CompletionPOSThttps://api.basicrouter.ai/api/v1/chat/completions
OpenAI Chat Completions 兼容端点。支持流式和非流式、工具调用、JSON 模式和多模态输入。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | String | 是 | 模型名称。 |
messages | Message[] | 是 | 对话消息。 |
stream | Boolean | 否 | 流式模式,默认 false。 |
temperature | Double | 否 | 采样温度。 |
max_tokens | Integer | 否 | 最大输出 token 数。 |
top_p | Double | 否 | 核采样。 |
presence_penalty | Double | 否 | — |
frequency_penalty | Double | 否 | — |
tools | Tool[] | 否 | 工具定义。 |
tool_choice | String|Object | 否 | auto / none / required / 指定 函数。 |
response_format | Object | 否 | {type, json_schema:{name,schema,strict}}; text/json_object/json_schema。 |
parallel_tool_calls | Boolean | 否 | — |
metadata | Map | 否 | 透传元数据。 |
Message 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
role | String | system / user / assistant / tool。 |
content | String|Array | 纯文本或多模态内容块数组 ([{type:"text",text},{type:"image_url",image_url:{url}}])。 |
tool_call_id | String | 当 role=tool 时关联到 tool_calls。 |
tool_calls | ToolCall[] | 当 role=assistant 发起工具调用时出现。 |
| 字段 | 类型 | 说明 |
|---|---|---|
type | String | 固定值 function。 |
function | Object | 函数定义。 |
function.name | String | 函数名称。 |
function.description | String | 函数描述。 |
function.parameters | Object | 输入的 JSON Schema。 |
curl --request POST \
--url https://api.basicrouter.ai/api/v1/chat/completions \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"model": "glm-5.2",
"messages": [{"role": "user", "content": "Describe Hangzhou in one sentence."}],
"stream": false,
"temperature": 0.7
}'{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1721380000,
"model": "glm-5.2",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hangzhou is ..."},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 12, "completion_tokens": 18, "total_tokens": 30}
}响应字段(非流式):
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 补全 ID。 |
object | String | 固定值 chat.completion。 |
created | Long | 创建时间戳(秒)。 |
model | String | 模型名称。 |
choices | Choice[] | {index, message:{role, content, tool_calls?}, finish_reason}。 |
usage | Object | {prompt_tokens, completion_tokens, total_tokens}。 |
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 工具调用 ID。 |
type | String | 固定值 function。 |
function | Object | 函数调用详情。 |
function.name | String | 函数名称。 |
function.arguments | Object | 函数参数。 |
流式响应示例:
data: {"object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant","content":"..."}}]}
data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"..."}}]}
data: [DONE]Create ResponsePOSThttps://api.basicrouter.ai/api/v1/responses
OpenAI Responses 兼容端点。使用 input 代替 messages,使用 instructions 代替系统消息,使用 text 块代替 response_format。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | String | 是 | 模型名称。 |
input | String|Array | 是 | 纯字符串(用户消息)或消息对象数组。 |
instructions | String | 否 | 系统提示词。 |
stream | Boolean | 否 | 默认 false。 |
max_output_tokens | Integer | 否 | 最大输出 token 数。 |
temperature | Double | 否 | 默认 1。 |
top_p | Double | 否 | — |
tools | Tool[] | 否 | 顶层 {type, name, description, parameters}。 |
tool_choice | String|Object | 否 | auto >/none/required/{type,name}。 |
text | Object | 否 | {format:{type, name, schema, strict}}; text/json_object/json_schema。 |
metadata | Map | 否 | — |
previous_response_id | String | 否 | 多轮对话的前一个响应 ID。 |
parallel_tool_calls | Boolean | 否 | — |
curl --request POST \
--url https://api.basicrouter.ai/api/v1/responses \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"model": "glm-5.2",
"input": "Describe Hangzhou in one sentence.",
"instructions": "Be concise.",
"stream": false
}'{
"id": "resp_xxx",
"object": "response",
"model": "glm-5.2",
"status": "completed",
"created_at": 1721380000,
"output": [
{
"id": "msg_xxx",
"type": "message",
"role": "assistant",
"content": [{"type": "output_text", "text": "Hangzhou is ..."}],
"status": "completed"
}
],
"usage": {"input_tokens": 12, "output_tokens": 18, "total_tokens": 30}
}响应字段(非流式):
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 响应 ID。 |
object | String | 固定值 response。 |
model | String | 模型名称。 |
status | String | 如 completed。 |
created_at | Long | 创建时间戳(秒)。 |
output | Array | 输出项。消息项: {id, type:"message", role, content:[{type:"output_text", text}], status}。工具调用项: {type:"function_call", id, name, call_id, arguments, status}。 |
usage | Object | {input_tokens, output_tokens, total_tokens}。对于 Claude 模型, input_tokens 包含 cache_read, output_tokens 包含 cache_write。 |
流式遵循 Responses API 事件:
| 事件 | 说明 |
|---|---|
response.created | 响应流开始。 |
response.output_text.delta | 文本增量输出更新。 |
response.completed | 响应流结束。 |
Create Anthropic MessagePOSThttps://api.basicrouter.ai/api/v1/messages
Anthropic Messages 兼容端点。接受 x-api-key 和 anthropic-version: 2023-06-01 请求头。内容块支持 text、image、tool_use、tool_result、 thinking 和 redacted_thinking。
| 字段 | 类型 | 必填 | JSON 字段 | 说明 |
|---|---|---|---|---|
model | String | 是 | model | 模型名称。 |
messages | Message[] | 是 | messages | 对话消息。 |
system | String|Array | 否 | system | 系统提示词,字符串或 [{type,text}]。 |
maxTokens | Integer | 是 | max_tokens | 最大输出 token 数。 |
stream | Boolean | 否 | stream | 流式。 |
temperature | Double | 否 | temperature | — |
topP | Double | 否 | top_p | — |
topK | Integer | 否 | top_k | — |
tools | Tool[] | 否 | tools | 工具定义(input_schema)。 |
toolChoice | Object | 否 | tool_choice | — |
metadata | Map | 否 | metadata | — |
thinking | Object | 否 | thinking | 扩展思考配置。 |
stopSequences | Object | 否 | stop_sequences | — |
anthropicBeta | Object | 否 | anthropic_beta | Beta 功能请求头。 |
| 字段 | 类型 | 说明 |
|---|---|---|
role | String | 消息角色,如 user / assistant。 |
content | String|ContentBlock[] | 纯文本或内容块数组。 |
| 字段 | 类型 | 说明 |
|---|---|---|
type | String | text、image、tool_use、 tool_result、thinking、 redacted_thinking 之一。 |
text | String | 当 type 为 text 时出现。 |
source | Object | 当 type 为 image 时出现。 |
示例:
{ "type": "image", "source": { "type": "base64", "media_type": "...", "data": "..." } }
{ "type": "image", "source": { "type": "url", "url": "..." } }| 字段 | 类型 | 说明 |
|---|---|---|
name | String | 函数名称。 |
description | String | 函数描述。 |
input_schema | Object | 输入的 JSON Schema。 |
cache_control | Object | 可选缓存控制。 |
curl --request POST \
--url https://api.basicrouter.ai/api/v1/messages \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "Content-Type: application/json" \
--data '{
"model": "claude-sonnet-4.6",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Describe Hangzhou in one sentence."}]
}'{
"id": "msg_xxx",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4.6",
"content": [{"type": "text", "text": "Hangzhou is ..."}],
"stop_reason": "end_turn",
"usage": {"input_tokens": 12, "output_tokens": 18}
}响应字段(非流式):
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 消息 ID。 |
type | String | 固定值 message。 |
role | String | 固定值 assistant。 |
model | String | 模型名称。 |
content | ContentBlock[] | 响应内容块(如 {type:"text", text}、 {type:"tool_use", ...})。 |
stop_reason | String | 如 end_turn、tool_use、max_tokens。 |
usage | Object | {input_tokens, output_tokens}。 |
| 事件 | 说明 |
|---|---|
message_start | 消息流开始。 |
content_block_start | 新内容块开始。 |
content_block_delta | 内容块增量更新。 |
content_block_stop | 内容块结束。 |
message_delta | 消息增量更新。 |
message_stop | 消息流结束。 |
List ModelsGEThttps://api.basicrouter.ai/api/v1/models
返回所有已上线、已启用的 API 模型。
curl --request GET \
--url https://api.basicrouter.ai/api/v1/models \
--header "Authorization: Bearer $BASICROUTER_API_KEY"{
"object": "list",
"data": [
{
"id": "glm-5.2",
"object": "model",
"display_name": "glm-5.2",
"created": 1721380000,
"owned_by": "Zai",
"input_modalities": ["text", "image"],
"output_modalities": ["text"],
"context_length": 128000,
"description": "..."
}
]
}每个模型条目(data[])字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 模型 ID。 |
object | String | 固定值 model。 |
display_name | String | 显示名称。 |
created | Long | 创建时间戳(秒)。 |
owned_by | String | 所有者 / 厂商。 |
input_modalities | String[] | 如 ["text","image"]。 |
output_modalities | String[] | 如 ["text"]。 |
context_length | Integer | 最大上下文长度。 |
description | String | 模型描述。 |
Retrieve ModelGEThttps://api.basicrouter.ai/api/v1/models/{model}
返回单个模型,结构与列表条目相同。模型不存在时返回 HTTP 404。
curl --request GET \
--url https://api.basicrouter.ai/api/v1/models/gpt-5.5 \
--header "Authorization: Bearer $BASICROUTER_API_KEY"成功响应:单个模型对象,字段与 /v1/models 列表条目相同。
当模型不存在时,返回 HTTP 404:
{"error": {"message": "The model 'xxx' does not exist", "type": "invalid_request_error", "code": "invalid_model_error"}}List Image ModelsGEThttps://api.basicrouter.ai/api/v1/image-models
在调用 /v1/image-generations 之前,查询图像模型支持的分辨率、比例和最大数量。无需认证。
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 模型 ID。 |
object | String | 固定值 image_model。 |
displayName | String | 显示名称。 |
description | String | 模型描述。 |
icon | String | 图标 URL。 |
created | Long | 创建时间戳(秒)。 |
maxCount | Integer | 每次请求最大图片数。 |
fileMax | Integer | 最大参考图片数。当为0时,不支持图生图。 |
resolutions | String[] | 支持的分辨率,如 ["720p","1080p"]。 |
ratios | String[] | 支持的宽高比,如 ["1:1","3:2"]。 |
curl --request GET \
--url https://api.basicrouter.ai/api/v1/image-models \
--header "Authorization: Bearer $BASICROUTER_API_KEY"{
"object": "list",
"data": [
{
"id": "gpt-image-2",
"object": "image_model",
"displayName": "GPT Image 1",
"description": "...",
"icon": "...",
"created": 1721380000,
"maxCount": 4,
"fileMax": 10,
"resolutions": ["720p", "1080p"],
"ratios": ["1:1", "3:2"]
}
]
}List Video ModelsGEThttps://api.basicrouter.ai/api/v1/video-models
在调用 /v1/video-generations 之前,查询视频模型支持的 videoType 值、时长范围、分辨率和比例。无需认证。
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 模型 ID。 |
object | String | 固定值 video_model。 |
displayName | String | 显示名称。 |
description | String | 模型描述。 |
icon | String | 图标 URL。 |
created | Long | 创建时间戳(秒)。 |
allowedVideoTypes | VideoTypeOption[] | 支持的 videoType 列表。 |
videoDurationMin | Integer | 每个片段最小秒数。 |
videoDurationMax | Integer | 每个片段最大秒数。 |
videoDurationSuggest | Integer[] | 推荐时长步长,如 [5,8,10]。 |
resolutions | String[] | 支持的分辨率。 |
ratios | String[] | 支持的宽高比。 |
resolutionOptions | ResolutionOption[] | 结构化的分辨率+比例+尺寸组合。 |
fileMax | Integer | 最大参考素材数。 |
VideoTypeOption 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
code | Integer | 传递给 /v1/video-generations 的 videoType 值。 |
name | String | 本地化类型名称(文生视频 / 图生视频 / ...)。 |
curl --request GET \
--url https://api.basicrouter.ai/api/v1/video-models \
--header "Authorization: Bearer $BASICROUTER_API_KEY"{
"object": "list",
"data": [
{
"id": "sora-2",
"object": "video_model",
"displayName": "Sora 2",
"description": "...",
"icon": "...",
"created": 1721380000,
"allowedVideoTypes": [
{"code": 1, "name": "text-to-video"},
{"code": 2, "name": "image-to-video"},
{"code": 3, "name": "image-to-video (first/last frame)"}
],
"videoDurationMin": 5,
"videoDurationMax": 10,
"videoDurationSuggest": [5, 8, 10],
"resolutions": ["1080p", "720p"],
"ratios": ["16:9", "9:16"],
"fileMax": 5
}
]
}价格档位字段说明
/v1/models、/v1/models/{model}、/v1/image-models、/v1/video-models 这 4 个模型查询接口的出参会返回当前调用方(API Key 用户)的有效计费价,与实际扣费完全一致,且返回该模型的全部价格档位。
字段命名差异:/v1/models / /v1/models/{model} 沿用 OpenAI 风格下划线命名(price_tiers);/v1/image-models / /v1/video-models 沿用 camelCase 命名(priceTiers)。两者结构完全相同。
price_tiers / priceTiers 为数组,每个元素是一个价格档位:
| 字段 | 类型 | 说明 |
|---|---|---|
outputPrice | decimal | 当前调用方有效输出单价 |
cachePrice | decimal | 缓存价格(通用模型,旧计费公式用) |
cacheReadPrice | decimal | 缓存读取单价(Bedrock Claude 专用) |
cacheWritePrice | decimal | 缓存写入单价(Bedrock Claude 专用) |
ratio | decimal | 计费倍率。FIXED 模式为 1;RATIO 模式为用户/分组倍率 |
mode | string | 定价模式:RATIO / FIXED |
planId | string | 命中的定价计划 id,可为 null |
三种模型类型共用同一结构,仅描述字段填充不同,不适用字段为 null:
| 模型类型 | UNIT | 生效的描述字段 |
|---|---|---|
| 文本 | token | region / bandMin / bandMax |
| 图片 | image | resolution / clarity |
| 视频 | video | resolution / clarity |
价格含义:返回的 inputPrice / outputPrice / cacheReadPrice / cacheWritePrice 等,是经价格链(chain)→ guidance → 基础价逐级解析后,针对当前 API Key 用户的最终计费单价,与实际扣费一致。调用方看到的单价会因自身所属的定价链(reseller / distributor / 企业 / 用户级配置)不同而不同。
实际扣费额 = 用量 ÷ quantity × 对应单价 × ratio。视频按秒计费时,实际扣费 = duration × outputPrice × ratio。
边界与异常处理(只读接口不会因价格配置问题返回 500)
| 场景 | 行为 |
|---|---|
| 单个档位解析失败(定价策略拒绝访问、配置缺失) | 跳过该档,记录 warn 日志,继续解析其余档位 |
| 某模型所有档位均解析失败 | 为空数组 [],模型仍正常返回 |
| 模型无任何价格配置 | 为空数组 [] |
| 价格解析抛出未捕获异常 | 整体 try-catch,返回空数组,接口仍 200 |
响应示例(/v1/models,文本模型):
{
"object": "list",
"data": [
{
"id": "gpt-4o",
"object": "model",
"display_name": "gpt-4o",
"context_length": 128000,
"price_tiers": [
{
"region": "GLOBAL",
"bandMin": null,
"bandMax": null,
"resolution": null,
"clarity": null,
"unit": "token",
"quantity": 1000,
"description": "每千 token",
"inputPrice": 0.0025,
"outputPrice": 0.01,
"cachePrice": 0.00125,
"cacheReadPrice": 0,
"cacheWritePrice": 0,
"ratio": 1,
"mode": "RATIO",
"planId": null
}
]
}
]
}响应示例(/v1/image-models,图片模型,priceTiers 结构同上,unit 为 image):
{
"object": "list",
"data": [
{
"id": "dall-e-3",
"object": "image_model",
"displayName": "dall-e-3",
"resolutions": ["1024x1024", "1792x1024", "1024x1792"],
"priceTiers": [
{
"region": null,
"bandMin": null,
"bandMax": null,
"resolution": "1024x1024",
"clarity": "standard",
"unit": "image",
"quantity": 1,
"description": "每张",
"inputPrice": 0,
"outputPrice": 0.04,
"cachePrice": 0,
"cacheReadPrice": 0,
"cacheWritePrice": 0,
"ratio": 1,
"mode": "RATIO",
"planId": null
}
]
}
]
}Create Image GenerationPOSThttps://api.basicrouter.ai/api/v1/image-generations
异步提交图像生成任务。立即返回 taskId;通过轮询 GET /v1/image-generations/{taskId} 或通过 callbackUrl webhook 获取结果。
model、支持的 resolution / ratio 值、 count 上限和参考图片上传限制(fileMax)必须先从 GET /v1/image-models 获取。仅接受该模型规格中公布的值。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
text | String | 是 | 提示词。 |
model | String | 是 | 模型名称。 |
imageUrls | String[] | 否 | 参考图片 URL(图生图)。 |
count | Integer | 否 | 图片数量(≥0)。 |
resolution | String | 否 | 分辨率(见 /v1/image-models)。 |
ratio | String | 否 | 宽高比。 |
callbackUrl | String | 否 | 任务级 webhook URL。 |
curl --request POST \
--url https://api.basicrouter.ai/api/v1/image-generations \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"model": "seedream-4.5",
"text": "A cat drinking water by the river",
"count": 1,
"resolution": "2k",
"ratio": "1:1",
"imageUrls": []
}'{
"code": 200,
"message": "image task is commit",
"data": {"taskId": "img_xxx"}
}错误响应:
// Insufficient credit
{ "code": 500, "message": "Insufficient credit" }
// Model not found
{ "code": 404, "message": "Model not found: xxx" }Retrieve Image GenerationGEThttps://api.basicrouter.ai/api/v1/image-generations/{taskId}
轮询图像生成任务。status 为 pending / success / failed。images 是图片 URL 数组的 JSON 字符串;text 携带模型附加的文本描述(如 Gemini 多模态输出),否则为 null。
curl --request GET \
--url https://api.basicrouter.ai/api/v1/image-generations/img_xxx \
--header "Authorization: Bearer $BASICROUTER_API_KEY"{
"code": 200,
"message": "success",
"data": {
"taskId": "img_xxx",
"status": "success",
"errorMessage": null,
"images": "[\"https://.../1.png\"]",
"text": null
}
}响应 data 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
taskId | String | 任务 ID。 |
status | String | pending / success / failed。 |
errorMessage | String | 失败原因,成功时为 null。 |
images | String | 图片 URL 数组的 JSON 字符串,如 "[\"https://.../1.png\"]"。 |
text | String | 模型附加的文本描述(如 Gemini 多模态输出);否则为 null。 |
任务未找到:
{ "code": 500, "message": "task not found" }如果提交时提供了 callbackUrl,服务器会通过 webhook 推送最终的 success / failed 结果,data 结构相同。
完整示例(提交 + 轮询)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class ImageGenerationExample {
private static final String BASE = "https://api.basicrouter.ai/api/v1";
private static final String API_KEY = System.getenv("BASICROUTER_API_KEY");
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10)).build();
// 1. Submit the task.
String body = "{"
+ "\"model\":\"seedream-4.5\","
+ "\"text\":\"A cat drinking water by the river\","
+ "\"count\":1,"
+ "\"resolution\":\"2k\","
+ "\"ratio\":\"1:1\","
+ "\"imageUrls\":[]"
+ "}";
HttpResponse<String> submit = http.send(
HttpRequest.newBuilder(URI.create(BASE + "/image-generations"))
.header("Authorization", "Bearer " + API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body)).build(),
HttpResponse.BodyHandlers.ofString());
String taskId = extract(submit.body(), "taskId");
System.out.println("taskId = " + taskId);
// 2. Poll until terminal status.
String status = "pending";
while ("pending".equals(status)) {
Thread.sleep(15_000L);
HttpResponse<String> poll = http.send(
HttpRequest.newBuilder(URI.create(BASE + "/image-generations/" + taskId))
.header("Authorization", "Bearer " + API_KEY).GET().build(),
HttpResponse.BodyHandlers.ofString());
status = extract(poll.body(), "status");
System.out.println("status = " + status);
}
if (!"success".equals(status)) {
throw new RuntimeException("image generation failed: " + status);
}
// images is a JSON-stringified array of URLs.
String images = extract(pollResult(http, taskId), "images");
System.out.println("images = " + images);
}
// Minimal JSON field extractor — use Jackson/Gson in production.
private static String extract(String json, String field) {
int i = json.indexOf("\"" + field + "\":");
if (i < 0) return null;
i += field.length() + 3;
if (json.charAt(i) == '\"') {
int end = json.indexOf('\"', i + 1);
return json.substring(i + 1, end);
}
int end = i;
while (end < json.length() && "0123456789.".indexOf(json.charAt(end)) >= 0) end++;
return json.substring(i, end);
}
private static String pollResult(HttpClient http, String taskId) throws Exception {
return http.send(HttpRequest.newBuilder(URI.create(BASE + "/image-generations/" + taskId))
.header("Authorization", "Bearer " + API_KEY).GET().build(),
HttpResponse.BodyHandlers.ofString()).body();
}
}import os
import time
import requests
BASE = "https://api.basicrouter.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['BASICROUTER_API_KEY']}"}
# 1. Submit the task.
resp = requests.post(
f"{BASE}/image-generations",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"model": "seedream-4.5",
"text": "A cat drinking water by the river",
"count": 1,
"resolution": "2k",
"ratio": "1:1",
"imageUrls": [],
},
)
resp.raise_for_status()
task_id = resp.json()["data"]["taskId"]
print(f"taskId = {task_id}")
# 2. Poll until terminal status.
while True:
time.sleep(15)
poll = requests.get(f"{BASE}/image-generations/{task_id}", headers=HEADERS)
poll.raise_for_status()
data = poll.json()["data"]
status = data["status"]
print(f"status = {status}")
if status != "pending":
break
if status != "success":
raise RuntimeError(f"image generation failed: {data.get('errorMessage')}")
# images is a JSON-stringified array of URLs.
import json
images = json.loads(data["images"])
print(f"images = {images}")Create Video GenerationPOSThttps://api.basicrouter.ai/api/v1/video-generations
异步提交视频生成任务。立即返回 taskId;通过轮询 GET /v1/video-generations/{taskId} 或通过 callbackUrl webhook 获取结果。
model、允许的 videoType 值、时长范围 (videoDurationMin/Max)、支持的 resolution / ratio 和参考素材上传限制(fileMax)必须先从 GET /v1/video-models 获取。仅接受该模型 allowedVideoTypes 中列出的 videoType 代码。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
text | String | 是 | 提示词。 |
model | String | 是 | 模型名称。 |
videoType | Integer | 是 | 1 文生视频 / 2 图生视频(首帧)/ 3 图生视频(首尾帧)/ 4 图生视频(参考)/ 5 全部参考。 |
imageUrls | String[] | 否 | 图片素材 URL。 |
videoUrls | VideoUrl[]|String[] | 否 | 视频素材 URL。 |
audioUrls | String[] | 否 | 音频素材 URL。 |
resolution | String | 否 | 分辨率。 |
ratio | String | 否 | 宽高比。 |
duration | Long | 否 | 秒数(>0)。 |
callbackUrl | String | 否 | 任务级 webhook URL。 |
各 videoType 示例:
1. 文生视频(videoType=1)
仅从文本提示词生成视频;无需参考素材。
curl --request POST \
--url https://api.basicrouter.ai/api/v1/video-generations \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"videoType": 1,
"text": "A cat jumping on a bed",
"resolution": "480p",
"ratio": "16:9",
"duration": 4,
"model": "seedance-2.0"
}'2. 图生视频 - 首帧(videoType=2)
在 imageUrls 中提供单张起始帧;模型从该帧开始生成视频。
curl --request POST \
--url https://api.basicrouter.ai/api/v1/video-generations \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"videoType": 2,
"text": "Happily shaking head",
"resolution": "480p",
"ratio": "16:9",
"duration": 4,
"model": "seedance-2.0",
"imageUrls": ["https://basicrouter-flie.oss-accelerate.aliyuncs.com/test/first-frame.png"]
}'3. 图生视频 - 首尾帧(videoType=3)
在 imageUrls 中提供首帧和尾帧(顺序: [首帧, 尾帧]);模型在两帧之间生成过渡视频。
curl --request POST \
--url https://api.basicrouter.ai/api/v1/video-generations \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"videoType": 3,
"text": "Put on the hat",
"resolution": "480p",
"ratio": "16:9",
"duration": 4,
"model": "seedance-2.0",
"imageUrls": [
"https://basicrouter-flie.oss-accelerate.aliyuncs.com/test/first-frame.png",
"https://basicrouter-flie.oss-accelerate.aliyuncs.com/test/last-frame.png"
]
}'4. 图生视频 - 参考(videoType=4)
在 imageUrls 中提供一张或多张参考图;模型使用其风格/内容作为参考(而非强制首/尾帧)来生成视频。
curl --request POST \
--url https://api.basicrouter.ai/api/v1/video-generations \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"videoType": 4,
"text": "Two cats playing together",
"resolution": "480p",
"ratio": "16:9",
"duration": 4,
"model": "kling-v3-omni-video",
"imageUrls": [
"https://basicrouter-flie.oss-accelerate.aliyuncs.com/test/ref-1.png",
"https://basicrouter-flie.oss-accelerate.aliyuncs.com/test/ref-2.png"
]
}'5. 全部参考(videoType=5)
混合图片 / 视频 / 音频参考。在提示词中按位置引用素材:imageUrls 中第 1 项为 @图片 1,videoUrls 中第 1 项为 @视频 1,audioUrls 中第 1 项为 @音频 1。videoUrls 也接受纯 URL 字符串。
curl --request POST \
--url https://api.basicrouter.ai/api/v1/video-generations \
--header "Authorization: Bearer $BASICROUTER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"videoType": 5,
"text": "Use the first-person framing of @视频 1 and @音频 1 as background music. First-person tea ad; start frame is @图片 1 ... end frame is @图片 2.",
"model": "seedance-2.0",
"imageUrls": [
"https://ark-project.tos-cn-beijing.volces.com/doc_image/r2v_tea_pic1.jpg",
"https://ark-project.tos-cn-beijing.volces.com/doc_image/r2v_tea_pic2.jpg"
],
"videoUrls": ["https://ark-project.tos-cn-beijing.volces.com/doc_video/r2v_tea_video1.mp4"],
"audioUrls": ["https://ark-project.tos-cn-beijing.volces.com/doc_audio/r2v_tea_audio1.mp3"],
"resolution": "1080p",
"ratio": "16:9",
"duration": 11
}'提交响应(五种类型通用):
{
"code": 200,
"message": "success",
"data": {"taskId": "vid_xxx"}
}Retrieve Video GenerationGEThttps://api.basicrouter.ai/api/v1/video-generations/{taskId}
轮询视频生成任务。status 为 pending / success / failed;videoUrl 是生成的视频 URL,lastFrameUrl 是尾帧 URL(图生视频场景)。
curl --request GET \
--url https://api.basicrouter.ai/api/v1/video-generations/vid_xxx \
--header "Authorization: Bearer $BASICROUTER_API_KEY"{
"code": 200,
"message": "success",
"data": {
"status": "success",
"videoUrl": "https://.../out.mp4",
"lastFrameUrl": null,
"message": null
}
}响应 data 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
status | String | pending / success / failed。 |
videoUrl | String | 生成的视频 URL。 |
lastFrameUrl | String | 尾帧 URL(图生视频场景);否则为 null。 |
message | String | 失败原因,成功时为 null。 |
如果提交时提供了 callbackUrl,服务器会通过 webhook 推送最终结果,data 结构相同。
完整示例(提交 + 轮询)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class VideoGenerationExample {
private static final String BASE = "https://api.basicrouter.ai/api/v1";
private static final String API_KEY = System.getenv("BASICROUTER_API_KEY");
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10)).build();
// 1. Submit the task (videoType=1: text-to-video).
String body = "{"
+ "\"videoType\":1,"
+ "\"text\":\"A cat jumping on a bed\","
+ "\"resolution\":\"480p\","
+ "\"ratio\":\"16:9\","
+ "\"duration\":4,"
+ "\"model\":\"seedance-2.0\""
+ "}";
HttpResponse<String> submit = http.send(
HttpRequest.newBuilder(URI.create(BASE + "/video-generations"))
.header("Authorization", "Bearer " + API_KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body)).build(),
HttpResponse.BodyHandlers.ofString());
String taskId = extract(submit.body(), "taskId");
System.out.println("taskId = " + taskId);
// 2. Poll until terminal status. Video tasks take longer — poll every 20s.
String status = "pending";
String lastBody = null;
while ("pending".equals(status)) {
Thread.sleep(20_000L);
HttpResponse<String> poll = http.send(
HttpRequest.newBuilder(URI.create(BASE + "/video-generations/" + taskId))
.header("Authorization", "Bearer " + API_KEY).GET().build(),
HttpResponse.BodyHandlers.ofString());
lastBody = poll.body();
status = extract(lastBody, "status");
System.out.println("status = " + status);
}
if (!"success".equals(status)) {
throw new RuntimeException("video generation failed: " + status);
}
String videoUrl = extract(lastBody, "videoUrl");
System.out.println("videoUrl = " + videoUrl);
}
// Minimal JSON field extractor — use Jackson/Gson in production.
private static String extract(String json, String field) {
int i = json.indexOf("\"" + field + "\":");
if (i < 0) return null;
i += field.length() + 3;
if (json.charAt(i) == '\"') {
int end = json.indexOf('\"', i + 1);
return json.substring(i + 1, end);
}
int end = i;
while (end < json.length() && "0123456789.".indexOf(json.charAt(end)) >= 0) end++;
return json.substring(i, end);
}
}import os
import time
import requests
BASE = "https://api.basicrouter.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['BASICROUTER_API_KEY']}"}
# 1. Submit the task (videoType=1: text-to-video).
resp = requests.post(
f"{BASE}/video-generations",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"videoType": 1,
"text": "A cat jumping on a bed",
"resolution": "480p",
"ratio": "16:9",
"duration": 4,
"model": "seedance-2.0",
},
)
resp.raise_for_status()
task_id = resp.json()["data"]["taskId"]
print(f"taskId = {task_id}")
# 2. Poll until terminal status. Video tasks take longer — poll every 20s.
while True:
time.sleep(20)
poll = requests.get(f"{BASE}/video-generations/{task_id}", headers=HEADERS)
poll.raise_for_status()
data = poll.json()["data"]
status = data["status"]
print(f"status = {status}")
if status != "pending":
break
if status != "success":
raise RuntimeError(f"video generation failed: {data.get('message')}")
print(f"videoUrl = {data['videoUrl']}")
if data.get("lastFrameUrl"):
print(f"lastFrameUrl = {data['lastFrameUrl']}")Retrieve Billing BalanceGEThttps://api.basicrouter.ai/api/v1/billing/balance
返回账户余额,分为三个钱包:月度套餐、资源包和按量付费积分。
curl --request GET \
--url https://api.basicrouter.ai/api/v1/billing/balance \
--header "Authorization: Bearer $BASICROUTER_API_KEY"{
"totalCredit": 128.50,
"totalResourceCredit": 30.00,
"wallets": {
"monthlyPlan": {"id": "pkg_xxx", "credit": 50.00, "name": "Monthly plan"},
"resourcePacks": [
{"id": "rp_xxx", "credit": 30.00, "name": "Video resource pack"}
],
"payAsYouGo": 48.50
}
}响应字段:
| 字段 | 类型 | 说明 |
|---|---|---|
totalCredit | BigDecimal | 总余额。 |
totalResourceCredit | BigDecimal | 资源包余额合计。 |
wallets.monthlyPlan | WalletDetail | 月度套餐(无则为 null)。 |
wallets.resourcePacks | WalletDetail[] | 资源包列表。 |
wallets.payAsYouGo | BigDecimal | 按量付费余额。 |
WalletDetailVO 字段::
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 钱包 ID。 |
credit | BigDecimal | 余额积分。 |
name | String | 钱包名称。 |
List UsageGEThttps://api.basicrouter.ai/api/v1/usage
分页的模型调用计费详情,按价格 (priceSnapshotId)快照,按订单创建时间倒序排列。仅返回正常扣费记录 (reason = model usage)。
查询参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
page | Integer | 否 | 1 | 页码,从 1 开始。 |
size | Integer | 否 | 20 | 每页条数(按 priceSnapshotId 分页)。 |
startTime | LocalDateTime | 否 | — | 开始时间,格式 yyyy-MM-ddTHH:mm:ss,按快照 orderCreatedAt 筛选。 |
endTime | LocalDateTime | 否 | — | 结束时间,格式 yyyy-MM-ddTHH:mm:ss。 |
curl --request GET \
--url "https://api.basicrouter.ai/api/v1/usage?page=1&size=20&startTime=2026-07-01T00:00:00&endTime=2026-07-31T23:59:59" \
--header "Authorization: Bearer $BASICROUTER_API_KEY"响应包装:
{
"code": 0,
"message": "success",
"data": { ... }
}| 字段 | 类型 | 说明 |
|---|---|---|
records | UsageDetailVO[] | 当前页记录。 |
total | Long | 总条数。 |
current | Long | 当前页码。 |
size | Long | 每页条数。 |
pages | Long | 总页数。 |
UsageDetailVO 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
priceSnapshotId | String | 价格快照 ID。 |
taskId | String | 任务 ID。 |
credit | BigDecimal | 扣费金额。 |
model | String | 模型名称。 |
modelType | String | text / image / video。 |
inputTokens | Long | 输入 token 数;图像/视频为 null。 |
outputTokens | Long | 输出 token 数。 |
totalTokens | Long | 总 token 数。 |
cacheReadTokens | Long | 缓存读取 token 数。 |
cacheWriteTokens | Long | 缓存写入 token 数。 |
imageCount | Integer | 图像数量;图像模型时设置。 |
imageResolution | String | 图像分辨率,例如 720P。 |
imageRatio | String | 图像宽高比,例如 1:1。 |
videoResolution | String | 视频分辨率,例如 1080p。 |
videoRatio | String | 视频宽高比,例如 16:9。 |
videoDurationSec | Long | 视频时长(秒)。 |
orderCreatedAt | LocalDateTime | 订单创建时间(快照 orderCreatedAt)。 |
creditDetails | CreditDetailItem[] | 该快照下的订单明细(来自 credit_order_t)。 |
CreditDetailItem 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
credit | BigDecimal | 该订单的扣费金额。 |
deductionSource | String | 扣费来源(Balance / Monthly Package / Resource Package)。 |
packageName | String | 套餐名称;无套餐时为 null。 |
空值约定:仅填充与各 modelType 相关的字段,其余为 null。text 填充 token 字段; image 填充 imageCount/imageResolution/imageRatio; video 填充 videoResolution/videoRatio/videoDurationSec。
响应示例:
{
"code": 200,
"message": "success",
"data": {
"records": [
{
"priceSnapshotId": "snap_9f3c1a2b",
"taskId": "task_5e8a1c33",
"credit": 0.0342,
"model": "glm-5.2",
"modelType": "text",
"inputTokens": 1280,
"outputTokens": 642,
"totalTokens": 1922,
"cacheReadTokens": 0,
"cacheWriteTokens": 0,
"imageCount": null,
"imageResolution": null,
"imageRatio": null,
"videoResolution": null,
"videoRatio": null,
"videoDurationSec": null,
"orderCreatedAt": "2026-07-18T14:23:11",
"creditDetails": [
{
"credit": 0.0342,
"deductionSource": "balance",
"packageName": ""
}
]
},
{
"priceSnapshotId": "snap_a12f77c0",
"taskId": "task_c71e44a2",
"credit": 1.8000,
"model": "seedance-2.0",
"modelType": "video",
"inputTokens": null,
"outputTokens": null,
"totalTokens": null,
"cacheReadTokens": null,
"cacheWriteTokens": null,
"imageCount": null,
"imageResolution": null,
"imageRatio": null,
"videoResolution": "1080p",
"videoRatio": "16:9",
"videoDurationSec": 8,
"orderCreatedAt": "2026-07-17T22:41:09",
"creditDetails": [
{
"credit": 1.5000,
"deductionSource": "Monthly Package",
"packageName": "基础月度套餐"
},
{
"credit": 0.3000,
"deductionSource": "Resource Package",
"packageName": "byteplus视频资源包"
}
]
}
],
"total": 128,
"current": 1,
"size": 20,
"pages": 7
}
}List Billing TransactionsGEThttps://api.basicrouter.ai/api/v1/billing/transactions
分页返回当前用户已支付(status=2)的充值交易,按 created_at 倒序排列。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
page | Integer | 否 | 1 | 页码。 |
size | Integer | 否 | 20 | 每页条数。 |
startTime | String | 否 | — | 开始时间,yyyy-MM-dd HH:mm:ss,包含边界。 |
endTime | String | 否 | — | 结束时间,yyyy-MM-dd HH:mm:ss,包含边界。 |
curl --request GET \
--url "https://api.basicrouter.ai/api/v1/billing/transactions?page=1&size=20&startTime=2026-07-01%2000:00:00&endTime=2026-07-31%2023:59:59" \
--header "Authorization: Bearer $BASICROUTER_API_KEY"响应包装:
{
"code": 0,
"message": "success",
"data": { ... }
}| 字段 | 类型 | 说明 |
|---|---|---|
records | TransactionVO[] | 当前页交易记录。 |
total | Long | 总条数。 |
current | Long | 当前页码。 |
size | Long | 每页条数。 |
pages | Long | 总页数。 |
TransactionVO 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
orderNo | String | 订单号。 |
thirdPartyOrderNo | String | 第三方订单号。 |
amount | BigDecimal | 订单金额。 |
actualAmount | BigDecimal | 实付金额。 |
discount | BigDecimal | 优惠金额。 |
paymentMethod | String | 支付方式(wechat / alipay / ustd / stripe / wallyt 等)。 |
TransactionVO 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
serviceFeeAmount | BigDecimal | 服务费金额。 |
paymentChannel | String | 支付渠道。 |
source | String | 订单来源(recharge / package_purchase 等)。 |
packageName | String | 套餐名称(套餐购买时设置;普通充值为 null)。 |
createdAt | LocalDateTime | 创建时间。 |
{
"code": 200,
"message": "success",
"data": {
"records": [
{
"orderNo": "R20260718abc123",
"thirdPartyOrderNo": "wx_pay_xxx",
"amount": 50.00,
"actualAmount": 48.50,
"discount": 1.50,
"paymentMethod": "wechat",
"serviceFeeAmount": 0.00,
"paymentChannel": "wechat",
"source": "recharge",
"packageName": null,
"createdAt": "2026-07-18T14:23:11"
}
],
"total": 28,
"current": 1,
"size": 20,
"pages": 2
}
}