Skip to main content
Pricing AI features requires per-token billing. Historically that meant building middleware to extract usage from every provider, normalize it across response shapes, batch events to a billing system, and survive outages. That work isn’t your product. The Lago Agent SDK removes that layer. You wrap your existing LLM client and keep calling it the same way: same arguments, same return shape, same exceptions. The SDK extracts normalized token usage from each response and streams events to Lago in the background. It can also price each call for you. The SDK fetches live LLM prices, turns every call into a dollar cost, and lets you add a margin on top. Your LLM costs land in one place, and you decide what to resell them for. Available in two flavors, both open source:

How it works

  • Wraps your existing LLM client in place. Your application code does not change.
  • Extracts usage from each response into a normalized shape (CanonicalUsage).
  • Buffers events in memory and flushes them in batches to Lago’s /events/batch endpoint.
  • Survives provider and Lago outages with exponential backoff and a bounded buffer.
  • p99 wrap overhead under 5 ms. Your LLM call is never blocked on Lago.
  • Never breaks your LLM call. Instrumentation errors are caught, logged, and optionally forwarded to your observability stack.

Supported providers

Quickstart

1

Install the SDK

2

Initialize and wrap your LLM client

Pass your Lago API key and a default external_subscription_id, then wrap your provider client. The returned object is a drop-in replacement.
3

Make LLM calls normally

The wrapped client preserves the original signature, return shape, and exceptions. No call-site changes required.
Streaming calls work the same way. client.converse_stream(...) in Python and ConverseStreamCommand in TypeScript both flow through the SDK and emit a single event per completed response.
4

Flush events on shutdown

Events flush automatically in the background. Call flush() explicitly at process exit (FastAPI shutdown hook, Express server close, AWS Lambda extension, etc.) so in-flight events are not lost.
5

Register billable metrics in Lago

Before events count toward charges, register matching billable metrics in your Lago tenant. The SDK ships with default metric codes (see Captured token dimensions below). Register each one as a sum_agg metric.Follow Create a billable metric to set them up, then attach charges to them in your plan. For a full example, see the per-token pricing template.

Mistral quickstart

The same wrap() call works with the native Mistral SDK:

Bill in tokens or in dollars

The SDK can bill two ways. By default it sends token counts, and you turn those into money with your Lago plans. Switch to price mode and the SDK sends the dollar cost of each call instead. It looks up the price of the model, multiplies by the tokens used, applies your markup, and emits one cost event. Where prices come from. The SDK reads public price lists that Lago maintains. OpenRouter for native OpenAI, Anthropic, Mistral, and Gemini clients. The AWS Bedrock public price list for Bedrock. No API keys, no price file for you to maintain. Prices refresh in the background about once an hour, so your LLM call is never slowed down waiting on a price. Add your margin. Set a markup to resell LLM access at a profit. 1.2 means the customer pays your cost plus 20%. Your cost, plus your margin, is what gets billed.
You can also flip a single call to price mode and set a one-off markup: Python extra_lago={"mode": "price", "markup": 1.5}, TypeScript lago: { mode: "price", markup: 1.5 } (for Bedrock, attach it as the command’s __lago).
Lago setup. In price mode the SDK emits one event per call with the metric code llm_cost. Register a sum billable metric named llm_cost and attach a dynamic charge to it. Lago adds up the per-call cost into a single fee. The event also carries a full breakdown in its properties: the USD value, the cost before markup, the markup applied, and the price source.
Never under-bill. If a price is not available yet (the price list has not warmed up on the very first call, or the model is missing from the source), the SDK falls back to sending normal token-count events and fires on_error / onError so you can see it. It never silently drops the usage.

Captured token dimensions

The SDK normalizes every provider response into a 10-field CanonicalUsage object and emits one event per non-zero field. The default metric codes match Lago’s conventions. Override them in the config if your tenant already uses different names.

Provider coverage

Which fields each adapter populates:
Reasoning, image, and audio fields are populated by the native OpenAI, Anthropic, and Gemini adapters.

Multi-tenant: pick a subscription per call

Lago needs to know which customer to bill for each LLM call. The SDK resolves the external_subscription_id in this priority order:
  1. Per-call override: highest precedence, attached to the individual request.
  2. Context-bound: set once per request handler. Propagates safely across async boundaries.
  3. Default at init: fallback if nothing else is set.
Any dimensions you pass go through to the event as properties. Useful for filter-based charges (model, feature, region, etc.). See Charges with filters.
If subscription resolution returns nothing (no per-call override, no context, no default), the event is dropped and an ERROR is logged. Make sure at least one of the three levels is set before your first call.

Configuration reference

Both SDKs expose the same configuration surface with idiomatic naming.

Custom metric codes

If your Lago tenant already uses different metric codes, override them at init time:

Error handling

The SDK never breaks your LLM call. If instrumentation fails (adapter bug, Lago unreachable, network error), the SDK catches the error, logs a warning, and your call returns normally.
Forward instrumentation errors to your observability stack with the on_error / onError hook:

Exception hierarchy

Both SDKs export the same error classes for callers that want to handle SDK errors explicitly:
  • LagoSDKError: base class for every SDK-raised error.
  • LagoApiError: non-2xx response from Lago.
  • LagoConfigError: invalid configuration at init time.
  • UnknownClientError: wrap() was called on a client the SDK does not recognize.

Verify the integration

1

Make a wrapped LLM call

Run one end-to-end request through the wrapped client, then call flush() explicitly to push the event immediately.
2

Check the event in Lago

In the Lago dashboard, open Developers → Events and confirm an event appears with the expected metric code and properties.
3

Confirm usage on the customer

Open the customer’s usage view and confirm the metric counter increased.
4

If nothing arrives

Check the on_error / onError callback first. Instrumentation failures are silent by design. The most common causes are an unregistered metric code, a missing external_subscription_id, or an API key without write access.

Resources