noburn.devdocs

SDK Reference

Complete reference for the noburn Python and Node.js SDKs.

Python SDK

Installation

pip install noburn

Requires Python 3.9+.

NoburnGuard

The main class. Create one instance per project and reuse it across requests.

from noburn import NoburnGuard

guard = NoburnGuard(
    api_key="sk-nb-xxxxxxxxxxxxxxxx",
    project_id="your-project-id",
    budget_cap_usd=10.00,
)

Constructor options

ParameterTypeRequiredDescription
api_keystrYour project SDK key from the dashboard
project_idstrProject UUID from the dashboard
budget_cap_usdfloatMonthly spend cap in USD. Calls are blocked once exceeded
base_urlstrOverride API endpoint (default: https://noburn.dev)
timeoutfloatRequest timeout in seconds (default: 2.0)
on_errorstrBehavior when the noburn API is unreachable: "allow" (default — fail open, never breaks your app) or "block" (fail closed — blocks with reason "noburn_unreachable")

guard.check()

Checks whether a planned LLM call should be allowed. Call this before every LLM API call.

result = guard.check(
    model="gpt-4o",
    estimated_tokens_in=1500,
    estimated_tokens_out=500,
    end_user_id="user_abc123",   # optional — for per-user tracking
    context={"plan": "free"},    # optional — matched against policy rules
)

Parameters

ParameterTypeRequiredDescription
modelstrModel name as it appears in your LLM provider's API
estimated_tokens_inintEstimated prompt token count
estimated_tokens_outintEstimated completion token count
end_user_idstrIdentifier for the end user. Enforces that user's per-user budget cap
run_idstrRun identifier from start_run(). Enforces that run's per-run budget cap
cost_usdfloatCost override for unrecognized models. For recognized models, noburn prices the call server-side from token counts
contextdictArbitrary key-value pairs matched against server-side policy rules (e.g. {"plan": "free"})

Return value: CheckResult

FieldTypeDescription
blockedboolTrue if the call should be blocked
block_reasonstr | NoneWhy the call was blocked: "budget_exceeded", "user_budget_exceeded", "run_budget_exceeded", "policy_rule", "plan_limit_exceeded", or "noburn_unreachable"
forced_model_tierstr | None"small" or "big" when a policy rule forces a cheaper/stronger model; otherwise None
spend_usdfloatCurrent monthly project spend at time of check
budget_cap_usdfloat | NoneThe active project budget cap
remaining_usdfloat | NoneProject budget remaining. None if no cap
user_spend_usd / user_cap_usd / user_remaining_usdfloat | NonePer-user budget state (when end_user_id is passed)
run_spend_usd / run_cap_usd / run_remaining_usdfloat | NonePer-run budget state (when run_id is passed)

Pricing authority

The SDK includes a local pricing table for offline fallback estimates only. On every check() and record(), noburn recomputes cost server-side from token counts. Call guard.fetch_pricing() (Python) or guard.fetchPricing() (Node) to refresh the local table from GET /api/v1/pricing (global rates + your project overrides). For unrecognized models, pass cost_usd explicitly or rely on the server's conservative token floor.

guard.record()

Records the actual outcome of an LLM call. Always call this after a successful call.

guard.record(
    model="gpt-4o",
    tokens_in=1423,
    tokens_out=487,
    cost_usd=0.00848,
    was_blocked=False,
    end_user_id="user_abc123",
    latency_ms=1240,
)

Parameters

ParameterTypeRequiredDescription
modelstrModel name
tokens_inintActual prompt token count
tokens_outintActual completion token count
cost_usdfloatActual cost in USD
was_blockedboolWhether the call was blocked
end_user_idstrSame identifier passed to check()
run_idstrSame run identifier passed to check()
latency_msintEnd-to-end latency in milliseconds
block_reasonstrReason if was_blocked=True
timestampstrISO 8601 timestamp (default: now)

record() is fire-and-forget — it returns immediately and never raises. The event is delivered on a background worker with retry. Call guard.flush() before your process exits to drain any queued events.

Per-user budgets

Pass end_user_id to track spend per user. You can set per-user caps in the dashboard under End-User Budgets — once a user crosses their cap, their calls are blocked with reason "user_budget_exceeded", independently of the project budget.

# Each user has independent spend tracking
result = guard.check(
    model="claude-3-5-sonnet-20241022",
    estimated_tokens_in=800,
    estimated_tokens_out=200,
    end_user_id=f"user_{request.user.id}",
)

Error handling

check() never raises for budget decisions. By default it fails open: if the noburn API is unreachable, the call is allowed (blocked=False). Set on_error="block" to fail closed instead — unreachable means blocked=True with reason "noburn_unreachable". The only exception check() can raise is NoburnAuthError (invalid SDK key).

from noburn import NoburnGuard, NoburnAuthError

guard = NoburnGuard(api_key="sk-nb-…", project_id="…", on_error="block")

try:
    result = guard.check(model="gpt-4o", estimated_tokens_in=1000, estimated_tokens_out=300)
except NoburnAuthError:
    # Invalid SDK key — fix your credentials
    raise

if result.blocked:
    handle_blocked(result.block_reason)

start_run() is stricter: it raises on network/auth failure (NoburnTimeoutError on timeout, NoburnAuthError on a bad key), since a run must be registered before you proceed.

Agent runs

A run bounds a single agent invocation with its own budget cap, separate from the project and per-user caps. Start a run, thread its run_id through check()/record(), then end it.

run = guard.start_run(budget_cap_usd=0.50)   # StartRunResult(run_id, budget_cap_usd, started_at)

result = guard.check(
    model="gpt-4o",
    estimated_tokens_in=1000,
    estimated_tokens_out=300,
    run_id=run.run_id,
)
# ... make the LLM call ...
guard.record(model="gpt-4o", tokens_in=980, tokens_out=290,
             cost_usd=0.005, was_blocked=False, run_id=run.run_id)

summary = guard.end_run(run.run_id)   # EndRunResult(status="completed" | "budget_exceeded", spend_usd, ...)
MethodDescription
start_run(budget_cap_usd=None, end_user_id=None, metadata=None)Registers a run. Raises on network/auth error.
end_run(run_id)Finalizes a run; returns EndRunResult or None on a network error.

Node.js SDK

Installation

npm install @noburn/sdk

Requires Node.js 18+. Works with TypeScript out of the box.

NoburnGuard

import { NoburnGuard } from '@noburn/sdk';

const guard = new NoburnGuard({
  apiKey: process.env.NOBURN_API_KEY!,
  projectId: process.env.NOBURN_PROJECT_ID!,
  budgetCapUsd: 10.00,
});

Constructor options

OptionTypeRequiredDescription
apiKeystringYour project SDK key
projectIdstringProject UUID
budgetCapUsdnumberMonthly spend cap in USD
baseUrlstringOverride API endpoint
timeoutMsnumberRequest timeout in ms (default: 2000)
onError'allow' | 'block'Behavior when the API is unreachable: 'allow' (default — fail open) or 'block' (fail closed — blockReason: 'noburn_unreachable')

guard.check()

const result = await guard.check({
  model: 'gpt-4o',
  estimatedTokensIn: 1500,
  estimatedTokensOut: 500,
  endUserId: 'user_abc123',
  context: { plan: 'free' },  // optional — matched against policy rules
});

if (result.blocked) {
  return new Response('Budget exceeded', { status: 402 });
}

Options

OptionTypeRequiredDescription
modelstringModel name
estimatedTokensInnumberEstimated prompt tokens
estimatedTokensOutnumberEstimated completion tokens
endUserIdstringIdentifier that enforces the user's per-user budget cap
runIdstringRun id from startRun(); enforces that run's per-run budget cap
costUsdnumberCost override for unrecognized models (recognized models are priced server-side from token counts)
contextRecord<string, string>Key-value pairs matched against server-side policy rules (e.g. { plan: 'free' })

Return: CheckResult

interface CheckResult {
  blocked: boolean;
  // 'budget_exceeded' | 'user_budget_exceeded' | 'run_budget_exceeded'
  // | 'policy_rule' | 'plan_limit_exceeded' | 'noburn_unreachable' | null
  blockReason: string | null;
  forcedModelTier: 'small' | 'big' | null;  // set by a force_small/force_big rule
  spendUsd: number;
  budgetCapUsd: number | null;
  remainingUsd: number | null;
  // Present when endUserId / runId are supplied:
  userSpendUsd?: number;
  userCapUsd?: number | null;
  userRemainingUsd?: number | null;
  runSpendUsd?: number;
  runCapUsd?: number | null;
  runRemainingUsd?: number | null;
}

guard.record()

await guard.record({
  model: 'gpt-4o',
  tokensIn: response.usage.prompt_tokens,
  tokensOut: response.usage.completion_tokens,
  costUsd: 0.00848,
  wasBlocked: false,
  endUserId: 'user_abc123',
  latencyMs: 1240,
});

Agent runs

Bound a single agent invocation with its own cap, threading runId through check()/record():

const run = await guard.startRun({ budgetCapUsd: 0.5 });

const check = await guard.check({
  model: 'gpt-4o',
  estimatedTokensIn: 1000,
  estimatedTokensOut: 300,
  runId: run.runId,
});
// ... make the LLM call ...
await guard.record({ model: 'gpt-4o', tokensIn: 980, tokensOut: 290, costUsd: 0.005, wasBlocked: false, runId: run.runId });

const summary = await guard.endRun(run.runId); // { status: 'completed' | 'budget_exceeded', spendUsd, ... }

record() is fire-and-forget with background retry; call await guard.flush() before exit to drain queued events. startRun() throws on network/auth error (NoburnTimeoutError on timeout).

Framework integrations

Next.js middleware

// middleware.ts
import { NoburnGuard } from '@noburn/sdk';

const guard = new NoburnGuard({
  apiKey: process.env.NOBURN_API_KEY!,
  projectId: process.env.NOBURN_PROJECT_ID!,
  budgetCapUsd: 50,
});

export async function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/api/ai')) {
    const userId = request.headers.get('x-user-id') ?? undefined;
    const check = await guard.check({
      model: 'gpt-4o',
      estimatedTokensIn: 1000,
      estimatedTokensOut: 500,
      endUserId: userId,
    });

    if (check.blocked) {
      return Response.json(
        { error: 'Budget exceeded', code: 'BUDGET_EXCEEDED' },
        { status: 402 }
      );
    }
  }
}

Express

import express from 'express';
import { NoburnGuard } from '@noburn/sdk';

const guard = new NoburnGuard({ apiKey: '...', projectId: '...' });
const app = express();

app.use('/api/ai', async (req, res, next) => {
  const check = await guard.check({
    model: req.body.model ?? 'gpt-4o',
    estimatedTokensIn: 1000,
    estimatedTokensOut: 500,
    endUserId: req.user?.id,
  });

  if (check.blocked) {
    return res.status(402).json({ error: check.blockReason });
  }
  next();
});

On this page