Appearance
OpenRouter
OpenRouter's Broadcast sends a trace for every API request you make through OpenRouter. Point it at simpledatalogs and each LLM call becomes one llm_call event, with the model, provider, tokens, cost in USD and latency as properties. Your app's code doesn't change.
Prompts and completions are never stored.
Set it up
- In simpledatalogs, create a secret key (
sdl_sec_…) on the deployment the calls should count under, such as a new deployment namedopenrouter. - In OpenRouter, open Settings → Observability and turn on Enable Broadcast. In an organization, you need the admin role.
- Click the edit icon next to Webhook and fill in:
| Field | Value |
|---|---|
| URL | https://edge.simpledatalogs.com/openrouter/v1/traces |
| Method | POST |
| Headers | {"Authorization": "Bearer sdl_sec_…"} |
- Under Privacy, turn on Privacy Mode. simpledatalogs drops prompts and completions either way, and with Privacy Mode they never leave OpenRouter.
Keep Privacy Mode on
A trace over 512 KB is refused and lost. Long prompts and agent transcripts get there quickly; without them, a trace is a few KB.
- Optional: under Additional generation metadata, turn on Cost to add the billed tier, BYOK and estimate flags. Costs and tokens come without it.
- Click Test Connection, then save. OpenRouter saves only when the test passes.
- Make one request through OpenRouter. An
llm_callevent shows up in Logs within a minute.
OpenRouter's test trace (a made-up GPT-4 Turbo call costing $0.02) arrives as llm_call_test instead, so it never adds to your llm_call spend.
OpenTelemetry Collector works too
OpenRouter's OpenTelemetry Collector destination sends the same traces. Use the same URL as its Endpoint and the same headers.
To send only some keys' calls, pick them under the destination's API keys, or exclude the ones to leave out. Sampling under 1.0 sends fewer traces, so counts and spend drop with it.
What's stored
One llm_call event per generation. Provider retries and fallbacks inside one call don't count twice.
| Property | What |
|---|---|
model | The model that answered, such as openai/gpt-4o-2024-08-06 |
requested_model | The model you asked for, only when routing picked another |
provider, provider_slug | Who served it, such as OpenAI and openai |
input_tokens, output_tokens, total_tokens | Token counts |
cached_tokens, cache_write_tokens, reasoning_tokens | When the provider reports them |
cost_usd | What OpenRouter charged for the call, in USD, fees included |
input_cost_usd, output_cost_usd | The prompt and completion parts of the cost |
input_unit_price_usd, output_unit_price_usd | The price per token OpenRouter charged |
duration_ms, ttft_ms, inter_token_latency_ms | Total time, time to first token, and the mean gap between tokens |
status, error, finish_reason | ok or error, the error message, and why the answer ended (stop, length …) |
generation_id | OpenRouter's generation id, to look the call up in OpenRouter |
api_key_name | Which OpenRouter key made the call |
service_tier, is_byok, usage_is_estimated, cancelled | With Cost on: the tier it was billed at, whether it used your own provider key, whether OpenRouter had to estimate usage, and whether the call was cancelled |
span_name, trace_id, span_id | The trace's names and ids |
gateway | Always openrouter |
- A failed call is an
error-level event, so it shows in error counts and alerts. - The request's
userbecomes the event's user, andsession_idits session. An email asuseris pseudonymized like any email user id. - Flat values in the request's
traceobject ("trace": {"feature": "support"}) become properties too, up to 20. - Prompts, completions, messages, tool definitions and the provider's raw usage are always dropped.
- A resent trace lands on the same event, so nothing counts twice.
Chart your spend
Add a board with these widgets:
- Number, event
llm_call, measure sum ofcost_usd: what you spent in the range. - Stacked bars, event
llm_call, split bymodel(orprovider): calls per model over time. - Number, measure avg of
duration_ms: how slow calls are. - Number, measure rate, condition
statusiserror: the share of calls that failed.
The Spend tab prices calls by a fixed unit cost. OpenRouter already sends the real cost, so chart cost_usd on a board instead.
Responses
200 {}once the trace is stored, including for Test Connection's empty payload.401with a wrong or missing key.- Other refusals are OTLP's, with
Retry-After.
Each LLM call counts as one event.