---
title: OpenTelemetry
description: Point any OTLP exporter at AgentPing and your existing traces become runs, steps, and costs. No new SDK.
section: integrations
order: 1
---

# OpenTelemetry

Already instrumented? Point your exporter at AgentPing and you are done.
AgentPing accepts standard OTLP/HTTP trace exports, protobuf or JSON, and
maps them onto runs and steps. No new SDK, no code changes.

## The endpoint

```
POST https://eu.ingest.agentping.io/v1/traces
```

Use the host matching your data residency (`eu` or `us`). Authenticate
with your team API key (`apk_...`) as a Bearer token; exporters that
cannot set `Authorization` can send `X-AgentPing-Key` instead. Per-agent
`ping_` tokens do not work here; OTel traffic can create agents, so it
needs the team key.

## Generic exporter setup

Any OpenTelemetry SDK works with two environment variables:

```bash
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://eu.ingest.agentping.io/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="authorization=Bearer apk_eu_..."
```

## How traces map to runs

| OpenTelemetry | AgentPing |
|---|---|
| Trace | Run (one run per trace; re-exports are deduplicated) |
| Root span | The run record; its name becomes the run's goal |
| Span with model/provider attributes | LLM call step, with tokens and server-side cost |
| Span with a tool name | Tool call step |
| Any other span | Step |
| `service.name` | The agent (created automatically on first sight) |

LLM calls are recognised across emitter conventions, not just the
canonical `gen_ai.*` set. AgentPing reads the model from `gen_ai.request.model`,
`gen_ai.response.model`, OpenInference's `llm.model_name`, or the Vercel AI
SDK's `ai.model.id`; the provider from `gen_ai.system`, `llm.provider`, or
`ai.model.provider`; and token counts from any of `gen_ai.usage.input_tokens`
/ `output_tokens`, the older `prompt_tokens` / `completion_tokens`,
OpenInference's `llm.token_count.*`, or Vercel's `ai.usage.*`. An
OpenInference span tagged `openinference.span.kind = LLM` or `TOOL` is mapped
even without `gen_ai.*` attributes.

Two attributes are worth setting explicitly:

- `agentping.agent` on the resource overrides the agent identity when
  `service.name` is not what you want on the dashboard.
- `agentping.goal` on the root span tells evaluations what the run was
  supposed to achieve. Setting it materially improves evaluation quality;
  without it the root span's name is used.

## Run completion

OTel delivers spans in batches with no end-of-run signal, so a run
ingested this way completes once its root span has arrived and no new
span has landed for two minutes. Costs, health checks, and evaluations
then run exactly as they do for SDK-reported runs.

## Limits

Span attributes AgentPing does not map are kept with the step, up to 8KB
per step; beyond that the largest attributes are dropped and the step is
flagged, never rejected. Prompt and completion attributes count as step
content with the usual 64KB allowance.

## Verified emitters

Anything that speaks OTLP/HTTP works; each needs only the endpoint and
header configuration above. These are the ones we test against recorded
fixtures, so an upstream attribute rename is caught by us, not by you:

| Emitter | What to set | Mapped from |
|---|---|---|
| OpenLLMetry / Traceloop | `TRACELOOP_BASE_URL` to the endpoint, plus the auth header | `gen_ai.*` |
| Pydantic AI | Enable instrumentation, point the OTLP exporter at the endpoint | `gen_ai.*` |
| Vercel AI SDK | `experimental_telemetry: { isEnabled: true }` plus an OTLP exporter | `ai.*` |
| OpenInference (Arize Phoenix, OpenAI Agents SDK) | OTLP exporter to the endpoint | `openinference.span.kind`, `llm.*` |
| LangGraph (via OpenLLMetry's LangChain instrumentation) | OTLP exporter to the endpoint | `gen_ai.*`, node spans as steps |

CrewAI and LlamaIndex are supported through their OpenLLMetry /
OpenInference instrumentation by the same path.
