See Inside Every Agent Run: Hands-On with OpenTelemetry Traces on the Claude Platform
AXC AI News · Friday, October 16, 2026
Topic: Claude Developer Platform · Observability · Managed Agents · Fast mode
Reading time: ~9 min · Hands-on time: ~25 min
TL;DR
What's new: On Oct 14, 2026, Anthropic launched OpenTelemetry (OTel) export (beta). Every Messages API request, model run, tool use and Managed Agents turn can become a span in the observability tool you already use: Langfuse, Grafana, Datadog, Honeycomb, Sentry, New Relic, LangSmith and others.
Two ways in:
SDK tracing: the Claude SDK (Python, TS, C#, Go, Java, PHP, Ruby) emits spans from your app. You only need to register a tracer provider, and no admin rights.
API tracing: an org admin adds a destination in the Claude Console, and Anthropic's servers send spans without any code change, including what happens inside the API: each model run, server tools such as web search, and agent turns.
Also new (Oct 15): Fast mode for Claude Opus 5.5 is out of research preview. Add
speed: "fast"to get up to 2.5× more output tokens/sec at 2× the price (40 per Mtok). You no longer need a waitlist or beta header.Today's hands-on: trace a small tool-using agent locally in 5 minutes, ship the traces to Langfuse, then use those traces to measure whether fast mode is worth it for your workload.
1. Why this matters: agents are black boxes until you trace them
Last issue we ran up to 1,000 agents with a single prompt. The next question your tech lead will ask is fair: "What did they actually do, how long did each step take, and where did the tokens go?"
Until this week, the honest answers were:
Approach | What you see | What you miss |
|---|---|---|
| Totals per call | Which step was slow, which tool failed, how calls relate to each other |
Hand-rolled wrapper spans | Your client-side calls | Everything inside the API: each model run, server tools, Managed Agents turns, sub-threads |
Third-party proxy | Request/response pairs | Agent structure, and you add a hop to every request |
OTel export closes the gap. The API now emits the spans it produces server-side and nests them under your spans through the standard W3C traceparent header. In one trace you see a single tree: your HTTP handler → the SDK call → each model run inside the API → each tool → the next turn.
POST /chat (your service)
└─ anthropic.messages.tool_runner ← SDK (your app)
├─ anthropic.messages.create [CLIENT] ← SDK
│ └─ anthropic.messages.create [SERVER] ← Claude API (destination)
│ ├─ anthropic.model_request
│ ├─ anthropic.tool_use web_search ← server tool, run by the API
│ └─ anthropic.model_request
├─ anthropic.custom_tool_use get_weather ← your tool, run by the tool runner
└─ anthropic.messages.create [CLIENT]
└─ anthropic.messages.create [SERVER] …All spans use the OpenTelemetry GenAI semantic conventions (gen_ai.usage.input_tokens, gen_ai.conversation.id, gen_ai.tool.name …), so Claude data sits next to your other services in the same dashboards. Customers from the launch post: Ramp scores Langfuse traces automatically and reports 10–20% lower token usage on some agents, and FloQast puts every model call in the same Grafana trace as the accounting workflow it served, so auditors can review it.
2. The key facts (from the official docs)
Item | SDK tracing | API tracing (destination) |
|---|---|---|
Set up via | Code: register a tracer provider | Console → Settings → OpenTelemetry → Add destination (or Admin API / |
Permission | None | Admin role (developers can view) |
Messages API & Sessions API requests | ✅ | ✅ |
Each model run inside a request | — | ✅ |
Managed Agents turns, sub-threads, outcome evaluations | — | ✅ |
Server tools (web search, MCP connector…) | — | ✅ |
Client tools run by the SDK tool runner | ✅ | — |
Works on Bedrock / Vertex / Foundry | ✅ | Claude Platform & Claude Platform on AWS only |
Content modes. The default is Metadata only: IDs, timing, status, token counts, and the names of agents, tools and MCP servers. Full content adds prompts and responses (gen_ai.input.messages, gen_ai.output.messages, gen_ai.system_instructions). Each span holds up to 0.75 MB of content by default; longer content is truncated and flagged with anthropic.content.truncated = true.
Destination requirements. An HTTPS OTLP/HTTP (protobuf) endpoint on port 443, with a hostname, not an IP. Delivery is at least once and best effort. The API disables a destination that sends a 3xx redirect, resolves to a non-public IP, or fails for 24 hours straight. Limits: 10 destinations per org, 20 headers each.
Partners with a ready-made Console provider: Arize, Braintrust, Databricks, Datadog, Grafana Cloud, Honeycomb, Langfuse, LangSmith, New Relic, PostHog, Sentry, Splunk, or Custom for any OTLP endpoint.
3. Hands-on: trace an agent, then measure fast mode (25 min)
We'll use Langfuse. It's open source, has a free cloud tier, and can also be self-hosted. Any OTLP backend works the same way.
Step 0 — Setup (3 min)
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install --upgrade anthropic opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
export ANTHROPIC_API_KEY=sk-ant-... # PowerShell: $env:ANTHROPIC_API_KEY="sk-ant-..."⚠️ The SDK's OTel options and span names are beta and can change between SDK versions. Pin the
anthropicversion inrequirements.txtonce your dashboards work.
Step 1 — Save this as traced_agent.py
The script runs a small tool-using agent inside your own span. If you set OTEL_EXPORTER_OTLP_ENDPOINT, spans go to Langfuse. If you don't, they print to the console, so you can try it in 5 minutes with no account.
"""traced_agent.py — a tiny tool-using agent, fully traced with OpenTelemetry.
Usage:
python traced_agent.py # standard speed
python traced_agent.py --fast # Opus 5.5 fast mode (2x price, up to 2.5x output tok/s)
"""
import json
import os
import sys
import time
from anthropic import Anthropic, beta_tool
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
BatchSpanProcessor, ConsoleSpanExporter, SimpleSpanProcessor,
)
# 1) Register a tracer provider BEFORE creating the client / first call.
provider = TracerProvider(resource=Resource.create({"service.name": "axc-trace-demo"}))
if os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"):
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
# Reads OTEL_EXPORTER_OTLP_ENDPOINT / _HEADERS from the environment, appends /v1/traces.
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
else:
provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("axc-trace-demo")
# 2) Full content = prompts & responses in spans. Use metadata_only for anything sensitive.
client = Anthropic(open_telemetry={"traces": {"content_mode": "content"}})
FAST = "--fast" in sys.argv
@beta_tool
def list_open_tickets(team: str) -> str:
"""List open tickets for a team.
Args:
team: Team name, e.g. 'payments'
"""
time.sleep(0.4) # pretend this is a slow internal API, so it shows up in the trace
return json.dumps([
{"id": "PAY-101", "title": "Refund webhook retries twice", "age_days": 12},
{"id": "PAY-117", "title": "Currency rounding in invoice PDF", "age_days": 3},
{"id": "PAY-120", "title": "Timeout calling FX provider", "age_days": 1},
])
@beta_tool
def get_ticket_details(ticket_id: str) -> str:
"""Get the description and recent comments for a ticket.
Args:
ticket_id: The ticket ID, e.g. 'PAY-101'
"""
return json.dumps({"id": ticket_id, "description": "Customer charged, webhook fired "
"twice because our handler returned 500 after committing."})
# 3) Wrap the agent in YOUR span: the SDK spans (and API spans, if a destination
# exists) nest under it in one trace.
with tracer.start_as_current_span("triage_payments_backlog") as span:
span.set_attribute("app.speed", "fast" if FAST else "standard")
extra = {"speed": "fast"} if FAST else {}
t0 = time.perf_counter()
runner = client.with_options(
open_telemetry={**client.open_telemetry, "conversation_id": "triage-demo-1"}
).beta.messages.tool_runner(
model="claude-opus-5-5",
max_tokens=2048,
tools=[list_open_tickets, get_ticket_details],
messages=[{"role": "user", "content":
"List the payments team's open tickets, open the oldest one, and write "
"a 5-bullet triage note with a likely root cause and a first fix."}],
**extra,
)
final = runner.until_done()
elapsed = time.perf_counter() - t0
print("\n".join(b.text for b in final.content if b.type == "text"))
print(f"\n⏱ {elapsed:.1f}s · output tokens (last turn): {final.usage.output_tokens}"
f" · speed used: {getattr(final.usage, 'speed', 'n/a')}")
provider.shutdown() # flush spans before exitStep 2 — Run it locally (2 min)
python traced_agent.pyScroll through the console output. You'll see JSON spans named anthropic.messages.tool_runner, anthropic.messages.create (one per round trip) and anthropic.custom_tool_use list_open_tickets, each with timing, gen_ai.usage.* token counts and, because we chose content mode, the full prompt and response. They all share one trace ID under your triage_payments_backlog span.
Step 3 — Ship the traces to Langfuse (5 min)
Sign up at cloud.langfuse.com (free) or self-host it, create a project, and go to Settings → API Keys to create a key pair.
Build the Basic-auth string from
public:secret:
# bash / Git Bash
export AUTH=$(echo -n "pk-lf-xxx:sk-lf-xxx" | base64 -w 0)
export OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel" # US: https://us.cloud.langfuse.com/...
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic ${AUTH},x-langfuse-ingestion-version=4"# PowerShell
$env:AUTH = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("pk-lf-xxx:sk-lf-xxx"))
$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://cloud.langfuse.com/api/public/otel"
$env:OTEL_EXPORTER_OTLP_HEADERS = "Authorization=Basic $($env:AUTH),x-langfuse-ingestion-version=4"Run
python traced_agent.pyagain and open Tracing in Langfuse. You'll get a waterfall: your span → tool runner → two or three model calls, with the 0.4 slist_open_ticketstool clearly visible between them.
💡 The
x-langfuse-ingestion-version=4header makes traces appear in real time. Without it, OTel data can take up to 10 minutes to show up.
Step 4 — Is fast mode worth 2× the price? Measure it (5 min)
python traced_agent.py # run 3x
python traced_agent.py --fast # run 3xIn Langfuse, filter by app.speed and compare the anthropic.messages.create durations. Before you judge the results, keep these points from the docs in mind:
Fast mode raises output tokens per second (up to 2.5×). It does not cut time-to-first-token. Long answers and code generation gain the most; short tool-calling turns gain little.
Fast and standard requests don't share the prompt cache. Switching speeds mid-conversation is a cache miss.
Fast mode isn't available with the Batch API, Priority Tier, or on Claude Platform on AWS. It needs the Start, Build or Scale usage tier (not Evaluation).
usage.speedin the response tells you which speed was actually used. On a429, the SDK retries twice, or you can fall back to standard by retrying withoutspeed.
Rule of thumb: use fast mode where a human is waiting on a long answer, such as interactive code generation or live demos. Keep standard speed for background agents and batch jobs. Now you have a trace to back that up.
Step 5 (admin, optional) — Turn on server-side spans (5 min)
Ask an org admin to open Console → Settings → OpenTelemetry → Add destination, pick Langfuse as the provider, choose the region, paste the keys, and select the workspaces. Then click Test in the destination's menu and look for the anthropic.test span.
You can also do it from the CLI:
ant auth login --profile admin --scope "org:admin"
ant --profile admin beta:organization:telemetry-destinations create \
--endpoint-url https://cloud.langfuse.com/api/public/otel \
--headers '{authorization: "Basic <BASE64_PK:SK>"}' \
--traces.content-mode metadata_only \
--workspaces.type allRun the script again. Each anthropic.messages.create CLIENT span now has a SERVER child (service.name = claude-platform) with anthropic.model_request spans inside it. For Managed Agents, you get one anthropic.session.turn per turn, including sub-threads from multi-agent runs. Group by anthropic.session.id to see the whole session, or by gen_ai.conversation.id to read one thread as a transcript.
Shortcut: the docs suggest simply asking Claude Code: "Set up OpenTelemetry tracing with the Claude API and Claude SDK." Claude Code 2.1.301 added OTel guidance to the built-in claude-api skill for exactly this.
4. Gotchas we found in the docs (read before you build dashboards)
Don't double-count tokens. The SDK span and the API span are both named
anthropic.messages.create. Tell them apart by span kind (CLIENTvsSERVER) orservice.name. Per request, sum one source only. For Managed Agents, sumanthropic.model_request+anthropic.session.outcome_evaluation.gen_ai.usage.input_tokensalready includes cache reads and writes. Don't add the cache attributes on top.Traces aren't billing. Delivery is best effort and spans can be lost or duplicated (dedupe on trace ID + span ID). For per-key usage, such as spotting a leaked key, use the Usage API.
Full content means your prompts leave Anthropic. The observability vendor stores them under its retention rules. Use
metadata_onlyfor customer data unless security has approved it. Orgs with HIPAA, FERPA or K-12 restrictions always export metadata only.Streams: the span ends only when you finish reading or close the stream. Content is captured only with
messages.stream().Node users: if you use
@opentelemetry/auto-instrumentations-node, setopenTelemetry: { propagation: false }. An undici bug otherwise adds a secondtraceparentheader and breaks the trace context.Self-hosted collectors: the API only sends to HTTPS on 443, while the Collector's OTLP/HTTP default is 4318. Put a reverse proxy in front of it.
conversation_idis always exported, even in metadata-only mode, so never put emails or user names in it.
5. Where we could use this right away
Use case | What to look at in the trace |
|---|---|
"Why is the bot slow today?" | Waterfall: model time vs. our tool or API time |
Cost per feature / per team | Sum |
Agent review before widening its access | Full-content traces of every tool call and its arguments ( |
Building evals from production | Export real conversations from Langfuse into a dataset |
Fast vs standard, Haiku vs Opus decisions | A/B durations and token counts, with evidence instead of gut feel |
6. Also new this week in the Claude ecosystem
Fast mode GA for Opus 5.5 (Oct 15): no beta header and no access request. It has a default rate limit in Start/Build/Scale tiers and is also on Microsoft Foundry (Azure-hosted deployments).
Claude Code 2.1.300 → 2.1.301:
Claude Code's own trace spans now carry GenAI semantic-convention attributes (
gen_ai.provider.name,gen_ai.conversation.id,gen_ai.tool.name,error.type), so Claude Code, SDK and API traces line up in one backend.OTel
tool_decisionevents gainedclassifier_verdict,permission_modeandclassifier_category, so you can tell auto-mode classifier blocks apart from permission rules.api_requestevents now split cache writes intocache_creation_5m_tokens/cache_creation_1h_tokens.Also: vim
%motion,CLAUDE_CODE_RETRY_WATCHDOG_MAX_USAGE_LIMIT_WAIT_MS, and a smarter summary when you switch to a smaller-context model.
Managed Agents scheduled deployments are now available on Claude Platform on AWS (Oct 14).
Compliance API (Enterprise, beta, Oct 15) now returns users' saved Instructions for Claude and Cowork Global instructions.
Anthropic opened a Singapore office and signed an MOU with the Government of Singapore (Oct 14). That's relevant if you work with customers in APAC.
7. On the GitHub radar (weekly trending, Oct 16)
morluto/rea ("Reverse Engineer Anything") is this week's runaway #1. It's an MCP server that lets Claude Code, Codex, Cursor and others analyze binaries, Electron/JS apps, .NET assemblies, APKs and network captures, with evidence for every claim. Setup:
npx rea-agents setup. MIT. Use it only on software you own or are authorized to examine, and install only from the official repo or npm package, because look-alike copies exist.heygen-com/hyperframes: write HTML, render deterministic MP4 video, built for agents. It's a Claude Code plugin:
claude plugin marketplace add heygen-com/hyperframesand thenclaude plugin install hyperframes@hyperframes. Apache-2.0. It's handy for release-note videos and demo clips.mattpocock/skills: a well-known engineer's personal agent-skills directory, still climbing.
cathrynlavery/diagram-design: 44 editorial diagram types as self-contained HTML/SVG for AI coding tools.
(Star counts on github.com/trending moved by tens of thousands between snapshots this week, so we've left them out.)
Your 3-step challenge for today
☐ Run
traced_agent.pylocally and find yourcustom_tool_usespan in the console output.☐ Send it to Langfuse and run the fast vs standard A/B three times each. Post the median durations in the team channel.
☐ Pick one of our Claude-powered services and wrap its main handler in a span. Next week, ask an admin to add a metadata-only destination for its workspace.
Show us your waterfalls, especially the surprising ones. 🔍🚀
Sources
Claude Docs: OpenTelemetry overview · SDK tracing · API tracing / destinations · Span reference · Fast mode · Pricing · Tool runner
Claude Platform release notes (Oct 14 & 15, 2026)
Launch article: Trace agents with OpenTelemetry on the Claude Platform · New Relic
Langfuse: Claude Platform integration · OpenTelemetry endpoint
Claude Code CHANGELOG (v2.1.300 – 2.1.301) · Anthropic News
GitHub radar: github.com/trending (weekly) · gittrend.io AI agents
