Консоль

Справочник API

Общие соглашения

Базовый URL

Все конечные точки обслуживаются с префиксом /v1.

Аутентификация

Вызовы к конечным точкам /v1/* используют аутентификацию API Key (не JWT). API-ключ передаётся через следующий заголовок:

ЗаголовокФорматОписание
AuthorizationBearer <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
  • Некоторые конечные точки также оценивают минимальную стоимость по модели для второй предварительной проверки.

Create Chat CompletionPOSThttps://api.basicrouter.ai/api/v1/chat/completions

Конечная точка, совместимая с OpenAI Chat Completions. Поддерживает потоковый и непотоковый режимы, вызовы инструментов, режим JSON и мультимодальный ввод.

ПолеТипОбязательноОписание
modelStringДаИмя модели.
messagesMessage[]ДаСообщения диалога.
streamBooleanНетПотоковый режим, по умолчанию false.
temperatureDoubleНетТемпература выборки.
max_tokensIntegerНетМаксимальное количество выходных токенов.
top_pDoubleНетЯдерная выборка (nucleus sampling).
presence_penaltyDoubleНет—
frequency_penaltyDoubleНет—
toolsTool[]НетОпределения инструментов.
tool_choiceString|ObjectНетauto / none / required / конкретная функция.
response_formatObjectНет{type, json_schema:{name,schema,strict}}; text/json_object/json_schema.
parallel_tool_callsBooleanНет—
metadataMapНетПрокси-метаданные.

Поля Message:

ПолеТипОписание
roleStringsystem / user / assistant / tool.
contentString|ArrayПростой текст или массив блоков мультимодального содержимого ([{type:"text",text},{type:"image_url",image_url:{url}}]).
tool_call_idStringСвязь с tool_calls при role=tool.
tool_callsToolCall[]Присутствует, когда role=assistant совершает вызовы инструментов.
ПолеТипОписание
typeStringФиксированное значение function.
functionObjectОпределение функции.
function.nameStringИмя функции.
function.descriptionStringОписание функции.
function.parametersObjectJSON 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}
}

Поля ответа (непотоковый):

ПолеТипОписание
idStringИдентификатор завершения.
objectStringФиксированное значение chat.completion.
createdLongМетка времени создания (в секундах).
modelStringИмя модели.
choicesChoice[]{index, message:{role, content, tool_calls?}, finish_reason}.
usageObject{prompt_tokens, completion_tokens, total_tokens}.
ПолеТипОписание
idStringИдентификатор вызова инструмента.
typeStringФиксированное значение function.
functionObjectСведения о вызове функции.
function.nameStringИмя функции.
function.argumentsObjectАргументы функции.

Пример потокового ответа:

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.

ПолеТипОбязательноОписание
modelStringДаИмя модели.
inputString|ArrayДаПростая строка (сообщение пользователя) или массив объектов сообщений.
instructionsStringНетСистемный промпт.
streamBooleanНетПо умолчанию false.
max_output_tokensIntegerНетМаксимальное количество выходных токенов.
temperatureDoubleНетПо умолчанию 1.
top_pDoubleНет—
toolsTool[]НетВерхнеуровневые {type, name, description, parameters}.
tool_choiceString|ObjectНетauto/none/required/{type,name}.
textObjectНет{format:{type, name, schema, strict}}; text/json_object/json_schema.
metadataMapНет—
previous_response_idStringНетИдентификатор предыдущего ответа для многоходового диалога.
parallel_tool_callsBooleanНет—
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}
}

Поля ответа (непотоковый):

ПолеТипОписание
idStringИдентификатор ответа.
objectStringФиксированное значение response.
modelStringИмя модели.
statusStringнапример, completed.
created_atLongМетка времени создания (в секундах).
outputArrayЭлементы вывода. Элементы сообщений: {id, type:"message", role, content:[{type:"output_text", text}], status}. Элементы вызова инструментов: {type:"function_call", id, name, call_id, arguments, status}.
usageObject{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-полеОписание
modelStringДаmodelИмя модели.
messagesMessage[]ДаmessagesСообщения диалога.
systemString|ArrayНетsystemСистемный промпт, строка или [{type,text}].
maxTokensIntegerДаmax_tokensМаксимальное количество выходных токенов.
streamBooleanНетstreamПотоковая передача.
temperatureDoubleНетtemperature—
topPDoubleНетtop_p—
topKIntegerНетtop_k—
toolsTool[]НетtoolsОпределения инструментов (input_schema).
toolChoiceObjectНетtool_choice—
metadataMapНетmetadata—
thinkingObjectНетthinkingКонфигурация расширенного мышления.
stopSequencesObjectНетstop_sequences—
anthropicBetaObjectНетanthropic_betaЗаголовок бета-функции.
ПолеТипОписание
roleStringРоль сообщения, например user / assistant.
contentString|ContentBlock[]Простой текст или массив блоков содержимого.
ПолеТипОписание
typeStringОдно из text, image, tool_use, tool_result, thinking, redacted_thinking.
textStringПрисутствует, когда тип — text.
sourceObjectПрисутствует, когда тип — image.

Примеры блоков изображений:

{ "type": "image", "source": { "type": "base64", "media_type": "...", "data": "..." } }
{ "type": "image", "source": { "type": "url", "url": "..." } }
ПолеТипОписание
nameStringИмя функции.
descriptionStringОписание функции.
input_schemaObjectJSON Schema для входных данных.
cache_controlObjectНеобязательное управление кэшем.
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}
}

Поля ответа (непотоковый):

ПолеТипОписание
idStringИдентификатор сообщения.
typeStringФиксированное значение message.
roleStringФиксированное значение assistant.
modelStringИмя модели.
contentContentBlock[]Блоки содержимого ответа (например, {type:"text", text}, {type:"tool_use", ...}).
stop_reasonStringнапример, end_turn, tool_use, max_tokens.
usageObject{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[]):

ПолеТипОписание
idStringИдентификатор модели.
objectStringФиксированное значение model.
display_nameStringОтображаемое имя.
createdLongМетка времени создания (в секундах).
owned_byStringВладелец / вендор.
input_modalitiesString[]например, ["text","image"].
output_modalitiesString[]например, ["text"].
context_lengthIntegerМаксимальная длина контекста.
descriptionStringОписание модели.

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. Аутентификация не требуется.

ПолеТипОписание
idStringИдентификатор модели.
objectStringФиксированное значение image_model.
displayNameStringОтображаемое имя.
descriptionStringОписание модели.
iconStringURL иконки.
createdLongМетка времени создания (в секундах).
maxCountIntegerМаксимум изображений за запрос.
fileMaxIntegerМаксимум референсных изображений. Если 0, генерация изображения по изображению не поддерживается.
resolutionsString[]Поддерживаемые разрешения, например, ["720p","1080p"].
ratiosString[]Поддерживаемые соотношения сторон, например, ["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. Аутентификация не требуется.

ПолеТипОписание
idStringИдентификатор модели.
objectStringФиксированное значение video_model.
displayNameStringОтображаемое имя.
descriptionStringОписание модели.
iconStringURL иконки.
createdLongМетка времени создания (в секундах).
allowedVideoTypesVideoTypeOption[]Список поддерживаемых videoType.
videoDurationMinIntegerМинимум секунд на клип.
videoDurationMaxIntegerМаксимум секунд на клип.
videoDurationSuggestInteger[]Рекомендуемые шаги длительности, например [5,8,10].
resolutionsString[]Поддерживаемые разрешения.
ratiosString[]Поддерживаемые соотношения сторон.
resolutionOptionsResolutionOption[]Структурированные комбинации разрешение+соотношение+размер.
fileMaxIntegerМаксимум референсных ресурсов.

Поля VideoTypeOption:

ПолеТипОписание
codeIntegerЗначение videoType для передачи в /v1/video-generations.
nameStringЛокализованное имя типа (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 — массив, каждый элемент — ценовой уровень:

ПолеТипОписание
outputPricedecimalДействующая выходная цена за единицу для текущего вызывающего
cachePricedecimalЦена кэша (универсальные модели, используется в старой формуле биллинга)
cacheReadPricedecimalЦена чтения кэша (только для Bedrock Claude)
cacheWritePricedecimalЦена записи кэша (только для Bedrock Claude)
ratiodecimalКоэффициент биллинга. Режим FIXED равен 1; режим RATIO — коэффициент пользователя/группы
modestringРежим ценообразования: RATIO / FIXED
planIdstringID совпавшего ценового плана, может быть null

Три типа моделей используют одну структуру; отличается только заполнение описательных полей. Неприменимые поля равны null:

Тип моделиUNITЭффективные описательные поля
Текстtokenregion / bandMin / bandMax
Изображениеimageresolution / clarity
Видеоvideoresolution / 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. Принимаются только значения, заявленные в спецификации модели.

ПолеТипОбязательноОписание
textStringДаПромпт.
modelStringДаИмя модели.
imageUrlsString[]НетURL референсных изображений (image-to-image).
countIntegerНетКоличество изображений (≥0).
resolutionStringНетРазрешение (см. /v1/image-models).
ratioStringНетСоотношение сторон.
callbackUrlStringНет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 ответа:

ПолеТипОписание
taskIdStringИдентификатор задачи.
statusStringpending / success / failed.
errorMessageStringПричина ошибки, null при успехе.
imagesStringJSON-строка массива URL изображений, например, "[\"https://.../1.png\"]".
textStringТекстовое описание, добавленное моделью (например, мультимодальный вывод 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 этой модели.

ПолеТипОбязательноОписание
textStringДаПромпт.
modelStringДаИмя модели.
videoTypeIntegerДа1 text-to-video / 2 image-to-video (первый кадр) / 3 image-to-video (первый+последний кадр) / 4 image-to-video (референс) / 5 все референсы.
imageUrlsString[]НетURL изображений.
videoUrlsVideoUrl[]|String[]НетURL видеоресурсов.
audioUrlsString[]НетURL аудиоресурсов.
resolutionStringНетРазрешение.
ratioStringНетСоотношение сторон.
durationLongНетСекунды (>0).
callbackUrlStringНет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 ответа:

ПолеТипОписание
statusStringpending / success / failed.
videoUrlStringURL сгенерированного видео.
lastFrameUrlStringURL последнего кадра (для сценариев image-to-video); иначе null.
messageStringПричина ошибки, 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
  }
}

Поля ответа:

ПолеТипОписание
totalCreditBigDecimalОбщий баланс.
totalResourceCreditBigDecimalСумма балансов пакетов ресурсов.
wallets.monthlyPlanWalletDetailМесячный тариф (null, если отсутствует).
wallets.resourcePacksWalletDetail[]Список пакетов ресурсов.
wallets.payAsYouGoBigDecimalБаланс с оплатой по факту.

Поля WalletDetailVO::

ПолеТипОписание
idStringИдентификатор кошелька.
creditBigDecimalКредиты на балансе.
nameStringНазвание кошелька.

List UsageGEThttps://api.basicrouter.ai/api/v1/usage

Постраничные детали биллинга вызовов моделей, снимок по цене (priceSnapshotId), упорядоченные по времени создания заказа в порядке убывания. Возвращаются только обычные записи списания (reason = model usage).

Параметры запроса:

ПараметрТипОбязательноПо умолчаниюОписание
pageIntegerНет1Номер страницы, начиная с 1.
sizeIntegerНет20Размер страницы (пагинация по priceSnapshotId).
startTimeLocalDateTimeНет—Время начала, формат yyyy-MM-ddTHH:mm:ss, фильтрует по снимку orderCreatedAt.
endTimeLocalDateTimeНет—Время окончания, формат 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": { ... }
}
ПолеТипОписание
recordsUsageDetailVO[]Записи текущей страницы.
totalLongОбщее количество.
currentLongТекущая страница.
sizeLongРазмер страницы.
pagesLongВсего страниц.

Поля UsageDetailVO:

ПолеТипОписание
priceSnapshotIdStringИдентификатор снимка цены.
taskIdStringИдентификатор задачи.
creditBigDecimalСписанная сумма.
modelStringИмя модели.
modelTypeStringtext / image / video.
inputTokensLongВходные токены; null для image/video.
outputTokensLongВыходные токены.
totalTokensLongВсего токенов.
cacheReadTokensLongТокены чтения из кэша.
cacheWriteTokensLongТокены записи в кэш.
imageCountIntegerКоличество изображений; задаётся для моделей изображений.
imageResolutionStringРазрешение изображения, например 720P.
imageRatioStringСоотношение сторон изображения, например 1:1.
videoResolutionStringРазрешение видео, например 1080p.
videoRatioStringСоотношение сторон видео, например 16:9.
videoDurationSecLongДлительность видео в секундах.
orderCreatedAtLocalDateTimeВремя создания заказа (снимок orderCreatedAt).
creditDetailsCreditDetailItem[]Детали заказов под этим снимком (из credit_order_t).

Поля CreditDetailItem:

ПолеТипОписание
creditBigDecimalСумма, списанная этим заказом.
deductionSourceStringИсточник списания (Balance / Monthly Package / Resource Package).
packageNameStringИмя пакета; 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 по убыванию.

ПараметрТипОбязательныйПо умолчаниюОписание
pageIntegerНет1Номер страницы.
sizeIntegerНет20Размер страницы.
startTimeStringНет—Начальное время, yyyy-MM-dd HH:mm:ss, включительно.
endTimeStringНет—Конечное время, 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": { ... }
}
ПолеТипОписание
recordsTransactionVO[]Транзакции текущей страницы.
totalLongОбщее количество.
currentLongТекущая страница.
sizeLongРазмер страницы.
pagesLongВсего страниц.

Поля TransactionVO:

ПолеТипОписание
orderNoStringНомер заказа.
thirdPartyOrderNoStringНомер заказа третьей стороны.
amountBigDecimalСумма заказа.
actualAmountBigDecimalФактически оплаченная сумма.
discountBigDecimalСумма скидки.
paymentMethodStringСпособ оплаты (wechat / alipay / ustd / stripe / wallyt и т. д.).

Поля TransactionVO:

ПолеТипОписание
serviceFeeAmountBigDecimalСумма сервисного сбора.
paymentChannelStringПлатёжная платформа.
sourceStringИсточник заказа (recharge / package_purchase и т. д.).
packageNameStringИмя пакета (задаётся при покупке пакета; null для обычных пополнений).
createdAtLocalDateTimeВремя создания.
{
  "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
  }
}