19 integrations
Whatever writes it, one table reads it.
Four shapes reach the same log: wrap a client, hand us a finish handler, point an OpenTelemetry exporter here, or drive it yourself. Every card below shows the row that arrives — the action names are the ones the code actually writes, not an illustration.
Wrap the client
7 of them.
One line at construction. Every call through the client is timed, priced and written — including the ones that throw, because a failed call still costs latency and often tokens.
OpenAI
Chat, responses and embeddings. Reads both token namings.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const openai = nitroping.observeOpenAI(new OpenAI())Writes
openai.chat gpt-5 info $0.00421204ms Anthropic
Cache writes count as input; cache reads bill at the cache rate.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const anthropic = nitroping.observeAnthropic(new Anthropic())Writes
anthropic.chat claude-sonnet-5 info 6.7k cached2870ms Google Gemini
The cached share is subtracted so a hit isn't billed twice.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const genai = nitroping.observeGoogleGenAI(new GoogleGenAI({ apiKey }))Writes
google.chat gemini-2.5-pro info $0.01181980ms Amazon Bedrock
Converse and InvokeAgent. InvokeModel passes through untraced.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const bedrock = nitroping.observeBedrock(new BedrockRuntimeClient({ region }))Writes
bedrock.chat anthropic.claude-sonnet-4 info 2140ms OpenRouter
Uses OpenRouter's own reported cost over an estimate.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const client = nitroping.observeOpenRouter(new OpenAI({ baseURL, apiKey }))Writes
openrouter.chat meta-llama/llama-4-maverick info $0.0006890ms LiteLLM
The proxy speaks OpenAI's protocol; only the label differs.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const litellm = nitroping.observeLiteLLM(new OpenAI({ baseURL, apiKey }))Writes
litellm.chat gpt-5-mini info 740ms Azure OpenAI
Same wire shape as OpenAI; the label keeps the spend separable.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const azure = nitroping.observeOpenAI(new AzureOpenAI({ ... }), "azure")Writes
azure.chat gpt-5 info 1310ms
Finish handler
4 of them.
The one path that covers streaming: the usage totals only exist once the stream has ended, so the handler runs there. A tool loop is counted as one call per step.
Vercel AI SDK
Counts every step of a tool loop. More than one step is an agent run.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) await streamText({ model, prompt, onFinish: nitroping.onFinish() })Writes
vercel-ai.chat gpt-5 info 1 step3050ms vercel-ai.agent gpt-5 info 4 steps8410ms TanStack AI
Compatible finish event — the same handler.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) onFinish: nitroping.onFinish()Writes
vercel-ai.chat claude-haiku-4-5 info 620ms Eve
Compatible finish event — the same handler.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) onFinish: nitroping.onFinish()Writes
vercel-ai.chat gpt-5-mini info 580ms Mastra
Runs on the AI SDK underneath, so usage arrives the same way.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) onFinish: nitroping.onFinish({ provider: "mastra" })Writes
mastra.chat claude-sonnet-5 info 2410ms
OpenTelemetry
7 of them.
No SDK at all: two environment variables, sometimes a third, and the spans and metrics become audit events on arrival. The action on the row is whatever the exporter named the span.
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY"The key is an ingest key from Settings → API keys — the same one the SDK uses. The endpoint ends at /v1/otlp on purpose: an exporter appends /v1/traces and /v1/metrics itself, so the doubled path on the wire is expected rather than a typo. Logs are accepted and discarded, so a tool exporting all three won't error. This line is shown once for reference — the card for your tool below already has it, so there is nothing to combine by hand.
Never sent anywhere — this page has no backend. It only replaces $NITROPING_KEY in what the copy buttons below put on your clipboard, so the block you paste into your shell is already complete.
If a card below doesn't already have this
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufThis endpoint speaks OTLP over HTTP — http/protobuf or http/json, both read the same. It does not speak gRPC at all. Add this line yourself only if your tool's card doesn't include it and nothing arrives after setup — that means its exporter picked gRPC, or has no default and picked nothing.
Claude Code
Reports tokens and cost as metrics, not spans. Both are handled.
Its docs state it has no default protocol, so it stays inert until one is named — the line below is already in the snippet. Traces are off until you ask for them separately.
ready to paste — .env above includedOTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY" OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf export CLAUDE_CODE_ENABLE_TELEMETRY=1 export OTEL_METRICS_EXPORTER=otlp export OTEL_LOGS_EXPORTER=otlp # optional: per-tool spans as well as the token metrics export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 export OTEL_TRACES_EXPORTER=otlpWrites
claude_code.token.usage claude-opus-4 info 5.0k input claude_code.token.usage claude-opus-4 info 900 cached oh-my-pi
Nothing beyond the shared block: export starts as soon as an endpoint is set.
Does not need the protocol line: its runtime enables http/protobuf and nothing else, so it is already right. Naming any other protocol disables that signal rather than switching it. OTEL_SDK_DISABLED=true turns the whole thing off.
ready to paste — .env above includedOTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY"Writes
agent turn info root span4000ms chat claude-opus-5 claude-opus-5 info 4.2k / 8103000ms OpenClaw
Standard GenAI semantic-convention spans.
If nothing arrives, name the protocol: an exporter that defaults to gRPC has nowhere to send it here.
ready to paste — .env above includedOTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY"Writes
chat gpt-5 gpt-5 info 1620ms Hermes Agent
Standard GenAI semantic-convention spans.
If nothing arrives, name the protocol: an exporter that defaults to gRPC has nowhere to send it here.
ready to paste — .env above includedOTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY"Writes
chat claude-sonnet-5 claude-sonnet-5 info 2260ms Pydantic AI
Built-in OTel support — just point it at the endpoint.
Python's OTLP exporter is commonly wired for gRPC, so this is the one most likely to need the protocol line.
ready to paste — .env above includedOTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY"Writes
agent run gpt-5 info 5120ms LangChain / LangGraph
Via OpenInference or OpenLIT. Graph parent links are kept.
Same Python exporter caveat: name the protocol if the traces never show up.
ready to paste — .env above includedOTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY" pip install openinference-instrumentation-langchainWrites
ChatOpenAI gpt-5 info nested1440ms CrewAI
Via OpenLIT, or use the manual agent API for finer control.
Same Python exporter caveat: name the protocol if the traces never show up.
ready to paste — .env above includedOTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.nitroping.dev/v1/otlp OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $NITROPING_KEY" pip install openlitWrites
crew.kickoff gpt-5 info 12800ms
Manual
1 of them.
For a loop you wrote yourself. The run opens a trace, every tool call nests under it, and closing the run records its total duration — so the waterfall shows where the time went rather than one long span.
Custom agents
Any hand-rolled loop. Tool calls nest under the run as one trace.
setupimport { NitroPing } from "nitroping" const nitroping = new NitroPing({ apiKey: process.env.NITROPING_KEY! }) const run = nitroping.startAgentRun({ name: "researcher" }) await run.tool("web_search", () => search(query)) run.end()Writes
agent.tool.web_search web_search info child820ms agent.agent.researcher researcher info root6340ms
Nothing listed
Then send the event yourself.
The list above is convenience, not capability. Anything that can POST JSON writes to the log, and the panel treats a hand-written event exactly like a wrapped one.
await fetch("https://ingest.nitroping.dev/v1/events", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NITROPING_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify([{ actor_id: "u_8f31c2", action: "invoice.void" }]),
})The full field list is in the API reference and in llms.txt, which is written for an agent to read on your behalf.
Missing one
Ask for it in the open.
The wrappers, the finish handlers and the OTel conventions all live in one repository. That is where an integration that is missing, a snippet on this page that no longer works, or a provider whose token naming we read wrong should be raised — in public, against the code, rather than in a support thread nobody else can read.
Pull requests are the fastest route for a new provider: a wrapper is a usage reader and a price table, and the existing ones are the template.