Claude Platform Docs
AdminOpenTelemetry

Use OpenTelemetry with the Claude SDK

Trace each request that the Claude SDK sends and each tool that its tool runner runs, and send the spans to your observability platform.

With OpenTelemetry, the Claude SDK can emit a span for each request that it sends and each tool use that the tool runner runs. You can view these spans in your observability platform, nested in your own traces.

The SDK emits spans in your application. Your application's OpenTelemetry exporter sends them to your observability platform.

1. Install the SDK

Install or upgrade to the latest version of the Claude SDK:

pip install --upgrade anthropic

If your application doesn't use the OpenTelemetry SDK yet, install it with a console exporter, which you use in the next step to print spans:

pip install opentelemetry-sdk

2. Register a tracer provider

A tracer provider is the OpenTelemetry object that creates spans and passes them to your exporter. The SDK uses the tracer provider that your application registers, so your SDK calls stay the same. Register it before the first call that you want to trace.

The following example registers a tracer provider that prints each span to the console and then makes one call. Use it to see spans before you connect an observability platform:

from anthropic import Anthropic
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

# Register a tracer provider that prints each span to the console.
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(provider)

client = Anthropic()

message = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(message.content)

The console shows one anthropic.messages.create span.

3. Send spans to your observability platform

Replace the console exporter with an OTLP exporter that points to your observability platform. For the setup in your language, see the OpenTelemetry docs on Python exporters, TypeScript exporters, .NET exporters, Go exporters, Java SDK configuration, PHP exporters, or Ruby exporters.

4. Configure the SDK

Use the following options to decide what goes into your spans and where they are sent. You can set an option in your code or, if it has one, with the environment variable under its name.

OptionUse it toDefault
traces.content_mode
ANTHROPIC_OPEN_TELEMETRY_TRACES_CONTENT_MODE=content
Include prompts and responses in spans. See Content modes.metadata_only
traces.max_content_bytes
ANTHROPIC_OPEN_TELEMETRY_TRACES_MAX_CONTENT_BYTES
Change the content limit of each span. See Content limits.0.75 MB
conversation_idLink several calls into one conversation. The SDK writes the ID to gen_ai.conversation.id. The ID is exported in both content modes, so don't put personal data or secrets in it.None
tracer_providerChoose the tracer provider that gets the SDK's spans.The tracer provider you set up in Register a tracer provider
propagation
ANTHROPIC_OPEN_TELEMETRY_PROPAGATION=false
Stop sending trace context and baggage headers with requests, for example when your HTTP client instrumentation already sends them. The SDK still emits spans. Available in the Python, TypeScript, Go, Java, Ruby, and PHP SDKs.On
open_telemetry
ANTHROPIC_OPEN_TELEMETRY=false
Turn spans off for one client, for example when another library already traces the SDK.On

The following example shows how to use each option:

# Use a specific tracer provider and record content.
# Setting open_telemetry overrides all ANTHROPIC_OPEN_TELEMETRY* environment variables.
client = Anthropic(
    open_telemetry={"tracer_provider": provider, "traces": {"content_mode": "content"}}
)

# Turn spans off for one client.
quiet_client = Anthropic(open_telemetry=False)

# Set a conversation ID with with_options(), which returns a copy of the client.
# with_options() replaces the whole option, so start from client.open_telemetry.
message = client.with_options(
    open_telemetry={**client.open_telemetry, "conversation_id": "conv_42"}
).messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello, Claude"}],
)

Next steps

Get observability into the Claude API.

Follow a request from your client into the Claude API.

Tracing a stream

A stream's anthropic.messages.create span ends when your code has read the whole stream or closed it, so close a stream that you stop reading early. In content mode, the span has the model response only when you read the stream with messages.stream().

Spans that you start while you read a stream are siblings of the SDK span, not its children. To group them with the request, start your own span around both the call and the read loop.

Tracing a Managed Agents self-hosted sandbox

In a Managed Agents self-hosted sandbox, your sandbox runs the tools, so the tool spans come from the SDK instead of the API. The environment worker in the Python, TypeScript, and Go SDKs emits a span for each tool that it runs and each request that it sends. It groups them under one anthropic.environment.work span for each work item.

To get these spans:

  1. In the process that runs the worker, set up SDK tracing as in steps 1 to 4.
  2. Allow network traffic from the sandbox to your observability platform or OpenTelemetry Collector.

When the worker sends a tool result, that request's API span is nested under its SDK span. For requests that the worker sends to Claude Platform on AWS, the API spans are in their own traces instead.

Trace context and baggage

With each request, the SDK uses your application's propagator to send the trace context to the API in headers such as traceparent. If that propagator also writes baggage, the SDK sends your baggage too. To send the trace context without baggage, register only the W3C trace context propagator.

To stop the SDK from sending these headers, for example when your HTTP client instrumentation already sends them, turn off the SDK's propagation:

client = Anthropic(open_telemetry={"propagation": False})

While the SDK calls an MCP tool, the SDK's tool span is the current span. Some MCP client libraries send the trace context to the MCP server in _meta, so a server that reads it can add its spans to your trace under that tool span. The Python mcp package from version 2.0 and the C# ModelContextProtocol package do this. The TypeScript, Go, Java, Ruby, and PHP MCP SDKs don't do it on their own.

Troubleshooting

ProblemWhat to check
No spans from the SDKRegister the tracer provider before the first call. Make sure that you installed the SDK as in Install the SDK and that ANTHROPIC_OPEN_TELEMETRY isn't false.
The Java SDK doesn't send spans to the registered tracer provider, or doesn't send trace context to the APIAnother dependency sets opentelemetry-api below 1.57.0. Upgrade opentelemetry-api to 1.57.0 or later.
The SDK spans don't nest under your spans, or the spans that your tool creates don't nest under the tool spanCall the SDK from inside one of your own spans, as the example in Connect API spans to your own traces does. In Go, start the spans that your tool creates from the ctx that the tool runner passes to it.
Requests from the SDK don't have a traceparent headerRegister the W3C trace context propagator with OpenTelemetry. The SDK writes the headers with your application's propagator.
A stream's SDK span doesn't end or doesn't have the responseSee Tracing a stream.
Each request appears twiceA request's SDK span and API span have the same name. Use the span kind or service.name to tell them apart. If another library also traces the SDK, turn off one of the two.

Compatibility

Supported platforms
  • Claude APIBeta
  • Claude Platform on AWSBeta
  • Amazon BedrockBeta
  • Google CloudBeta
  • Microsoft FoundryBeta
Works with
Messages API, Claude Managed Agents

Was this page helpful?