Console

Referência da API

Convenções comuns

URL base

Todos os endpoints são servidos sob o prefixo /v1.

Autenticação

Chamadas para endpoints /v1/* usam autenticação por Chave API (não JWT). A Chave API é passada pelo seguinte cabeçalho:

CabeçalhoFormatoDescrição
AuthorizationBearer <api_key>Estilo OpenAI. O endpoint compatível com Anthropic também aceita x-api-key com anthropic-version: 2023-06-01.

Chaves ausentes ou inválidas retornam 401.

Pré-verificação de saldo

Todos os endpoints de chamada de modelo executam uma pré-verificação de saldo antes da execução:

  • Saldo insuficiente retorna Insufficient credit, mapeado para:
    • Protocolo OpenAI: HTTP 400, code = insufficient_quota
    • Protocolo Anthropic: HTTP 402, type = billing_error
  • Alguns endpoints também estimam um custo mínimo por modelo para uma segunda pré-verificação.

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

Endpoint compatível com OpenAI Chat Completions. Suporta streaming e não streaming, chamadas de ferramentas, modo JSON e entrada multimodal.

CampoTipoObrigatórioDescrição
modelStringSimNome do modelo.
messagesMessage[]SimMensagens da conversa.
streamBooleanNãoModo de stream, padrão false.
temperatureDoubleNãoTemperatura de amostragem.
max_tokensIntegerNãoMáximo de tokens de saída.
top_pDoubleNãoAmostragem por núcleo.
presence_penaltyDoubleNão—
frequency_penaltyDoubleNão—
toolsTool[]NãoDefinições de ferramentas.
tool_choiceString|ObjectNãoauto / none / required / função específica.
response_formatObjectNão{type, json_schema:{name,schema,strict}}; text/json_object/json_schema.
parallel_tool_callsBooleanNão—
metadataMapNãoMetadados de passagem.

Campos de Message:

CampoTipoDescrição
roleStringsystem / user / assistant / tool.
contentString|ArrayTexto simples ou array de blocos de conteúdo multimodal ([{type:"text",text},{type:"image_url",image_url:{url}}]).
tool_call_idStringVincula ao tool_calls quando role=tool.
tool_callsToolCall[]Presente quando role=assistant faz chamadas de ferramentas.
CampoTipoDescrição
typeStringFixo function.
functionObjectDefinição da função.
function.nameStringNome da função.
function.descriptionStringDescrição da função.
function.parametersObjectEsquema JSON para entradas.
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}
}

Campos de resposta (não streaming):

CampoTipoDescrição
idStringID de conclusão.
objectStringFixo chat.completion.
createdLongCarimbo de data/hora de criação (segundos).
modelStringNome do modelo.
choicesChoice[]{index, message:{role, content, tool_calls?}, finish_reason}.
usageObject{prompt_tokens, completion_tokens, total_tokens}.
CampoTipoDescrição
idStringID da chamada de ferramenta.
typeStringFixo function.
functionObjectDetalhes da chamada de função.
function.nameStringNome da função.
function.argumentsObjectArgumentos da função.

Exemplo de resposta em streaming:

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

Endpoint compatível com OpenAI Responses. Usa input em vez de messages, instructions em vez de uma mensagem de sistema, e um bloco text em vez de response_format.

CampoTipoObrigatórioDescrição
modelStringSimNome do modelo.
inputString|ArraySimString simples (mensagem do usuário) ou array de objetos de mensagem.
instructionsStringNãoPrompt de sistema.
streamBooleanNãoPadrão false.
max_output_tokensIntegerNãoMáximo de tokens de saída.
temperatureDoubleNãoPadrão 1.
top_pDoubleNão—
toolsTool[]NãoNível superior {type, name, description, parameters}.
tool_choiceString|ObjectNãoauto/none/required/{type,name}.
textObjectNão{format:{type, name, schema, strict}}; text/json_object/json_schema.
metadataMapNão—
previous_response_idStringNãoID da resposta anterior para múltiplos turnos.
parallel_tool_callsBooleanNão—
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}
}

Campos de resposta (não streaming):

CampoTipoDescrição
idStringID da resposta.
objectStringFixo response.
modelStringNome do modelo.
statusStringex. completed.
created_atLongCarimbo de data/hora de criação (segundos).
outputArrayItens de saída. Itens de mensagem: {id, type:"message", role, content:[{type:"output_text", text}], status}. Itens de chamada de ferramenta: {type:"function_call", id, name, call_id, arguments, status}.
usageObject{input_tokens, output_tokens, total_tokens}. Para modelos Claude, input_tokens inclui cache_read e output_tokens inclui cache_write.

O streaming segue os eventos da API Responses:

EventoDescrição
response.createdInício do fluxo de resposta.
response.output_text.deltaAtualização incremental de saída de texto.
response.completedFim do fluxo de resposta.

Create Anthropic MessagePOSThttps://api.basicrouter.ai/api/v1/messages

Endpoint compatível com Anthropic Messages. Aceita cabeçalhos x-api-key e anthropic-version: 2023-06-01. Os blocos de conteúdo suportam text, image, tool_use, tool_result, thinking e redacted_thinking.

CampoTipoObrigatórioCampo JSONDescrição
modelStringSimmodelNome do modelo.
messagesMessage[]SimmessagesMensagens da conversa.
systemString|ArrayNãosystemPrompt de sistema, string ou [{type,text}].
maxTokensIntegerSimmax_tokensMáximo de tokens de saída.
streamBooleanNãostreamResposta em streaming.
temperatureDoubleNãotemperature—
topPDoubleNãotop_p—
topKIntegerNãotop_k—
toolsTool[]NãotoolsDefinições de ferramentas (input_schema).
toolChoiceObjectNãotool_choice—
metadataMapNãometadata—
thinkingObjectNãothinkingConfiguração de raciocínio estendido.
stopSequencesObjectNãostop_sequences—
anthropicBetaObjectNãoanthropic_betaCabeçalho de recurso beta.
CampoTipoDescrição
roleStringPapel da mensagem, ex. user / assistant.
contentString|ContentBlock[]Texto simples ou um array de blocos de conteúdo.
CampoTipoDescrição
typeStringUm entre text, image, tool_use, tool_result, thinking, redacted_thinking.
textStringPresente quando o tipo é texto.
sourceObjectPresente quando type é image.

Exemplos de bloco de imagem:

{ "type": "image", "source": { "type": "base64", "media_type": "...", "data": "..." } }
{ "type": "image", "source": { "type": "url", "url": "..." } }
CampoTipoDescrição
nameStringNome da função.
descriptionStringDescrição da função.
input_schemaObjectEsquema JSON para entradas.
cache_controlObjectControle de cache opcional.
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}
}

Campos de resposta (não streaming):

CampoTipoDescrição
idStringID da mensagem.
typeStringFixo message.
roleStringFixo assistant.
modelStringNome do modelo.
contentContentBlock[]Blocos de conteúdo da resposta (ex. {type:"text", text}, {type:"tool_use", ...}).
stop_reasonStringex. end_turn, tool_use, max_tokens.
usageObject{input_tokens, output_tokens}.
EventoDescrição
message_startInício do fluxo de mensagens.
content_block_startInício de um novo bloco de conteúdo.
content_block_deltaAtualização incremental de um bloco de conteúdo.
content_block_stopFim de um bloco de conteúdo.
message_deltaAtualização incremental da mensagem.
message_stopFim do fluxo de mensagens.

List ModelsGEThttps://api.basicrouter.ai/api/v1/models

Retorna todos os modelos de API online e ativados.

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": "..."
    }
  ]
}

Campos de cada entrada de modelo (data[]):

CampoTipoDescrição
idStringID do modelo.
objectStringFixo model.
display_nameStringNome de exibição.
createdLongCarimbo de data/hora de criação (segundos).
owned_byStringProprietário / fornecedor.
input_modalitiesString[]por exemplo ["text","image"].
output_modalitiesString[]por exemplo ["text"].
context_lengthIntegerComprimento máximo de contexto.
descriptionStringDescrição do modelo.

Retrieve ModelGEThttps://api.basicrouter.ai/api/v1/models/{model}

Retorna um único modelo com o mesmo formato de uma entrada de lista. Retorna HTTP 404 quando o modelo não existe.

curl --request GET \
  --url https://api.basicrouter.ai/api/v1/models/gpt-5.5 \
  --header "Authorization: Bearer $BASICROUTER_API_KEY"

Resposta de sucesso: um objeto de modelo único com os mesmos campos de uma entrada de lista /v1/models.

Quando o modelo não existe, retorna 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

Consulte as resoluções, proporções e contagens máximas suportadas por um modelo de imagem antes de chamar /v1/image-generations. Não requer autenticação.

CampoTipoDescrição
idStringID do modelo.
objectStringValor fixo image_model.
displayNameStringNome de exibição.
descriptionStringDescrição do modelo.
iconStringURL do ícone.
createdLongCarimbo de data/hora de criação (segundos).
maxCountIntegerMáximo de imagens por solicitação.
fileMaxIntegerMáximo de imagens de referência. Quando 0, não suporta geração de imagem a partir de imagem.
resolutionsString[]Resoluções suportadas, por exemplo ["720p","1080p"].
ratiosString[]Proporções de imagem suportadas, por exemplo ["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

Consulte os valores de videoType suportados, intervalo de duração, resoluções e proporções para um modelo de vídeo antes de chamar /v1/video-generations. Não requer autenticação.

CampoTipoDescrição
idStringID do modelo.
objectStringValor fixo video_model.
displayNameStringNome de exibição.
descriptionStringDescrição do modelo.
iconStringURL do ícone.
createdLongCarimbo de data/hora de criação (segundos).
allowedVideoTypesVideoTypeOption[]Lista de videoType suportados.
videoDurationMinIntegerMínimo de segundos por clipe.
videoDurationMaxIntegerMáximo de segundos por clipe.
videoDurationSuggestInteger[]Passos de duração recomendados, ex. [5,8,10].
resolutionsString[]Resoluções suportadas.
ratiosString[]Proporções de tela suportadas.
resolutionOptionsResolutionOption[]Combinações estruturadas de resolução+proporção+tamanho.
fileMaxIntegerMáximo de ativos de referência.

Campos de VideoTypeOption:

CampoTipoDescrição
codeIntegerO valor de videoType a ser passado para /v1/video-generations.
nameStringNome do tipo localizado (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
    }
  ]
}

Descrição dos campos de faixas de preço

Os 4 endpoints de consulta de modelos /v1/models, /v1/models/{model}, /v1/image-models e /v1/video-models retornam o preço de faturamento efetivo para o chamador (usuário de API Key), totalmente consistente com a cobrança real, e retornam todas as faixas de preço do modelo.

Diferença de nomenclatura: /v1/models e /v1/models/{model} usam o estilo OpenAI snake_case (price_tiers); /v1/image-models e /v1/video-models usam camelCase (priceTiers). As estruturas são idênticas.

price_tiers / priceTiers é um array; cada elemento é uma faixa de preço:

CampoTipoDescrição
outputPricedecimalPreço unitário de saída efetivo para o chamador atual
cachePricedecimalPreço de cache (modelos gerais, usado pela fórmula de faturamento antiga)
cacheReadPricedecimalPreço unitário de leitura de cache (exclusivo do Bedrock Claude)
cacheWritePricedecimalPreço unitário de escrita de cache (exclusivo do Bedrock Claude)
ratiodecimalMultiplicador de faturamento. O modo FIXED é 1; o modo RATIO é o multiplicador de usuário/grupo
modestringModo de preço: RATIO / FIXED
planIdstringID do plano de preços correspondente, pode ser null

Os três tipos de modelo compartilham a mesma estrutura; apenas o preenchimento dos campos descritivos difere. Campos não aplicáveis são null:

Tipo de modeloUNITCampos descritivos efetivos
Textotokenregion / bandMin / bandMax
Imagemimageresolution / clarity
Vídeovideoresolution / clarity

Significado do preço: os valores inputPrice / outputPrice / cacheReadPrice / cacheWritePrice retornados são os preços unitários de faturamento finais para o usuário de API Key atual após a resolução pela cadeia de preços (chain)→ guidance → preço base, consistentes com a cobrança real. O preço unitário visto pelo chamador varia de acordo com a cadeia de preços à qual pertence (reseller / distributor / empresa / configuração em nível de usuário).

Dedução real = uso ÷ quantity × preço unitário correspondente × ratio. Para vídeo por segundo: dedução real = duration × outputPrice × ratio.

Limites e tratamento de exceções (endpoints somente leitura não retornarão 500 devido a problemas de configuração de preços)

CenárioComportamento
Falha na resolução de uma única faixa (política de preços nega acesso, configuração ausente)Ignorar essa faixa, registrar log warn e continuar resolvendo as faixas restantes
Todas as faixas de um modelo falham na resoluçãoArray vazio [], o modelo ainda é retornado normalmente
O modelo não possui nenhuma configuração de preçosArray vazio []
A análise de preços lança uma exceção não capturadatry-catch geral, retornar array vazio, o endpoint ainda retorna 200

Exemplo de resposta (/v1/models, modelo de texto):

{
  "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": "por cada 1k tokens",
          "inputPrice": 0.0025,
          "outputPrice": 0.01,
          "cachePrice": 0.00125,
          "cacheReadPrice": 0,
          "cacheWritePrice": 0,
          "ratio": 1,
          "mode": "RATIO",
          "planId": null
        }
      ]
    }
  ]
}

Exemplo de resposta (/v1/image-models, modelo de imagem, a estrutura de priceTiers é a mesma, 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": "por imagem",
          "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

Envie assincronamente uma tarefa de geração de imagem. Retorna um taskId imediatamente; recupere o resultado fazendo polling de GET /v1/image-generations/{taskId} ou via webhook callbackUrl.

O model, os valores de resolution / ratio suportados, o limite máximo de count e o limite de upload de imagens de referência (fileMax) devem ser obtidos primeiro em GET /v1/image-models. Apenas os valores divulgados na especificação do modelo são aceitos.

CampoTipoObrigatórioDescrição
textStringSimPrompt.
modelStringSimNome do modelo.
imageUrlsString[]NãoURLs de imagens de referência (imagem-para-imagem).
countIntegerNãoNúmero de imagens (≥0).
resolutionStringNãoResolução (consulte /v1/image-models).
ratioStringNãoProporção da tela.
callbackUrlStringNãoURL de webhook no nível da tarefa.
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"}
}

Respostas de erro:

// 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}

Faça polling de uma tarefa de geração de imagem. status é pending / success / failed. images é um array em string JSON de URLs de imagem; text contém qualquer descrição de texto anexada pelo modelo (ex. saída multimodal do Gemini), null caso contrário.

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
  }
}

Campos de data da resposta:

CampoTipoDescrição
taskIdStringID da tarefa.
statusStringpending / success / failed.
errorMessageStringMotivo da falha, null em caso de sucesso.
imagesStringArray de URLs de imagens serializado como string JSON, por exemplo "[\"https://.../1.png\"]".
textStringDescrição de texto anexada pelo modelo (ex. saída multimodal do Gemini); null caso contrário.

Tarefa não encontrada:

{ "code": 500, "message": "task not found" }

Se callbackUrl foi fornecido no envio, o servidor envia o resultado final success / failed via webhook com o mesmo formato de data.

Exemplo completo (envio + polling)

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

Envie assincronamente uma tarefa de geração de vídeo. Retorna um taskId imediatamente; recupere o resultado fazendo polling de GET /v1/video-generations/{taskId} ou via webhook callbackUrl.

O model, os valores de videoType permitidos, o intervalo de duração (videoDurationMin/Max), resolution / ratio suportados e o limite de upload de ativos de referência (fileMax) devem ser obtidos primeiro em GET /v1/video-models. Apenas códigos de videoType listados em allowedVideoTypes do modelo são aceitos.

CampoTipoObrigatórioDescrição
textStringSimPrompt.
modelStringSimNome do modelo.
videoTypeIntegerSim1 texto-para-vídeo / 2 imagem-para-vídeo (primeiro quadro) / 3 imagem-para-vídeo (primeiro+último quadro) / 4 imagem-para-vídeo (referência) / 5 todas as referências.
imageUrlsString[]NãoURLs de ativos de imagem.
videoUrlsVideoUrl[]|String[]NãoURLs de ativos de vídeo.
audioUrlsString[]NãoURLs de ativos de áudio.
resolutionStringNãoResolução.
ratioStringNãoProporção da tela.
durationLongNãoSegundos (>0).
callbackUrlStringNãoURL de webhook no nível da tarefa.

Exemplos para cada videoType:

1. Texto para vídeo (videoType=1)

Gere um vídeo apenas a partir de um prompt de texto; não são necessários ativos de referência.

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. Imagem para vídeo - primeiro quadro (videoType=2)

Forneça um único quadro inicial em imageUrls; o modelo gera um vídeo a partir desse quadro.

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. Imagem para vídeo - primeiro e último quadro (videoType=3)

Forneça o primeiro e o último quadro em imageUrls (ordem: [primeiro, último]); o modelo gera um vídeo de transição entre os dois quadros.

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. Imagem para vídeo - referência (videoType=4)

Forneça uma ou mais imagens de referência em imageUrls; o modelo usa o estilo/conteúdo delas como referência (não como um primeiro/último quadro forçado) para gerar o vídeo.

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. Todas as referências (videoType=5)

Referências mistas de imagem / vídeo / áudio. Faça referência aos ativos por posição no prompt: a 1ª entrada em imageUrls é @图片 1, a 1ª em videoUrls é @视频 1, a 1ª em audioUrls é @音频 1. videoUrls também aceita strings de URL simples.

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
  }'

Resposta de envio (todos os cinco tipos):

{
  "code": 200,
  "message": "success",
  "data": {"taskId": "vid_xxx"}
}

Retrieve Video GenerationGEThttps://api.basicrouter.ai/api/v1/video-generations/{taskId}

Faça polling de uma tarefa de geração de vídeo. status é pending / success / failed; videoUrl é a URL do vídeo gerado e lastFrameUrl é a URL do último quadro (cenários de imagem-para-vídeo).

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
  }
}

Campos de data da resposta:

CampoTipoDescrição
statusStringpending / success / failed.
videoUrlStringURL do vídeo gerado.
lastFrameUrlStringURL do último quadro (cenários de imagem-para-vídeo); null caso contrário.
messageStringMotivo da falha, null em caso de sucesso.

Se callbackUrl foi fornecido no envio, o servidor envia o resultado final via webhook com o mesmo formato de data.

Exemplo completo (envio + polling)

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

Retorna o saldo da conta dividido em três carteiras: plano mensal, pacotes de recursos e crédito pré-pago.

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
  }
}

Campos de resposta:

CampoTipoDescrição
totalCreditBigDecimalSaldo total.
totalResourceCreditBigDecimalSoma dos saldos dos pacotes de recursos.
wallets.monthlyPlanWalletDetailPlano mensal (null se nenhum).
wallets.resourcePacksWalletDetail[]Lista de pacotes de recursos.
wallets.payAsYouGoBigDecimalSaldo pré-pago.

Campos de WalletDetailVO:

CampoTipoDescrição
idStringID da carteira.
creditBigDecimalCréditos do saldo.
nameStringNome da carteira.

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

Detalhes de cobrança paginados de chamadas de modelo, com snapshot por preço (priceSnapshotId), ordenados por tempo de criação do pedido em ordem decrescente. Apenas registros de cobrança normal (reason = model usage) são retornados.

Parâmetros de consulta:

ParâmetroTipoObrigatórioPadrãoDescrição
pageIntegerNão1Número da página, baseado em 1.
sizeIntegerNão20Tamanho da página (paginado por priceSnapshotId).
startTimeLocalDateTimeNão—Hora de início, formato yyyy-MM-ddTHH:mm:ss, filtra por snapshot orderCreatedAt.
endTimeLocalDateTimeNão—Hora de término, formato 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"

Wrapper de resposta:

{
  "code": 0,
  "message": "success",
  "data": { ... }
}
CampoTipoDescrição
recordsUsageDetailVO[]Registros da página atual.
totalLongContagem total.
currentLongPágina atual.
sizeLongTamanho da página.
pagesLongTotal de páginas.

Campos de UsageDetailVO:

CampoTipoDescrição
priceSnapshotIdStringID do snapshot de preço.
taskIdStringID da tarefa.
creditBigDecimalValor cobrado.
modelStringNome do modelo.
modelTypeStringtext / image / video.
inputTokensLongTokens de entrada; null para imagem/vídeo.
outputTokensLongTokens de saída.
totalTokensLongTotal de tokens.
cacheReadTokensLongTokens de leitura em cache.
cacheWriteTokensLongTokens de escrita em cache.
imageCountIntegerContagem de imagens; definido para modelos de imagem.
imageResolutionStringResolução da imagem, ex. 720P.
imageRatioStringProporção da imagem, ex. 1:1.
videoResolutionStringResolução do vídeo, ex. 1080p.
videoRatioStringProporção do vídeo, ex. 16:9.
videoDurationSecLongDuração do vídeo em segundos.
orderCreatedAtLocalDateTimeTempo de criação do pedido (snapshot orderCreatedAt).
creditDetailsCreditDetailItem[]Detalhes do pedido neste snapshot (de credit_order_t).

Campos de CreditDetailItem:

CampoTipoDescrição
creditBigDecimalValor cobrado por este pedido.
deductionSourceStringOrigem da dedução (Balance / Monthly Package / Resource Package).
packageNameStringNome do pacote; null se nenhum pacote.

Convenção de valor nulo: apenas os campos relevantes para cada modelType são preenchidos; o restante é null. text preenche os campos de token; image preenche imageCount/imageResolution/imageRatio; video preenche videoResolution/videoRatio/videoDurationSec.

Exemplo de resposta:

{
  "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

Lista paginada das transações de recarga pagas (status=2) do usuário atual, ordenadas por created_at em ordem decrescente.

ParâmetroTipoObrigatórioPadrãoDescrição
pageIntegerNão1Número da página.
sizeIntegerNão20Tamanho da página.
startTimeStringNão—Hora de início, yyyy-MM-dd HH:mm:ss, inclusivo.
endTimeStringNão—Hora de término, yyyy-MM-dd HH:mm:ss, inclusivo.
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"

Wrapper de resposta:

{
  "code": 0,
  "message": "success",
  "data": { ... }
}
CampoTipoDescrição
recordsTransactionVO[]Transações da página atual.
totalLongContagem total.
currentLongPágina atual.
sizeLongTamanho da página.
pagesLongTotal de páginas.

Campos de TransactionVO:

CampoTipoDescrição
orderNoStringNúmero do pedido.
thirdPartyOrderNoStringNúmero do pedido de terceiros.
amountBigDecimalValor do pedido.
actualAmountBigDecimalValor efetivamente pago.
discountBigDecimalValor do desconto.
paymentMethodStringMétodo de pagamento (wechat / alipay / ustd / stripe / wallyt etc.).

Campos de TransactionVO:

CampoTipoDescrição
serviceFeeAmountBigDecimalValor da taxa de serviço.
paymentChannelStringPlataforma de pagamento.
sourceStringOrigem do pedido (recharge / package_purchase etc.).
packageNameStringNome do pacote (definido para compras de pacotes; null para recargas simples).
createdAtLocalDateTimeTempo de criação.
{
  "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
  }
}