GPUs Preços IA por Token Templates Blog API Contato Login Começar Agora

Referência de API

Integre GPUs de topo de linha à sua aplicação. Simples, rápido e confiável.

A API em si não tem mensalidade: você paga pelo tempo de GPU por hora ou por token de inferência consumido, sempre em reais. Crie sua conta, gere uma API key e comece em segundos.

URL base

Todos os endpoints ficam sob a URL base abaixo e respondem em JSON:

https://gpubrasil.com.br

Autenticação

Autentique com uma API key no header Authorization. A key não expira e pode ser revogada a qualquer momento no painel. Trate-a como uma senha — quem tiver a key pode criar e apagar instâncias na sua conta.

Authorization: Bearer gpub_live_suachaveaqui

Alternativamente, você pode enviar a key no header x-api-key.

Gere sua API key no painel

Por segurança, as API keys são geradas e gerenciadas dentro da sua conta — na seção API Keys do painel. A chave em claro é exibida uma única vez, no ambiente autenticado.

Abrir painel → API Keys

Endpoints

Fluxo completo

O ciclo de vida de uma instância é: escolher a GPU → criar → consultar status/conexão → (opcional) parar/iniciar → deletar. Nos exemplos abaixo, defina sua chave em uma variável de ambiente:

export GPUB_API_KEY="gpub_live_suachaveaqui"

1. Listar GPUs e preços

Retorna o catálogo com preço por hora (em R$) e o gpu_key que você usa para criar a instância. Não exige autenticação.

curl -s "https://gpubrasil.com.br/api/gpus"

Resposta (resumo):
{
  "gpus": [
    { "model": "NVIDIA H100 PCIe 80GB", "gpu_key": "premium_H100-80G-PCIe",
      "pricePerHourBrl": 19.88, "provider": "premium" },
    { "model": "RTX 4090", "gpu_key": "economic_RTX_4090",
      "pricePerHourBrl": 3.34, "provider": "economic" }
  ]
}

Dica: use /api/gpus/available para ver também a quantidade disponível em tempo real.

Só as GPUs compatíveis com um template

Os templates de 1 clique não rodam em qualquer máquina: cada um exige um mínimo de memória de vídeo, e alguns só funcionam em instância de máquina virtual Dedicada. Passe ?template=<id> em /api/gpus e o catálogo volta já filtrado, pelas mesmas regras que o deploy aplica.

curl -s "https://gpubrasil.com.br/api/gpus?template=llama-factory"

Resposta (resumo):
{
  "template": { "id": "llama-factory", "name": "LLaMA-Factory",
                "minVramGb": 16, "diskGb": 80, "requiresVm": false },
  "gpus": [
    { "model": "RTX 4090", "gpu_key": "economic_RTX_4090", "gpuRamGb": 24,
      "pricePerHourBrl": 3.34, "templateMinGpuCount": 1 }
  ]
}

Os ids de template vêm de GET /api/templates. templateMinGpuCount é o mínimo de GPUs a pedir no deploy (campo gpuCount) para o template caber na máquina — 1 na maioria, mais de 1 nos modelos grandes, que somam a memória das placas. Id desconhecido devolve 400 com TEMPLATE_NOT_FOUND.

Filtrar o catálogo

O catálogo tem centenas de entradas. Em vez de baixar tudo e escolher no seu código, peça já filtrado:

# 4090 abaixo de R$ 4/h nas Américas, mais barata primeiro
curl -s "https://gpubrasil.com.br/api/gpus?model=4090&max_price_brl=4®ion=NA&sort=price&limit=5"

# qualquer placa com 80 GB de memória de vídeo ou mais, tier Dedicada
curl -s "https://gpubrasil.com.br/api/gpus?min_vram_gb=80&tier=dedicada&sort=price"
ParâmetroO que faz
tierdedicada, economica, spot (aceita lista separada por vírgula).
model / qPedaço do nome do modelo (4090, h100).
max_price_brl / min_price_brlTeto e piso de preço por hora, em reais.
min_vram_gbMemória de vídeo mínima por placa.
regionMacro-região: NA (América do Norte), EU, AP (Ásia-Pacífico) ou GL (qualquer região).
gpu_countQuantas placas você quer. Esconde os modelos que só saem em bloco maior que isso — evita descobrir a restrição só na hora do deploy.
availabletrue deixa só o que tem unidade livre agora.
supports_templates / video_encoderSó máquinas que rodam template de 1 clique / que têm codificador de vídeo (streaming e vídeo).
sort / order / limitOrdena por price, vram ou model; order=desc inverte; limit corta a lista.

Com filtro, a resposta ganha count (o que sobrou) e total (o tamanho do catálogo). Sem filtro, o formato é o de sempre. Preço e disponibilidade mudam ao longo do dia: consulte perto da hora de criar a máquina, não uma vez por semana.

2. Criar instância

Use o gpuModel = gpu_key obtido no passo 1. O deploy roda em segundo plano; a resposta volta na hora com um instanceId e status creating.

curl -X POST "https://gpubrasil.com.br/api/instances/deploy" \
  -H "Authorization: Bearer $GPUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "minha-vm",
    "gpuModel": "premium_H100-80G-PCIe",
    "vcpuCount": 8,
    "ramGb": 64,
    "storageGb": 100
  }'

Resposta:
{
  "success": true,
  "instance": { "instanceId": "abc123", "name": "minha-vm", "status": "creating" },
  "chargedAmount": 19.88,
  "currency": "R$",
  "message": "Instância sendo criada..."
}
CampoPadrãoO que é
gpuModel (obrigatório)O gpu_key que veio do catálogo.
namegeradoO nome pelo qual você vai operar a máquina depois. Até 64 caracteres. Veja Gerenciar pelo nome.
gpuCount1Quantas placas. Alguns modelos só saem em bloco — o catálogo diz o mínimo em minGpuCount.
vcpuCount · ramGb · storageGb4 · 16 · 100Mínimos aceitos: 2 vCPU, 8 GB de RAM, 40 GB de disco.
sshKey ou sshKeyNameA chave pública inteira, ou o nome de uma já cadastrada no painel. É o seu acesso à máquina.
templateIdTemplate de 1 clique. Use /api/gpus?template=<id> para ver onde ele roda.
diskIdDisco Persistente a anexar. Aceita o id ou name:<nome do disco>.
metadataSuas etiquetas (objeto JSON simples): id do job, lote, ambiente. Viram filtro na listagem.
requireUniqueNamefalsetrue recusa a criação (409 NAME_IN_USE, sem cobrar) se já existir máquina ativa com aquele nome.

A resposta devolve instanceId na hora — guarde-o, ou use o name que você escolheu. O valor em chargedAmount é a primeira hora, pré-paga: a cobrança seguinte só acontece se a máquina passar dessa hora. Se o provisionamento falhar, esse valor volta para o seu saldo automaticamente.

3. Status e dados de conexão SSH

Consulte pelo instanceId — ou pelo nome que você deu, com name:<nome>. Quando o status virar running, o objeto connection traz IP, porta e o comando SSH pronto (já com o usuário de login correto daquela máquina).

curl -s "https://gpubrasil.com.br/api/instances/abc123" \
  -H "Authorization: Bearer $GPUB_API_KEY"

Resposta:
{
  "id": "abc123",
  "name": "minha-vm",
  "status": "running",
  "gpu_model": "premium_H100-80G-PCIe",
  "connection": {
    "ip": "203.0.113.42",
    "port": 22,
    "user": "ubuntu",
    "sshCommand": "ssh -i ~/.ssh/your_key -p 22 ubuntu@203.0.113.42"
  },
  "resources": { "vcpuCount": 8, "ramGb": 64, "storageGb": 100 },
  "pricing": { "hourlyRateBrl": 19.88 }
}

4. Listar e filtrar suas instâncias

A listagem aceita filtros. Se você opera dezenas de máquinas ao mesmo tempo, peça só o que interessa em vez de baixar tudo e procurar no seu código.

# tudo
curl -s "https://gpubrasil.com.br/api/instances" -H "Authorization: Bearer $GPUB_API_KEY"

# só o que está rodando, do lote da noite, 10 por página, mais novas primeiro
curl -s "https://gpubrasil.com.br/api/instances?status=running&metadata.lote=noite&limit=10" \
  -H "Authorization: Bearer $GPUB_API_KEY"

Resposta:
{
  "data": [
    {
      "instance_id": "abc123",
      "name": "treino-noturno",
      "status": "running",
      "tier": "economica",
      "gpu_model": "RTX_4090@NA",
      "gpu_model_label": "RTX 4090 · América do Norte",
      "gpu_count": 1,
      "ip_address": "203.0.113.42",
      "ssh_port": 22,
      "ssh_user": "root",
      "ssh_key_name": "deploy-ci",
      "supports_pause": false,
      "disk_persists_on_stop": false,
      "price_per_hour_brl": 3.34,
      "daily_stopped_brl": 3.94,
      "metadata": { "job": "7f21", "lote": "noite" },
      "template_id": null, "template_port": null, "template_url": null,
      "error_message": null, "deleting": false,
      "created_at": "2026-09-18 22:14:02"
    }
  ],
  "count": 1,
  "total": 1,
  "limit": 10,
  "offset": 0
}

Filtros aceitos

ParâmetroExemploO que faz
name?name=treino-noturnoNome exato (maiúsculas não importam).
q / name_contains?q=treinoPedaço do nome. Ótimo para prefixos de esteira (?q=job-2026-09).
status?status=running,stoppedUm ou vários status, separados por vírgula.
tier?tier=dedicada,spotdedicada, economica, spot ou cpu.
gpu?gpu=4090Pedaço do modelo da GPU.
template?template=llama-factorySó as máquinas criadas com aquele template de 1 clique.
has_ip?has_ip=trueSó as que já têm endereço para conectar.
metadata.<chave>?metadata.job=7f21Filtra pelas etiquetas que você mesmo gravou.
created_after / created_before?created_after=2026-09-01Janela de criação (data ISO, em UTC).
sort / order?sort=name&order=ascOrdena por created_at (padrão), name, price ou status.
limit / offset?limit=20&offset=40Paginação. limit vai até 500; sem ele, vem tudo.

Filtros combinam entre si. count é o que veio nesta página e total é quanto o filtro encontrou no total — é a diferença entre os dois que diz se falta paginar. O campo localInstances continua na resposta com o mesmo conteúdo de data, por compatibilidade com integrações antigas; em código novo use data.

Campos que valem atenção

CampoPor que existe
ssh_userO usuário de login daquela máquina, reportado no provisionamento. Não deduza do tipo da máquina: máquinas do mesmo tier podem ter logins diferentes.
ssh_key_nameQual das suas chaves cadastradas foi instalada — é a privada correspondente que vai no -i do ssh.
supports_pauseSe false, esta máquina não hiberna: /stop devolve 409. Para encerrar, use DELETE.
disk_persists_on_stopSe false, hibernar descarta o disco e a máquina volta zerada. Confira antes de parar.
template_urlEndereço real da interface do template, com a porta que de fato responde (em máquina de contêiner ela é sorteada e não é a do catálogo). null enquanto não houver endereço de verdade.
deleting / status: "deleting"Cancelamento já aceito, aguardando o provisionador liberar a destruição. A cobrança por hora já parou — não repita o DELETE.
error_messageMotivo da falha em linguagem de gente, quando status é error.

5. Gerenciar pelo nome, sem listar tudo

Toda rota que aceita :instanceId aceita também name:<nome>. Você batiza a máquina na criação e passa a operá-la por esse nome — sem guardar o id que geramos e sem baixar a lista inteira para procurar a cada comando.

# cria com um nome seu
curl -X POST "https://gpubrasil.com.br/api/instances/deploy" \
  -H "Authorization: Bearer $GPUB_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "treino-noturno", "gpuModel": "economic_RTX_4090", "requireUniqueName": true }'

# e daí em diante opera pelo nome, em qualquer rota
H="Authorization: Bearer $GPUB_API_KEY"
curl -s          "https://gpubrasil.com.br/api/instances/name:treino-noturno"      -H "$H"
curl -s -X POST   "https://gpubrasil.com.br/api/instances/name:treino-noturno/stop" -H "$H"
curl -s -X DELETE "https://gpubrasil.com.br/api/instances/name:treino-noturno"      -H "$H"

Regras que valem a pena conhecer antes de automatizar:

RegraPor quê
O id sempre ganha do nome. Sem o prefixo name:, primeiro procuramos um id; só se não existir é que tratamos o texto como nome.Integração que já usa id não muda de comportamento, nem que alguém batize uma máquina com o id de outra.
O nome endereça só máquinas ativas.Nome se repete ao longo do tempo (uma esteira recria worker todo dia). Casar com uma máquina já destruída mandaria comandos para o vazio.
Maiúsculas não importam: name:Treino-01 e name:treino-01 resolvem igual.Quem digita o nome não deveria ter que lembrar a capitalização.
Dois nomes iguais ativos = 409 AMBIGUOUS_NAME, com a lista dos candidatos. Nunca escolhemos por você."Apaga a treino-01" acertando a máquina errada é dano que não volta atrás.
HTTP 409 — duas máquinas ativas com o mesmo nome
{
  "code": "AMBIGUOUS_NAME",
  "error": "Existem 2 instâncias ativas com o nome \"treino-noturno\". Use o id da instância (veja \"matches\") ou renomeie uma delas.",
  "matches": [
    { "id": "abc123", "name": "treino-noturno", "status": "running", "gpu_model_label": "RTX 4090", "created_at": "2026-09-18 22:14:02" },
    { "id": "def456", "name": "treino-noturno", "status": "creating", "gpu_model_label": "RTX 4090", "created_at": "2026-09-19 03:40:11" }
  ]
}

Para garantir que o nome continue sendo um endereço único, mande "requireUniqueName": true no deploy: se já existir máquina ativa com aquele nome, a criação é recusada com 409 NAME_IN_USEsem cobrar nada — e a resposta diz qual máquina já ocupa o nome. Sem esse campo, o comportamento antigo continua valendo (nomes repetidos são aceitos).

6. Renomear e etiquetar

PATCH /api/instances/:ref corrige o nome e grava etiquetas suas (metadata) na máquina — id do job, lote, ambiente, o que a sua esteira precisar reencontrar depois.

curl -X PATCH "https://gpubrasil.com.br/api/instances/name:treino-noturno" \
  -H "Authorization: Bearer $GPUB_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "treino-noturno-v2", "metadata": { "job": "7f21", "lote": "noite", "tentativa": 2 } }'

Resposta:
{ "success": true, "instance": { "id": "abc123", "name": "treino-noturno-v2",
  "metadata": { "job": "7f21", "lote": "noite", "tentativa": 2 }, "status": "running", ... } }
CampoRegras
nameAté 64 caracteres: letras, números, espaço, ponto, @, hífen e sublinhado. Não pode começar com name: nem creating- (são prefixos de endereçamento). Nome já usado por outra máquina ativa sua devolve 409 NAME_IN_USE.
metadataObjeto JSON simples: até 20 chaves, valores de texto, número ou booleano (sem objetos aninhados), 2 KB no total. Mandar null apaga as etiquetas.

Renomear só é permitido depois que a máquina existe de verdade (status running/stopped). Durante a criação, a resposta é 409 RENAME_TOO_EARLY: enquanto o provisionamento está em curso, o nome ainda é a chave que usamos internamente para reconciliar a máquina — trocá-lo ali abriria uma janela para a máquina ficar fora do nosso controle de cobrança. Espere o running, que leva de segundos a poucos minutos.

As etiquetas voltam prontas (como objeto, não como texto) em GET /api/instances e viram filtro: ?metadata.lote=noite.

7. Quanto esta máquina já custou

GET /api/instances/:ref/usage devolve a conta fechada daquela instância — incluindo a primeira hora pré-paga, que não aparece em nenhum contador de uso por hora.

curl -s "https://gpubrasil.com.br/api/instances/name:treino-noturno/usage" \
  -H "Authorization: Bearer $GPUB_API_KEY"

Resposta:
{
  "id": "abc123",
  "name": "treino-noturno",
  "status": "running",
  "currency": "BRL",
  "hourly_rate_brl": 3.34,
  "daily_stopped_brl": 3.94,
  "total_charged_brl": 13.36,
  "breakdown": { "hourly_usage_brl": 10.02, "reservations_net_brl": 3.34 },
  "created_at": "2026-09-18 22:14:02",
  "last_charged_at": "2026-09-19 01:14:02",
  "age_hours": 4.12
}
CampoO que é
total_charged_brlTudo que já saiu do seu saldo por esta máquina, com estornos já descontados. É este o número para relatório de custo.
breakdown.hourly_usage_brlAs horas inteiras já fechadas e cobradas.
breakdown.reservations_net_brlA hora pré-paga na criação (e a de cada religada), menos estornos.
daily_stopped_brlQuanto custa um dia com a máquina hibernada. Não é 1× o preço/hora: a GPU é liberada, mas o disco continua reservado. É o número que decide entre hibernar e destruir.
last_charged_atÚltima hora fechada. A próxima cobrança acontece uma hora depois desta marca.

8. Parar e iniciar (opcional)

Parar interrompe a cobrança por hora, mas não zera o custo: enquanto parada, a instância passa a ser cobrada em uma diária equivalente a 1 hora de uso. Atenção: nem toda máquina preserva o disco ao parar — em parte das máquinas do tipo Dedicada o disco é descartado e a instância volta zerada no start, perdendo tudo que foi instalado nela. Confira o campo disk_persists_on_stop em GET /api/instances antes de parar. Para encerrar a cobrança por completo, use DELETE.

Nem toda máquina hiberna: onde a placa é devolvida ao parar, não haveria como garantir a mesma máquina de volta. O campo supports_pause diz quais aceitam — nas outras, /stop devolve 409 em vez de destruir a máquina sem você pedir.

curl -X POST "https://gpubrasil.com.br/api/instances/name:minha-vm/stop" \
  -H "Authorization: Bearer $GPUB_API_KEY"

curl -X POST "https://gpubrasil.com.br/api/instances/name:minha-vm/start" \
  -H "Authorization: Bearer $GPUB_API_KEY"

9. Deletar instância

Destrói a instância no provedor e encerra a cobrança. É o endpoint que você perguntou — ele existe e é definitivo.

curl -X DELETE "https://gpubrasil.com.br/api/instances/name:minha-vm" \
  -H "Authorization: Bearer $GPUB_API_KEY"

Resposta:
{ "success": true, "message": "Instância removida" }

Se a máquina ainda estiver sendo criada, a destruição pode ser recusada (HTTP 409) — tente de novo em alguns minutos. Quando a resposta vier como 202 (ou a instância aparecer com status: "deleting"), o pedido já foi aceito e a cobrança por hora já parou: não repita o DELETE, a destruição termina sozinha.

Outros recursos da conta

Tudo abaixo usa a mesma API key e o mesmo saldo em reais. E, como nas instâncias, disco e chave SSH também podem ser endereçados pelo nome que você deu.

Máquinas de CPU (sem GPU)

Para pré-processamento, fila, scraping e serviços que não precisam de placa de vídeo. São máquinas dedicadas de verdade, com opção em São Paulo.

# catálogo (público)
curl -s "https://gpubrasil.com.br/api/cpus"

# criar
curl -X POST "https://gpubrasil.com.br/api/cpus/deploy" \
  -H "Authorization: Bearer $GPUB_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "fila-etl", "cpu_key": "cpu_c3-small-x86@SAO2", "sshKeyName": "deploy-ci",
        "metadata": { "lote": "etl" }, "requireUniqueName": true }'

Depois de criada, a máquina de CPU aparece e é operada pelas mesmas rotas de instância (listar, status, renomear, deletar), com tier: "cpu". O provisionamento é de máquina física e leva alguns minutos — mais que uma GPU. Chave SSH é obrigatória (é o único acesso).

Disco Persistente

Armazenamento que sobrevive à destruição da máquina. Você anexa um disco na criação e o conteúdo de /workspace/disco é sincronizado; na próxima máquina, anexe o mesmo disco e os dados voltam.

# criar um disco de 100 GB
curl -X POST "https://gpubrasil.com.br/api/disks" \
  -H "Authorization: Bearer $GPUB_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "dados-treino", "sizeGb": 100 }'

# listar / achar pelo nome
curl -s "https://gpubrasil.com.br/api/disks?name=dados-treino" -H "Authorization: Bearer $GPUB_API_KEY"

# anexar na criação da máquina — pelo NOME do disco
curl -X POST "https://gpubrasil.com.br/api/instances/deploy" \
  -H "Authorization: Bearer $GPUB_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "treino-01", "gpuModel": "economic_RTX_4090", "diskId": "name:dados-treino" }'

# renomear / apagar (apagar destrói os dados)
curl -X PATCH  "https://gpubrasil.com.br/api/disks/name:dados-treino" -H "Authorization: Bearer $GPUB_API_KEY" \
  -H "Content-Type: application/json" -d '{ "name": "dados-treino-2026" }'
curl -X DELETE "https://gpubrasil.com.br/api/disks/name:dados-treino-2026" -H "Authorization: Bearer $GPUB_API_KEY"

O disco é cobrado pelo tamanho contratado (não pelo usado) enquanto existir, mesmo sem máquina anexada. Dois discos com o mesmo nome fazem qualquer comando por nome devolver 409 AMBIGUOUS_NAME em vez de escolher um — apagar o disco errado é perda de dados que não volta.

Chaves SSH

Cadastre a chave uma vez e depois só cite o nome dela no deploy, com sshKeyName — sem carregar o material da chave dentro da sua esteira.

curl -X POST "https://gpubrasil.com.br/api/ssh-keys" \
  -H "Authorization: Bearer $GPUB_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "deploy-ci", "publicKey": "ssh-ed25519 AAAAC3Nza... ci@empresa" }'

curl -s "https://gpubrasil.com.br/api/ssh-keys" -H "Authorization: Bearer $GPUB_API_KEY"

# apagar pelo nome
curl -X DELETE "https://gpubrasil.com.br/api/ssh-keys/name:deploy-ci" -H "Authorization: Bearer $GPUB_API_KEY"

No deploy, mande sshKey (a chave pública inteira) ou sshKeyName (o nome de uma já cadastrada). Nome que não existe devolve 400 SSH_KEY_NOT_FOUND e não cria máquina nenhuma — máquina paga e inacessível é o pior desfecho possível. O campo ssh_key_name da instância diz qual chave acabou instalada.

Saldo e extrato

curl -s "https://gpubrasil.com.br/api/user/balance" -H "Authorization: Bearer $GPUB_API_KEY"
{ "balance_brl": 482.15, "user": { "id": 42, "name": "...", "email": "..." } }

# extrato, com recorte
curl -s "https://gpubrasil.com.br/api/transactions?type=reservation,usage&limit=100" \
  -H "Authorization: Bearer $GPUB_API_KEY"
curl -s "https://gpubrasil.com.br/api/transactions?q=treino-noturno" -H "Authorization: Bearer $GPUB_API_KEY"
ParâmetroO que faz
typedeposit, reservation, usage, refund — aceita lista separada por vírgula.
qBusca no texto do lançamento (é nele que o nome da máquina aparece).
created_after / created_beforeJanela de datas, para fechar o mês.
limit / offsetPaginação (padrão 50, máximo 500).

Convenção do extrato: cobrança é valor negativo e crédito é positivo. As datas vêm em UTC. A resposta é um array puro.

Receitas prontas

Subir, esperar ficar pronta e conectar

O deploy responde na hora com status: "creating"; a máquina fica utilizável alguns instantes depois. Consulte pelo nome até o status virar running com IP:

NOME="treino-$(date +%s)"

curl -sX POST "https://gpubrasil.com.br/api/instances/deploy" \
  -H "Authorization: Bearer $GPUB_API_KEY" -H "Content-Type: application/json" \
  -d "{\"name\":\"$NOME\",\"gpuModel\":\"economic_RTX_4090\",\"sshKeyName\":\"deploy-ci\",\"requireUniqueName\":true}"

# espera até 15 min, perguntando a cada 10 s
for i in $(seq 1 90); do
  J=$(curl -s "https://gpubrasil.com.br/api/instances/name:$NOME" -H "Authorization: Bearer $GPUB_API_KEY")
  S=$(echo "$J" | python3 -c "import json,sys; print(json.load(sys.stdin)['status'])")
  [ "$S" = "running" ] && echo "$J" | python3 -c "import json,sys; print(json.load(sys.stdin)['connection']['sshCommand'])" && break
  [ "$S" = "error" ]   && echo "$J" | python3 -c "import json,sys; print('falhou:', json.load(sys.stdin)['error_message'])" && break
  sleep 10
done

Intervalo de 5 a 10 segundos entre consultas é suficiente e educado. O campo connection.sshCommand já vem montado com o usuário certo daquela máquina; troque só o caminho da sua chave privada no -i.

Derrubar um lote inteiro pelo prefixo do nome

Use o filtro por pedaço do nome (ou por etiqueta) e destrua cada uma pelo id — que a própria listagem já devolveu.

# tudo que começa com "job-2026-09-19"
curl -s "https://gpubrasil.com.br/api/instances?q=job-2026-09-19&status=running" \
  -H "Authorization: Bearer $GPUB_API_KEY" \
| python3 -c "import json,sys; [print(i['instance_id']) for i in json.load(sys.stdin)['data']]" \
| while read ID; do
    curl -sX DELETE "https://gpubrasil.com.br/api/instances/$ID" -H "Authorization: Bearer $GPUB_API_KEY"
  done

O mesmo vale por etiqueta: ?metadata.lote=noite. Prefira apagar pelo id devolvido na listagem quando for lote: é imune a nome repetido.

Quanto gastei hoje, por máquina

for NOME in $(curl -s "https://gpubrasil.com.br/api/instances?status=running" \
  -H "Authorization: Bearer $GPUB_API_KEY" \
  | python3 -c "import json,sys; [print(i['name']) for i in json.load(sys.stdin)['data']]"); do
    curl -s "https://gpubrasil.com.br/api/instances/name:$NOME/usage" -H "Authorization: Bearer $GPUB_API_KEY" \
    | python3 -c "import json,sys; d=json.load(sys.stdin); print(f\"{d['name']}: R\$ {d['total_charged_brl']:.2f} ({d['age_hours']:.1f}h)\")"
done

Vocabulário de status

statusO que significaEstá cobrando?
creatingProvisionamento em curso. Ainda sem IP.Sim — a primeira hora é pré-paga na criação.
runningNo ar e utilizável. connection traz IP, porta e usuário.Sim, por hora.
stoppedHibernada (só onde supports_pause é true).Sim, uma diária de armazenamento — veja daily_stopped_brl.
deletingCancelamento aceito, aguardando a destruição terminar.Não. Não repita o DELETE.
errorFalhou. O motivo está em error_message.Não — o que foi reservado é estornado automaticamente.
stopped_no_balanceDesligada por saldo insuficiente.Como parada. Recarregue e use /start.

Limites de uso

LimiteValorQuando você encosta nele
Criação de máquinas40 por 10 minutos, por conta429, com Retry-After em segundos. É proteção contra laço em retry, não teto comercial: precisa de mais, é só pedir.
Consultas de leiturasem limite fixoPedimos bom senso: intervalo de 5 a 10 s ao aguardar uma máquina subir, e filtro em vez de listar tudo em laço.
Chaves SSH cadastradas10 por conta400 ao cadastrar a 11ª.
Tamanho do disco10 GB a 10 TB400 fora da faixa.

Inferência por token (compatível com OpenAI)

Nem todo projeto precisa de uma GPU inteira ligada. Você também pode chamar modelos de pesos abertos pagando por token consumido, usando a mesma API key gpub_live_ e o mesmo saldo em reais que paga as GPUs. Não existe assinatura, não existe mensalidade e não existe mínimo de compra de tokens: a cobrança é proporcional aos tokens de entrada e de saída de cada chamada, e o valor debitado volta na própria resposta.

A API é compatível com a da OpenAI. Qualquer SDK, framework ou ferramenta que já converse com ela funciona aqui trocando apenas a URL base e a chave:

https://gpubrasil.com.br/v1

Não usamos seus prompts nem suas respostas para treinar modelos. Se o seu caso exige controle total sobre os pesos, os logs e o ciclo de vida do que trafega, rode o modelo em uma GPU dedicada só sua, sem API de terceiro no caminho.

1. Listar modelos e preços

Devolve os modelos disponíveis, o tamanho da janela de contexto e o preço por milhão de tokens, em reais. Cada modelo tem um id, que é o que vai no campo model, um name comercial para exibição e uma prateleira.

São três prateleiras: fronteira (modelos de marca — Claude, GPT e Gemini), confidenciais (rodam dentro de um enclave lacrado por hardware) e essenciais (pesos abertos, melhor preço por token). Todos respondem pelo mesmo formato da OpenAI, inclusive os que na origem exigiriam outro formato de requisição.

curl -s "https://gpubrasil.com.br/v1/models" \
  -H "Authorization: Bearer $GPUB_API_KEY"

Resposta (resumo):
{
  "object": "list",
  "data": [
    { "id": "gpub-fast", "object": "model", "owned_by": "gpubrasil",
      "name": "DeepSeek V4 Flash", "prateleira": "essenciais", "context_length": 1000000,
      "pricing": { "currency": "BRL", "inputPerMillion": 0.59, "cachedInputPerMillion": 0.118, "outputPerMillion": 1.29 } },
    { "id": "claude-sonnet-5", "object": "model", "owned_by": "gpubrasil",
      "name": "Claude Sonnet 5", "prateleira": "fronteira", "context_length": 1000000,
      "pricing": { "currency": "BRL", "inputPerMillion": 20.40, "cachedInputPerMillion": 2.04, "outputPerMillion": 102.00 } },
    { "id": "gpub-selado-nemotron", "object": "model", "owned_by": "gpubrasil",
      "name": "Nemotron 3 Nano", "prateleira": "confidenciais", "context_length": 128000,
      "pricing": { "currency": "BRL", "inputPerMillion": 0.15, "cachedInputPerMillion": 0.015, "outputPerMillion": 0.60 } }
  ]
}

O exemplo acima está resumido a três linhas, uma de cada prateleira; a chamada devolve o catálogo inteiro. A lista completa, sempre atual, está em Modelos de IA. Precisa da tabela sem autenticar (para uma página de preços, por exemplo)? Use GET /api/inference/models, que é público.

2. Chat completion

Formato idêntico ao da OpenAI. A única diferença é o campo extra usage.cost_brl: o custo em reais daquela chamada, já calculado pelo servidor e já debitado do seu saldo, para você não precisar refazer a conta no cliente.

curl -X POST "https://gpubrasil.com.br/v1/chat/completions" \
  -H "Authorization: Bearer $GPUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpub-plus",
    "messages": [
      { "role": "system", "content": "Você responde em português do Brasil." },
      { "role": "user", "content": "Explique o que é uma GPU em duas frases." }
    ],
    "max_tokens": 300
  }'

Resposta:
{
  "id": "chatcmpl-8f2c1b",
  "object": "chat.completion",
  "created": 1754870400,
  "model": "gpub-plus",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "Uma GPU é um processador..." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 500,
    "completion_tokens": 300,
    "total_tokens": 800,
    "cost_brl": 0.0085,
    "balance_brl_after": 92.15
  }
}

O campo model da resposta devolve o que você pediu (aqui, gpub-plus), não o identificador técnico — se você chamar por apelido, é o apelido que volta. cost_brl (custo da chamada) e balance_brl_after (saldo depois dela) são acréscimos nossos dentro do objeto usage; o cabeçalho X-Request-Id identifica a requisição no nosso lado, guarde-o se precisar abrir um chamado. SDKs oficiais ignoram campos que não conhecem, então esses extras não quebram nenhuma integração existente. Para continuação de texto puro (sem papéis de conversa), o endpoint é POST /v1/completions, com prompt no lugar de messages.

Imagem e áudio na entrada. Use o bloco image_url padrão da OpenAI dentro de content — funciona em todos os modelos que aceitam imagem, em qualquer prateleira, sem mudar nada no seu código. Para áudio, o bloco é input_audio. Se o modelo escolhido não aceitar o que você mandou, a resposta é 400 com o motivo; nunca aceitamos a chamada ignorando a parte que o modelo não entende. Em alguns modelos a imagem precisa ir embutida (data:<mime>;base64,…) em vez de URL, e o erro diz quando é o caso.

curl -X POST "https://gpubrasil.com.br/v1/chat/completions" \
  -H "Authorization: Bearer $GPUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "max_tokens": 300,
    "messages": [{
      "role": "user",
      "content": [
        { "type": "text", "text": "O que há de errado neste diagrama?" },
        { "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KG..." } }
      ]
    }]
  }'

3. Streaming

Mande "stream": true para receber a resposta em pedaços, no padrão server-sent events. Para receber também o consumo e o custo, peça stream_options: {"include_usage": true}: o usage chega em um frame próprio, logo antes do [DONE].

curl -N -X POST "https://gpubrasil.com.br/v1/chat/completions" \
  -H "Authorization: Bearer $GPUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpub-fast",
    "messages": [{ "role": "user", "content": "Conte até três." }],
    "stream": true,
    "stream_options": { "include_usage": true }
  }'

Resposta (text/event-stream):
data: {"id":"chatcmpl-3a91","object":"chat.completion.chunk","model":"gpub-fast","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-3a91","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Um"},"finish_reason":null}]}

data: {"id":"chatcmpl-3a91","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":", dois"},"finish_reason":null}]}

data: {"id":"chatcmpl-3a91","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":", três."},"finish_reason":null}]}

data: {"id":"chatcmpl-3a91","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-3a91","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":9,"total_tokens":23,"cost_brl":0.0000155,"balance_brl_after":92.15}}

data: [DONE]

Sem stream_options, o frame de usage não é enviado e você fica sem o cost_brl daquela chamada. O consumo continua registrado no servidor e aparece em /api/inference/usage. Encerre a leitura ao ver data: [DONE], que não é JSON.

4. SDK oficial da OpenAI

Não é preciso trocar de biblioteca. Aponte o SDK oficial para a nossa URL base e use a sua API key; o resto do código continua igual.

Python (pip install openai)

from openai import OpenAI

client = OpenAI(
    base_url="https://gpubrasil.com.br/v1",
    api_key="gpub_live_suachaveaqui",
)

r = client.chat.completions.create(
    model="gpub-mini",
    messages=[{"role": "user", "content": "Olá!"}],
)

print(r.choices[0].message.content)
# cost_brl é um campo extra nosso; no SDK Python ele chega em model_extra
print(r.usage.model_extra["cost_brl"])
Node.js (npm i openai)

import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://gpubrasil.com.br/v1',
  apiKey: process.env.GPUB_API_KEY,
});

const r = await client.chat.completions.create({
  model: 'gpub-mini',
  messages: [{ role: 'user', content: 'Olá!' }],
});

console.log(r.choices[0].message.content);
console.log(r.usage.cost_brl); // custo em reais desta chamada

Vale o mesmo para ferramentas que aceitam um endpoint compatível com a OpenAI: preencha a URL base com https://gpubrasil.com.br/v1 e a chave com a sua gpub_live_.

5. Geração de imagem

Modelos que geram imagem têm superfície própria: POST /v1/images/generations, no mesmo formato da OpenAI, com a mesma chave e o mesmo saldo. Eles não atendem em /v1/chat/completions — pedir um ali devolve 400 apontando para cá, e vice-versa. O campo endpoint de cada modelo em GET /v1/models diz qual é a superfície dele.

curl -X POST "https://gpubrasil.com.br/v1/images/generations" \
  -H "Authorization: Bearer $GPUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpub-imagem",
    "prompt": "uma praça arborizada ao amanhecer, estilo aquarela",
    "size": "1024x1024"
  }'

Resposta:
{
  "created": 1789807470,
  "data": [ { "b64_json": "/9j/4AAQSkZJRgABAQ..." } ]
}

A imagem volta em b64_json (base64), pronta para gravar em arquivo. n precisa ser 1: estes modelos geram uma imagem por chamada, e pedir mais devolve 400 antes de reservar qualquer valor do seu saldo. size aceita uma lista fechada de tamanhos — mandar um fora dela devolve 400 com a lista inteira no erro. Alguns modelos não aceitam size; nesses, omita o campo.

Cobrança. Depende do modelo, e o preço de cada um está em GET /v1/models: uns cobram por imagem (campo perImage, em reais) e outros cobram por token de imagem (campo imageOutputPerMillion). A contagem sai sempre do que foi entregue, nunca do que foi pedido — resposta sem imagem não gera cobrança.

Limites, contexto e saldo

Janela de contexto. Entrada e saída somadas precisam caber no context_length do modelo escolhido, que hoje vai de 128.000 a 1.000.000 de tokens conforme o modelo. Passar disso devolve 400, sem cobrança. O valor de cada modelo está em GET /v1/models — consulte de lá em vez de fixar no código, porque modelo novo entra e janela muda.

Tamanho da resposta. max_tokens limita quantos tokens o modelo pode gerar. Sem ele, o modelo decide onde parar dentro da janela, e como a saída custa mais que a entrada em todos os modelos, vale definir um teto em produção.

Saldo. Antes de encaminhar a chamada, o servidor estima o custo. Se o seu saldo não cobrir essa estimativa, a requisição é recusada com 402 e nada é gasto. Recarregue por Pix no painel e tente de novo. A inferência pela API é liberada depois do primeiro depósito confirmado: antes disso a chamada volta com 403 e code: "deposit_required" — para experimentar sem depositar, use o playground do painel. Um excesso de chamadas em pouco tempo devolve 429; uma indisponibilidade momentânea devolve 503.

HTTP 402
{
  "error": {
    "message": "Saldo insuficiente para cobrir o custo estimado desta chamada.",
    "type": "insufficient_quota",
    "param": null,
    "code": "insufficient_balance"
  }
}

Os erros seguem o envelope da OpenAI (error.message, error.type, error.param, error.code), então bibliotecas existentes já sabem lê-los. Repare que type e code não são iguais: saldo curto vem como type: "insufficient_quota" e code: "insufficient_balance". Trate a decisão pelo status HTTP. Para acompanhar gasto e volume, use GET /api/inference/usage?days=30, que devolve o consumo por dia e por modelo.

Códigos de Resposta

200 · OK

Requisição bem-sucedida

201 · Criado

Instância/recurso criado

400 · Erro

Parâmetros inválidos ou saldo insuficiente

401 · Não autorizado

API key ausente, inválida ou revogada

403 · Proibido

Ação não permitida para esta credencial

402 · Saldo insuficiente

O saldo não cobre o custo estimado da chamada

429 · Limite de requisições

Muitas requisições em pouco tempo

404 · Não encontrado

Instância/recurso inexistente

409 · Conflito

Instância ainda sendo criada — tente depois

500 · Erro interno

Erro no servidor

Campo code nos erros

Além do status HTTP, os erros das rotas de instância trazem um code estável — trate por ele, não pelo texto da mensagem (que muda de idioma e de redação).

codeHTTPO que fazer
INSTANCE_NOT_FOUND404O id/nome não existe entre as suas máquinas ativas. Nome só endereça máquina viva.
AMBIGUOUS_NAME409Duas ou mais máquinas ativas com o mesmo nome. Use um dos ids que vêm em matches, ou renomeie.
NAME_IN_USE409O nome já é de outra máquina ativa sua. Nada foi criado nem cobrado.
RENAME_TOO_EARLY409Espere a máquina sair de creating para renomear.
INVALID_NAME · INVALID_METADATA400Nome ou etiquetas fora das regras. Nada foi criado nem cobrado.
SSH_KEY_NOT_FOUND400sshKeyName não bate com nenhuma chave sua. Veja GET /api/ssh-keys.
DELETE_PENDING409A máquina já está sendo destruída; parar/iniciar não se aplica.
PRICE_CHANGED409O preço mudou entre a consulta e o deploy. Releia o catálogo e tente de novo — nunca cobramos acima do que você viu.
TEMPLATE_NOT_FOUND · TEMPLATE_REQUIRES_GPU400Template inexistente ou incompatível com a máquina escolhida. Use /api/gpus?template=<id> para listar só o que roda.
INSUFFICIENT_BALANCE402Saldo não cobre a primeira hora. A resposta diz quanto falta.
DISK_NOT_FOUND · DISK_UNSUPPORTED_MACHINE404 / 400Disco inexistente, ou tipo de máquina que ainda não aceita Disco Persistente.

Regra geral: toda recusa acontece antes de qualquer cobrança. Se a resposta foi 4xx, nenhum valor saiu do seu saldo. E se uma máquina falha depois de criada, a reserva é estornada automaticamente — você não precisa pedir.