Skip to main content

Por qué es importante el control de presupuesto

Un agente de IA que llama a APIs de pago en un bucle puede acumular costos rápidamente. El control de presupuesto te permite:
  • Limitar el gasto total por sesión
  • Establecer límites por dominio (p. ej., máx. 100 sats/sesión en api.weather.com)
  • Recibir un callback antes de cada pago
  • Obtener un informe completo de gasto en cualquier momento

Presupuesto global

import { L402Client, BlinkWallet } from "l402-kit";

const client = new L402Client({
  wallet: new BlinkWallet(process.env.BLINK_API_KEY!, process.env.BLINK_WALLET_ID!),
  budgetSats: 500, // max 500 sats total this session
});
Cuando una respuesta 402 incluye un campo priceSats y este superaría el presupuesto restante, el cliente lanza BudgetExceededError antes de pagar — no se gastan satoshis.

Presupuesto por dominio

const client = new L402Client({
  wallet,
  budgetSats: 2000,
  budgetPerDomain: {
    "api.weather.com": 100,
    "api.finance.com": 500,
  },
});
Los límites por dominio se verifican de forma independiente al límite global — ambos deben cumplirse para que el pago continúe.

Callbacks

const client = new L402Client({
  wallet,
  budgetSats: 1000,
  onSpend: (sats, url) => {
    console.log(`✓ Paid ${sats} sats → ${url}`);
    // log to your telemetry, update a dashboard, etc.
  },
  onBudgetExceeded: (url, sats) => {
    console.warn(`✗ Blocked: ${sats} sats requested by ${url} — budget exhausted`);
    // alert, notify Slack, etc.
  },
});
onBudgetExceeded / on_budget_exceeded se llama justo antes de que se lance BudgetExceededError — útil para registro de logs o alertas.

Informe de gasto

const report = client.spendingReport();

if (report) {
  console.log(`Total spent: ${report.total} sats`);
  console.log(`Remaining:   ${report.remaining} sats`);
  console.log("By domain:", report.byDomain);
  // { "api.weather.com": 42, "api.finance.com": 105 }

  for (const tx of report.transactions) {
    console.log(`  ${tx.ts}  ${tx.sats} sats  ${tx.url}`);
  }
}
spendingReport() devuelve null / None cuando no hay presupuesto configurado.

Manejo de BudgetExceededError

import { BudgetExceededError } from "l402-kit";

try {
  const res = await client.fetch("https://api.example.com/premium");
} catch (err) {
  if (err instanceof BudgetExceededError) {
    console.log(`Need ${err.required} sats, only ${err.remaining} remaining`);
    // gracefully degrade — return cached data, skip this step, etc.
  }
}

Notas sobre concurrencia

No compartas una instancia de L402Client entre llamadas Promise.all concurrentes cuando los límites de presupuesto son importantes.BudgetTracker.check() y record() están separados por un await (el pago Lightning). Dos llamadas client.fetch() concurrentes a diferentes endpoints pueden superar ambas la verificación de presupuesto antes de que cualquiera registre el gasto — lo que significa que el costo combinado puede exceder temporalmente tu límite de presupuesto por un pago.Patrón seguro — llamadas secuenciales:
for (const url of urls) {
  const res = await client.fetch(url); // awaited one at a time
}
Patrón arriesgado — llamadas en paralelo:
// Both may pass budget.check() before either calls budget.record()
const results = await Promise.all(urls.map(url => client.fetch(url)));
Mitigación para cargas de trabajo en paralelo: establece tu budgetSats de forma conservadora (p. ej., 80% de tu límite real) para absorber el exceso de gasto de un pago concurrente. Para una aplicación estricta, procesa las llamadas de forma secuencial.

Referencia completa de opciones

OpciónTypeScriptPythonValor predeterminadoDescripción
Presupuesto globalbudgetSatsbudget_satsilimitadoMáximo de sats para la sesión
Por dominiobudgetPerDomainbudget_per_domain{}Mapa de dominio → máx. sats
Hook de gastoonSpendon_spendSe llama después de cada pago
Hook de excedidoonBudgetExceededon_budget_exceededSe llama antes de lanzar la excepción