Voltar para o blog
BunIACustos

Limite de gastos: o que o seu backend Bun precisa ter

05 de outubro de 2026·7 min de leitura·Diego Horvatti

Você sobe um script Bun numa sexta à noite. Ele usa um agente que chama uma API de LLM em loop. Você vai dormir. No sábado de manhã, o e-mail da fatura chega antes do café. Esse é o pesadelo que o Simon Willison resolveu encarar de frente: no texto We're going to need default hard budget caps on pretty much everything, ele defende que limite de gastos rígido deveria vir ligado por padrão em quase todo serviço pago. Neste artigo eu explico a ideia, por que ela pesa mais agora e como aplicar isso hoje no seu código Bun, sem esperar ninguém.

O que o Simon Willison está defendendo

O argumento é simples. Hoje, a maioria dos serviços cobra por uso e não tem teto. Você define um alerta, recebe um e-mail quando passa de certo valor e o serviço continua rodando. E cobrando.

A proposta é inverter o padrão. Toda conta nova nasceria com um teto rígido. Bateu o teto, o serviço para. Quem quiser gastar mais aumenta o limite de forma consciente.

Isso sempre fez sentido para nuvem. Só que antes o risco era um bucket público ou uma função mal configurada. Agora tem um elemento novo na sala: código que decide sozinho quantas vezes vai chamar uma API paga.

Por que alerta de gastos não é limite de gastos

Muita gente acha que já está protegida porque configurou um alerta. Não está.

Alerta é notificação. Chega no e-mail, vai para o spam, ou chega às 3h da manhã enquanto você dorme. O loop não dorme. Ele continua.

Alerta te avisa que o dinheiro foi embora. Limite impede que ele vá.

Faz a conta com um agente de código. Digamos que cada chamada mande 200 mil tokens de contexto, a um preço de US$ 3 por milhão de tokens de entrada. Isso dá US$ 0,60 por chamada, sem contar a saída. Se o agente entrar num ciclo de "tentar corrigir o teste, falhar, tentar de novo" e rodar 1.000 vezes durante a noite, são US$ 600. Em reais, é um mês de servidor de muita gente jogado fora por causa de um teste flaky.

E 1.000 iterações nem é muito. Um agente rápido faz isso em poucas horas.

Eu sinto o lado bom do teto na pele. Minha conta da Vercel é free e tem limite de 100 deploys por dia. Já me irritou? Já. Mas nunca recebi uma fatura surpresa. O limite rígido é chato exatamente no dia em que ele te salva.

Onde o Bun entra nessa história

O Bun deixou muito fácil fazer coisas que custam dinheiro. Isso é elogio, mas tem um preço.

  • bun run agente.ts roda TypeScript direto, sem build. Um script de automação nasce em dois minutos.
  • Bun.serve sobe um endpoint HTTP em cinco linhas. Expor uma rota que chama LLM ficou trivial.
  • O runtime é rápido. Um loop que chama fetch termina mais cedo, ou seja, gasta mais rápido também.

Nenhum desses pontos é problema do Bun. O problema é que a fricção sumiu. Antigamente, montar um serviço que chamasse uma API paga exigia configuração suficiente para você parar e pensar no custo. Hoje você não para. Você roda.

Então, enquanto os provedores não adotam o padrão que o Simon propõe, a responsabilidade fica com quem escreve o código. A boa notícia: o Bun já traz tudo o que você precisa para isso, sem instalar nada.

Como colocar um limite de gastos no seu código Bun

A ideia é ter um contador persistente que roda antes de cada chamada paga. Se o gasto estimado passar do teto do dia, o código lança um erro e para. Uso o bun:sqlite, que vem embutido no runtime:

// budget.ts
import { Database } from "bun:sqlite";

const db = new Database("budget.sqlite");
db.run(`CREATE TABLE IF NOT EXISTS gasto (
  dia TEXT PRIMARY KEY,
  centavos INTEGER NOT NULL
)`);

const LIMITE_DIA = Number(process.env.LIMITE_CENTAVOS_DIA ?? 500); // US$ 5

export function reservar(centavos: number) {
  const dia = new Date().toISOString().slice(0, 10);
  const { total } = db
    .query(`INSERT INTO gasto (dia, centavos) VALUES (?1, ?2)
            ON CONFLICT(dia) DO UPDATE SET centavos = centavos + ?2
            RETURNING centavos AS total`)
    .get(dia, centavos) as { total: number };

  if (total > LIMITE_DIA) {
    throw new Error(`Limite de gastos do dia estourado: ${total} centavos`);
  }
}

Repare num detalhe: se o limite estoura, o valor já foi somado. Isso é proposital. O sistema falha fechado. Depois de bater no teto, toda chamada seguinte também falha até o dia virar ou até você mexer no limite.

Agora o uso, com duas travas extras que custam uma linha cada:

import { reservar } from "./budget";

const MAX_PASSOS = 25;

for (let passo = 0; passo < MAX_PASSOS; passo++) {
  const tokens = estimarTokens(contexto);
  reservar(Math.ceil((tokens / 1_000_000) * 300)); // US$ 3/M em centavos

  const res = await fetch(API_URL, {
    method: "POST",
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(30_000),
  });

  if (await terminou(res)) break;
}

São três camadas:

  1. Teto de dinheiro por dia, que sobrevive a restart porque está no SQLite.
  2. Teto de passos no loop. Agente que precisa de mais de 25 passos provavelmente está andando em círculo.
  3. Timeout em cada requisição, para nenhuma chamada ficar pendurada para sempre.

A estimativa de tokens não precisa ser perfeita. Uma regra grosseira de "caracteres divididos por 4" já serve para um teto. O objetivo não é contabilidade, é ter um freio de mão.

Se o seu serviço roda em mais de uma instância, o SQLite local deixa de servir como fonte única. Aí o mesmo INSERT ... ON CONFLICT vai para o PostgreSQL, que você provavelmente já tem. A lógica é a mesma.

O que configurar fora do código

O seu código é só uma das camadas. Um bug nele, ou uma chave de API vazada, passa por cima de tudo. Então vale fechar as portas por fora também:

  • Limite no provedor de LLM. A maioria dos consoles deixa definir teto mensal por projeto ou workspace. Crie uma chave separada para cada app, com o próprio limite. Chave de experimento nunca deveria ter o mesmo teto que a de produção.
  • Orçamento na nuvem com ação, não só com e-mail. Se a plataforma oferece pausar o projeto ao bater o valor, ligue. Um site fora do ar por algumas horas sai mais barato que a fatura.
  • Rate limit na rota pública. Se você expôs um Bun.serve que chama LLM, um bot fazendo 50 requisições por segundo transforma seu teto diário em teto de cinco minutos. Ainda bem que tem teto, mas melhor nem chegar nele.

Nenhum desses itens dá trabalho. O difícil é lembrar de fazer antes do incidente, e não depois.

Os provedores vão adotar isso por padrão?

Aqui vai minha opinião sincera: não tão cedo, e não por vontade própria.

Cobrança sem teto é um modelo de negócio confortável. Gasto acidental também é receita. Alguns provedores devolvem o dinheiro quando o cliente reclama no Twitter, mas devolver depois é bem diferente de impedir antes. Quem não tem audiência para reclamar em público geralmente paga calado.

O que pode mudar o jogo é o volume. Quando milhões de devs estiverem rodando agentes em loop, as faturas surpresa deixam de ser caso isolado e viram suporte, chargeback e cliente perdido. Aí o padrão muda, por pura conta de custo.

Até lá, trate todo serviço pago como se ele não tivesse teto, porque provavelmente não tem. Coloque o limite de gastos no seu código Bun hoje: são umas 20 linhas, e elas valem mais que qualquer alerta bonito no dashboard.

É o tipo de detalhe que eu coloco em todo projeto que mexe com IA. Se quiser ver como isso fica em app de verdade, dá uma olhada nos projetos que eu já construí.

Resumo para o LinkedIn

Alerta de gastos não protege ninguém. Ele só avisa que o dinheiro já foi embora.

Imagina um agente de IA rodando em loop numa sexta à noite. São 1.000 chamadas de US$ 0,60 enquanto você dorme, e no sábado a fatura chega antes do café.

O Simon Willison defende que todo serviço pago já venha com teto rígido ligado por padrão. Eu concordo, mas não espero os provedores mudarem.

No meu backend Bun resolvo isso com umas 20 linhas: um contador diário no bun:sqlite, um limite de passos no loop e timeout em cada requisição.

Um limite rígido irrita no dia a dia e te salva justamente no dia em que algo dá errado.

Escrevi o passo a passo com o código completo no blog. Se você roda IA em produção, vale os 5 minutos.

#Bun #TypeScript #IA #LLM #Backend