Send telemetry
Point any OpenTelemetry exporter at Ratel Cloud. No Ratel SDK needed.
Ratel Cloud ingests standard OpenTelemetry traces. Any agent that emits LLM spans can send them, in any language.
Using the Ratel SDK already? See Observe your agent instead.
What you need
- A project API key. Create one in your project's Settings > API keys on cloud.ratel.sh.
- An OTLP/HTTP trace exporter. Protobuf and JSON both work; gRPC does not.
| Setting | Value |
|---|---|
| Endpoint | https://cloud.ratel.sh/v1/traces |
| Header | Authorization: Bearer <your-key> |
| Protocol | http/protobuf or http/json |
Configure with environment variables
Most OpenTelemetry SDKs read these at startup:
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://cloud.ratel.sh/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer%20$RATEL_API_KEY"
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="http/protobuf"
# Send message content so Cloud can break down what fills the context:
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT="true"The space after Bearer is URL-encoded as %20.
Or configure in code
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http \
opentelemetry-instrumentation-openai-v2import os
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
provider = TracerProvider()
provider.add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(
endpoint="https://cloud.ratel.sh/v1/traces",
headers={"Authorization": f"Bearer {os.environ['RATEL_API_KEY']}"},
)
)
)
trace.set_tracer_provider(provider)
OpenAIInstrumentor().instrument()Every OpenAI call is now traced. Set OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true to include message content.
npm install @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-protoimport { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { generateText } from "ai";
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "https://cloud.ratel.sh/v1/traces",
headers: { Authorization: `Bearer ${process.env.RATEL_API_KEY}` },
}),
});
sdk.start();
const { text } = await generateText({
model,
prompt: "Summarize the open tasks",
experimental_telemetry: { isEnabled: true },
});The AI SDK records prompts and tool calls by default. Cloud reads its ai.* attributes directly.
Any other emitter that follows the OpenTelemetry GenAI semantic conventions works the same way.
What Cloud reads
| You get | From | Needs |
|---|---|---|
| Tokens, cost, model, latency | gen_ai.usage.*, gen_ai.request.model, gen_ai.provider.name | Nothing extra |
| Context breakdown: user input, history, tools, skills, memory | Message content | Content capture on |
| Tool calls | execute_tool spans and tool-call messages | Nothing extra |
| Unused tools | gen_ai.tool.definitions | Your instrumentation emitting the offered tools |
Without content capture, totals stay exact and the breakdown counts everything as user input.
Content capture sends prompts and responses to Cloud. Turn it on only if that is acceptable for your data.
Check it works
Send one test span:
curl -s https://cloud.ratel.sh/v1/traces \
-H "Authorization: Bearer $RATEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "resourceSpans": [{ "scopeSpans": [{ "spans": [{
"traceId": "0123456789abcdef0123456789abcdef", "spanId": "0123456789abcdef",
"name": "chat gpt-4o", "kind": 3,
"startTimeUnixNano": "1751800000000000000", "endTimeUnixNano": "1751800001200000000",
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "chat" } },
{ "key": "gen_ai.provider.name", "value": { "stringValue": "openai" } },
{ "key": "gen_ai.request.model", "value": { "stringValue": "gpt-4o" } },
{ "key": "gen_ai.usage.input_tokens", "value": { "intValue": "1200" } },
{ "key": "gen_ai.usage.output_tokens", "value": { "intValue": "80" } }
] }] }] }] }'A 202 with {"accepted":1,"calls":1,"spans":1} means Cloud stored it. The call then shows on your project's dashboard.
Limits and errors
- Up to 4 MB per export.
- LLM calls per month: Free 50,000, Plus 500,000, Pro 5,000,000, Enterprise unlimited.
| Status | Meaning |
|---|---|
202 | Accepted. |
401 | Missing, invalid, or revoked API key. |
413 | Export over 4 MB. Lower your batch size. |
429 | Monthly call limit reached. Upgrade in Billing. |