Справочник API
Общие соглашения
Базовый URL
Все конечные точки обслуживаются с префиксом /v1.
Аутентификация
Вызовы к конечным точкам /v1/* используют аутентификацию API Key (не JWT). API-ключ передаётся через следующий заголовок:
| Заголовок | Формат | Описание |
|---|---|---|
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 | Нет | Максимальное количество выходных токенов. |
top_p | Double | Нет | Ядерная выборка (nucleus sampling). |
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 | Связь с tool_calls при role=tool. |
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 | Идентификатор завершения. |
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 | Идентификатор вызова инструмента. |
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 | Нет | Максимальное количество выходных токенов. |
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 | Нет | Идентификатор предыдущего ответа для многоходового диалога. |
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 | Идентификатор ответа. |
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 | Максимальное количество выходных токенов. |
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 | Заголовок бета-функции. |
| Поле | Тип | Описание |
|---|---|---|
role | String | Роль сообщения, например user / assistant. |
content | String|ContentBlock[] | Простой текст или массив блоков содержимого. |
| Поле | Тип | Описание |
|---|---|---|
type | String | Одно из text, image, tool_use, tool_result, thinking, redacted_thinking. |
text | String | Присутствует, когда тип — text. |
source | Object | Присутствует, когда тип — 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 | Идентификатор сообщения. |
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 | Идентификатор модели. |
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 | Идентификатор модели. |
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
Запросите поддерживаемые значения videoType, диапазон длительности, разрешения и соотношения сторон для видеомодели перед вызовом /v1/video-generations. Аутентификация не требуется.
| Поле | Тип | Описание |
|---|---|---|
id | String | Идентификатор модели. |
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 | Значение videoType для передачи в /v1/video-generations. |
name | String | Локализованное имя типа (text-to-video / image-to-video / ...). |
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
}
]
}Описание полей ценовых уровней
4 endpoint запроса моделей /v1/models, /v1/models/{model}, /v1/image-models и /v1/video-models возвращают действующую цену биллинга для текущего вызывающего (пользователя API Key), полностью совпадающую с фактическим списанием, и возвращают все ценовые уровни модели.
Различие в именовании полей: /v1/models и /v1/models/{model} используют стиль OpenAI snake_case (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 — это конечные расчётные единицы цены для текущего пользователя API Key после пошагового разрешения цепочки цен (chain)→ guidance → базовая цена, совпадающие с фактическим списанием. Единица цены, видимая вызывающей стороной, зависит от принадлежности к цепочке цен (reseller / distributor / компания / пользовательская конфигурация).
Фактическое списание = объём ÷ quantity × соответствующая цена × ratio. При посекундном биллинге видео: фактическое списание = duration × outputPrice × ratio.
Граничные случаи и обработка исключений (read-only endpoint не вернёт 500 из-за проблем конфигурации цен)
| Сценарий | Поведение |
|---|---|
| Ошибка разрешения одного уровня (ценовая политика запрещает доступ, конфигурация отсутствует) | Пропустить этот уровень, записать warn-лог и продолжить разрешение остальных уровней |
| Все уровни модели не удалось разрешить | Пустой массив [], модель возвращается нормально |
| У модели нет ценовой конфигурации | Пустой массив [] |
| Разбор цен выбросил необработанное исключение | Общий try-catch, вернуть пустой массив, endpoint всё ещё возвращает 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": "за 1 тыс. токенов",
"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.
Значения model, поддерживаемые значения resolution / ratio, верхний предел count и лимит загрузки референсных изображений (fileMax) необходимо сначала получить из GET /v1/image-models. Принимаются только значения, заявленные в спецификации модели.
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
text | String | Да | Промпт. |
model | String | Да | Имя модели. |
imageUrls | String[] | Нет | URL референсных изображений (image-to-image). |
count | Integer | Нет | Количество изображений (≥0). |
resolution | String | Нет | Разрешение (см. /v1/image-models). |
ratio | String | Нет | Соотношение сторон. |
callbackUrl | String | Нет | 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 — это JSON-строка массива URL изображений; 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 | Идентификатор задачи. |
status | String | pending / success / failed. |
errorMessage | String | Причина ошибки, null при успехе. |
images | String | JSON-строка массива URL изображений, например, "[\"https://.../1.png\"]". |
text | String | Текстовое описание, добавленное моделью (например, мультимодальный вывод Gemini); иначе null. |
Задача не найдена:
{ "code": 500, "message": "task not found" }Если при отправке был предоставлен callbackUrl, сервер отправляет окончательный результат 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.
Значения model, разрешённые значения videoType, диапазон длительности (videoDurationMin/Max), поддерживаемые resolution / ratio и лимит загрузки референсных ресурсов (fileMax) необходимо сначала получить из GET /v1/video-models. Принимаются только коды videoType, перечисленные в allowedVideoTypes этой модели.
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
text | String | Да | Промпт. |
model | String | Да | Имя модели. |
videoType | Integer | Да | 1 text-to-video / 2 image-to-video (первый кадр) / 3 image-to-video (первый+последний кадр) / 4 image-to-video (референс) / 5 все референсы. |
imageUrls | String[] | Нет | URL изображений. |
videoUrls | VideoUrl[]|String[] | Нет | URL видеоресурсов. |
audioUrls | String[] | Нет | URL аудиоресурсов. |
resolution | String | Нет | Разрешение. |
ratio | String | Нет | Соотношение сторон. |
duration | Long | Нет | Секунды (>0). |
callbackUrl | String | Нет | 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)
Смешанные референсы изображений / видео / аудио. Указывайте референсные ресурсы по позиции в промпте: 1-й элемент в imageUrls — это @图片 1, 1-й в videoUrls — @视频 1, 1-й в audioUrls — @音频 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 последнего кадра (для сценариев image-to-video).
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 последнего кадра (для сценариев image-to-video); иначе null. |
message | String | Причина ошибки, null при успехе. |
Если при отправке был предоставлен callbackUrl, сервер отправляет окончательный результат через вебхук с той же структурой 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 | Идентификатор кошелька. |
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 | Идентификатор снимка цены. |
taskId | String | Идентификатор задачи. |
credit | BigDecimal | Списанная сумма. |
model | String | Имя модели. |
modelType | String | text / image / video. |
inputTokens | Long | Входные токены; null для image/video. |
outputTokens | Long | Выходные токены. |
totalTokens | Long | Всего токенов. |
cacheReadTokens | Long | Токены чтения из кэша. |
cacheWriteTokens | Long | Токены записи в кэш. |
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, если пакета нет. |
Соглашение о null-значениях: заполняются только поля, относящиеся к каждому modelType; остальные равны null. Для text заполняются поля токенов; для 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
}
}