Skip to main content
SGP Traces must be enabled on your deployment. The OpenTelemetry GenAI semantic conventions are still evolving, so the attribute mapping below can change.
Anything that emits OpenTelemetry traces can send them to SGP Traces. Point its OTLP exporter at the endpoint below; no SGP SDK or collector is needed, and usually no code change. This includes frameworks with built-in tracing such as Pydantic AI and the Claude Agent SDK, instrumentation layers such as OpenLLMetry and LangSmith, and your own OpenTelemetry SDK setup. To use the SGP SDK instead, see Tracing SDK Initialization. For openai-agents, use the native OpenAI Agents Integration.

Prerequisites

  • An SGP API key and the ID of the account that will own the traces.
  • Your SGP Traces host. The default is sgp-traces.<deployment-url>, where <deployment-url> is the domain your SGP API runs under as api.<deployment-url>. The development deployment, for example, is https://sgp-traces.dev-sgp.scale.com/v1/traces. Deployments can override the host; ask your deployment owner if the default does not resolve.
  • A framework or SDK that exports over OTLP/HTTP protobuf. For hand instrumentation in Python:
In Node, use @opentelemetry/exporter-trace-otlp-proto. The -http package sends JSON regardless of OTEL_EXPORTER_OTLP_TRACES_PROTOCOL and is rejected with 400.

Configure the exporter

Set these variables before starting your application. Every OpenTelemetry SDK reads them. Frameworks that manage their own exporter or keep tracing opt-in need one extra step, listed under Framework notes.
The trace-specific variables take the full URL, /v1/traces included. The generic OTEL_EXPORTER_OTLP_ENDPOINT must be the bare https://sgp-traces.<deployment-url>: the SDK appends /v1/traces, and a value that already has it is exported to /v1/traces/v1/traces. LangSmith is the exception, see below. Prefer the trace-specific variables; the generic ones also drive metric and log exporters, and SGP Traces accepts traces only. If your framework emits through the global tracer provider but does not register an exporter, add one at startup. Existing instrumentation is unaffected:

Framework notes

  • Pydantic AI emits spans only after Agent.instrument_all() or instrument=True, through the global tracer provider, so register the exporter as above. With Logfire, logfire.configure(send_to_logfire=False) builds the exporter from the OTEL_EXPORTER_OTLP_* variables and logfire.instrument_pydantic_ai() turns the spans on.
  • OpenLLMetry (Traceloop) ignores the OTEL_EXPORTER_OTLP_* variables. Set TRACELOOP_BASE_URL="https://sgp-traces.<deployment-url>" (it appends /v1/traces) and TRACELOOP_HEADERS="x-api-key=...,x-selected-account-id=...", or pass api_endpoint and headers to Traceloop.init().
  • LangSmith and LangChain need pip install "langsmith[otel]", LANGSMITH_TRACING=true to create runs at all, and LANGSMITH_TRACING_MODE=otel to export them over OpenTelemetry (older releases: LANGSMITH_OTEL_ENABLED=true plus LANGSMITH_OTEL_ONLY=true, otherwise runs also go to LangSmith). LangSmith reads only the generic OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS and uses the endpoint verbatim, so include /v1/traces. An already-configured global tracer provider takes precedence.
  • Claude Agent SDK and Claude Code keep telemetry opt-in and trace export in beta. Add CLAUDE_CODE_ENABLE_TELEMETRY=1, CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1, OTEL_TRACES_EXPORTER=otlp, and OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf to the variables above. Only the built-in claude_code.llm_request spans carry gen_ai.system and gen_ai.request.model, and none carry the semconv input, output, or token keys, so those land as completions with a model and nothing else while interaction and tool spans get no derived fields; the prompt is redacted unless OTEL_LOG_USER_PROMPTS=1. For full input, output, and token mapping, instrument the Agent SDK with otel-instrumentation-claude-agent-sdk instead.

Endpoint contract

The default SDK batch of 512 spans fits.

Errors

Service errors follow the OTLP/HTTP specification: a protobuf Status body with a message, and the HTTP status decides retry behavior. On mesh-fronted deployments an unauthenticated request may be answered by the mesh instead, as 403 with a plain-text RBAC: access denied body or 401 with an empty body.
  • 400: the whole request is rejected. Causes: a missing x-selected-account-id (including the legacy x-sgp-account-id in its place), non-protobuf content type, Content-Encoding other than gzip or identity, invalid gzip, empty body, more than 1,000 spans, an invalid span such as an all-zero trace ID or an end time before its start, or a malformed sgp.obs_trace_id or sgp.obs_span_id. Fix the request or producer; do not retry. With an OpenTelemetry Collector, set both send_batch_size: 1000 and send_batch_max_size: 1000; under load the defaults emit 8,192-span batches, which are rejected.
  • 401: invalid API key.
  • 403: the key is not a member of the account, or its role there is not admin, manager, or editor.
  • 413: body over the deployment limit. Lower the export batch size.
  • 429: rate limited. The ingress gateway’s limit is one bucket per gateway pod shared by every caller; it answers with a plain-text body and no Retry-After. The service’s own load shedding answers with an OTLP Status and Retry-After. The OTLP specification makes 429, 502, 503, and 504 retryable and exporters from 2026 onward follow it, but older ones differ: the Python OTLP/HTTP exporter up to 1.44.0 retries only 408 and 5xx, so a 429 drops the batch. Keep exporters current, stay within your deployment’s limit, or export through a Collector, which retries 429 with backoff.

How spans map to SGP

Every span keeps its OpenTelemetry identity and structure: trace and span IDs, parent, name, kind, status, timing, attributes, events, links, and resource and scope attributes. An UNSET status is stored as success, matching SGP SDK writes. The keys sgp.obs_trace_id and sgp.obs_span_id are reserved: they move into SGP’s observability correlation fields and must be valid W3C IDs. Spans with GenAI semantic convention attributes also get SGP’s tracing fields, so they render like SGP SDK spans. This is best effort and never rejects a span:
  • Input and output: gen_ai.input.messages and gen_ai.output.messages, then the indexed gen_ai.prompt.N and gen_ai.completion.N attributes, then gen_ai.prompt and gen_ai.completion, then framework-specific keys, then a tool span’s gen_ai.tool.call.arguments and gen_ai.tool.call.result, and finally the legacy gen_ai.content.* events.
  • Token usage: gen_ai.usage.input_tokens and gen_ai.usage.output_tokens, or the prompt_tokens and completion_tokens spellings.
  • Operation type: gen_ai.operation.name, then traceloop.span.kind, then rerank and vector-store signals, then a tool span (gen_ai.tool.name, gen_ai.tool.call.*) with no model or tokens (custom step), then gen_ai.system or gen_ai.provider.name (completion). A span with derived input or output and none of these is also a completion. The operations fetch_response, guardrail_check, and agent_handoff are custom steps, and so is unknown on a span with gen_ai.system or gen_ai.provider.name but no model, tokens, input, or output.
  • Model and provider metadata from the same keys are merged into the span’s attributes.
Any producer of standard gen_ai.* attributes gets these fields, including LangSmith and otel-instrumentation-claude-agent-sdk. If you use the opentelemetry-instrumentation-openai-agents-v2 bridge instead of the native integration, each agent’s name arrives on its invoke_agent span, and the bridge’s default name and ID (OpenAI Agent, agent) on its other spans are read as unknown. Its agent_name and agent_id arguments replace every agent’s name with one, so pass them only in a single-agent app, and not under opentelemetry-instrument, which instruments the bridge first and ignores a second call. SGP also reads Pydantic AI (pydantic_ai.*) and OpenLLMetry (traceloop.*, llm.*) keys, and OpenInference and Vercel AI SDK token counts. Spans with none of these keep their raw attributes and get no derived fields.

Migrating from the legacy forwarder

The opentelemetry.<deployment-url> forwarder is deprecated and being decommissioned. It spoke the same protocol, so migration is a host and header change: Do not mix the two. The native endpoint does not read the legacy headers: without x-api-key the request is rejected as unauthenticated by the service or, behind a mesh, as 403 RBAC: access denied; with x-api-key but only the legacy account header it is a 400 for the missing x-selected-account-id.

Viewing traces

Exported traces appear on the Traces page for the selected account. Open one to inspect its spans in the Trace Detail View.