Skip to main content
Pydantic AI emits OpenTelemetry spans and metrics for every agent run, model request and tool call. You can send them to Bronto with the plain OpenTelemetry SDK, or with the Logfire SDK pointed at Bronto. You do not need a Logfire account for either. Already sending to Logfire? The Logfire SDK can send the same data to Logfire and Bronto at once, so you can keep your Logfire project while you try Bronto.
This page was verified on October 28, 2026 with pydantic-ai-slim 2.63.0, logfire 5.4.0, anthropic 2.3.0 and the OpenTelemetry Python SDK 1.45.1. GenAI libraries and semantic conventions are evolving rapidly, so package configuration and emitted attributes can change between releases. Keep your instrumentation current and verify the fields emitted by the version you deploy.

Prerequisites

  • Python 3.11+ and Pydantic AI 2.x
  • An LLM provider API key, such as Anthropic or OpenAI
  • An OTel Collector configured using Connect OpenTelemetry to Bronto, or a direct OTLP exporter (below)

Instrument Pydantic AI

Choose one of the two options. Both produce the same Pydantic AI spans and metrics.
In Pydantic AI 2.x, the Agent(..., instrument=True) constructor argument no longer exists. Use Agent.instrument_all() or logfire.instrument_pydantic_ai() as shown below.

Option 1: OpenTelemetry SDK

Register a tracer provider and a meter provider, then turn on instrumentation for all agents:
Pass InstrumentationSettings(...) to Agent.instrument_all() to change what is recorded, for example include_content=False (see Prompts and responses).

Option 2: Logfire SDK

The Logfire SDK is an OpenTelemetry distribution. With send_to_logfire=False it sends only to the OTLP endpoint in your environment:
By default, Logfire aggregates every histogram as an exponential histogram. Bronto accepts explicit-bucket histograms, so without the metrics= option above the token and cost metrics don’t arrive. Traces are not affected. To send to Logfire and Bronto, leave out send_to_logfire=False and keep the OTEL_EXPORTER_OTLP_* variables set. The Logfire SDK then exports to both. It exports over OTLP/HTTP with protobuf only.

Send traces and metrics to Bronto

Via a Collector: point the application’s OTLP exporter at the Collector, then configure the Collector using Connect OpenTelemetry to Bronto. This is recommended in production. Direct export: point the exporter at the Bronto ingestion endpoint and add your API key. Both options above read these variables:
The SDK appends /v1/traces and /v1/metrics. Set the temporality to delta: with the Python SDK’s default cumulative temporality, every export repeats the running total, and summing the metric in Bronto counts the same tokens and cost more than once. See API Keys for how to create a key with ingestion permissions.

What you get

Spans

One agent run produces this trace:
Open Tracing and select the invoke_agent <agent name> operation to see each run as a waterfall, with the provider call marked as a downstream call. Select a span to see its attributes, including prompts and responses as JSON. Fields on the Pydantic AI spans:

Metrics

Pydantic AI records two histograms, which appear in Metrics Explorer: Both carry gen_ai.operation.name, gen_ai.provider.name, gen_ai.request.model and gen_ai.response.model, plus your resource attributes, which Metrics Explorer lists as Resource:service.name and so on. They don’t carry the agent name.

Provider SDK spans

The Anthropic Python SDK (2.x) creates its own anthropic.messages.create span for every API call whenever an OpenTelemetry tracer provider is registered. It sits under Pydantic AI’s chat span and adds fields Pydantic AI doesn’t record:
  • http.response.status_code: the provider’s HTTP status, for example 429 or 529
  • anthropic.request.id: the ID to quote to Anthropic support
  • gen_ai.response.time_to_first_chunk: time to the first streamed token, in seconds
It also repeats the request’s token counts on the span; it records no metrics. To turn these spans off, set ANTHROPIC_OPEN_TELEMETRY=false. With the Claude Platform’s own telemetry enabled, the SDK’s trace context also joins Anthropic’s server-side spans to the same trace; see Claude Platform.
Do not add a second provider instrumentation, such as opentelemetry-instrumentation-openai-v2, for a client that Pydantic AI already instruments, unless you want the extra HTTP-level span. Each extra layer repeats the token counts on its own span.

Track tokens and cost

Use the metrics for totals. In Metrics Explorer, select the metric, set Calculate to Sum, and group by a field: With the instrumentation shown here, the metrics count each model request once. Additional provider instrumentation, such as opentelemetry-instrumentation-openai-v2, can also record gen_ai.client.token.usage; disable the overlapping instrumentation or filter out its token metric stream before calculating totals. On spans, the same tokens can appear three times for one request: on invoke_agent as the run total, on chat, and on the provider SDK span. For one run’s tokens, read gen_ai.aggregated_usage.* on its invoke_agent span; its cost is the sum of operation.cost on its chat spans. The metrics don’t carry the agent name. To split cost by agent, run each agent in its own service, or set a resource attribute per process with OTEL_RESOURCE_ATTRIBUTES and group by it.

Find failing runs

A failed run ends with an error status on its invoke_agent span. In Tracing, the Errors column of the invoke_agent <agent name> operation shows how many runs failed. Select the operation and the Error status filter to list the failed traces, then open one: the root span’s status message names the exception. A tool call with invalid arguments doesn’t fail the run: the execute_tool span gets an error status and pydantic_ai.tool.failure_stage = validation, and the model is asked to retry. Output validation retries don’t get a span of their own. The run shows two or more chat spans followed by the error; with content capture on, the rejected output is in gen_ai.output.messages of the earlier chat span. To stop a runaway agent before it spends more, set limits on the run, for example UsageLimits(request_limit=20, cost_limit=Decimal("0.50")). A run that hits a limit fails with UsageLimitExceeded.

Alert on cost and failures

Monitors evaluate log datasets and metrics, not spans. Create a metric monitor on operation.cost with Sum over a time window, for example more than $5 in an hour, to be alerted when model spend climbs. A monitor on gen_ai.client.token.usage works the same way for tokens. Pydantic AI’s metrics don’t record failures: a request that fails emits no token or cost data. To alert on failed runs, export the failures as logs or metrics first, for example by logging the exception your application catches around agent.run() through the OpenTelemetry Python SDK, then create a dataset monitor on those logs.

Prompts and responses

Pydantic AI records prompts, responses, tool arguments and tool results by default. To keep the message structure, roles and tool names but drop the text, pass include_content=False:
With the Logfire SDK, pass include_content=False to logfire.instrument_pydantic_ai(). The Anthropic SDK spans contain metadata only unless you set ANTHROPIC_OPEN_TELEMETRY_TRACES_CONTENT_MODE=content.

Verify in Bronto

Open Tracing and filter by the service you configured in OTEL_SERVICE_NAME. Each agent run appears as an invoke_agent <agent name> operation. Then open Metrics Explorer and select operation.cost or gen_ai.client.token.usage. If no traces appear, check that instrumentation is set up before the first agent run, verify the OTLP endpoint and API-key header, and make sure short-lived processes flush their span processor before exit. If traces appear but metrics don’t, check the temporality variable and, with the Logfire SDK, the metrics= option above.

References