◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-05-01Advanced

Claude API Telemetry on ClickHouse: A Production Guide to Cost, Latency, and Error Analytics

Stream per-request Claude API telemetry into ClickHouse, build sub-second dashboards with materialized views, and detect cost spikes, retry loops, and silent failures with practical SQL recipes.

claude-api83clickhouseobservability21analytics3production111

✦ Premium Article

About six months after putting the Claude API into production, I opened a monthly invoice and felt my hands tremble. Costs had ballooned to 4.2x the previous month. Tracing the root cause took two full days. If I had per-request telemetry on the application side back then, I could have found the offending endpoint in roughly thirty minutes.

This article shows how to push per-request Claude API usage, cost, and latency data into ClickHouse, build the kind of fast aggregation and anomaly detection that Postgres struggles with, and run all of it at production quality. We will cover safe ingestion from Cloudflare Workers into ClickHouse Cloud, materialized views that return dashboards in under one second, and practical SQL recipes for catching the silent failures that never show up on an Anthropic invoice — retry loops, hallucination outliers, and latency regressions.

Why ClickHouse — How It Differs From Postgres, BigQuery, and Snowflake

Most teams start by writing telemetry to Postgres. I did the same thing. One row per request inserted into an events table, then aggregate with SQL. It looks simple enough.

The cracks appear once API traffic crosses roughly one million calls per day. Aggregations like SELECT model, sum(input_tokens), sum(output_tokens) FROM events WHERE ts > now() - interval '1 day' GROUP BY model start taking thirty seconds or more, even with carefully chosen indexes. Postgres is optimized for OLTP — row-oriented transactional access — and that orientation works against you when you need columnar aggregation.

The strengths of ClickHouse that matter most for telemetry, in priority order: columnar storage that returns SUM, AVG, and quantile() over hundreds of millions of rows in single-digit seconds; specialized engines like ReplacingMergeTree and AggregatingMergeTree that let you declare deduplication and pre-aggregation in DDL; materialized views that act as live aggregation tables — the foundation for sub-second dashboards; and high compression ratios — telemetry data has lots of repetition, and 10x compression is normal.

BigQuery and Snowflake are also columnar, but their slot startup latency (a few seconds to over ten) and per-query pricing make them awkward for the "frequently re-queried dashboard backend" use case. ClickHouse trades a richer SaaS feature set for sheer real-time responsiveness, which is exactly what you want for ops dashboards, alert evaluation, and Slack-bot reporting.

My personal heuristic, weighing team size, budget, and ops overhead: if you are running more than a million Claude API calls per day, ClickHouse is the first option I reach for. Below that volume, Postgres with a thoughtfully designed rollup table can still serve you well — pick to your scale.

What to Measure and the Minimal Schema That Actually Works

"Just put everything in" leads to schema bloat. Decide upfront on the minimum set of fields that will pay for themselves in production.

After running this schema across four products, here is what I recommend.

CREATE TABLE claude_api_events (
    -- Identifiers
    event_id UUID,
    request_id String,        -- Anthropic request-id header
    user_id String,           -- App-level user identifier
    workspace_id LowCardinality(String),  -- Multi-tenant support
 
    -- Time and model info
    ts DateTime64(3, 'UTC'),  -- Millisecond precision
    model LowCardinality(String),
    api_version LowCardinality(String),
 
    -- Request and response
    input_tokens UInt32,
    output_tokens UInt32,
    cache_read_tokens UInt32 DEFAULT 0,
    cache_write_5m_tokens UInt32 DEFAULT 0,   -- 5-minute TTL writes (1.25x input)
    cache_write_1h_tokens UInt32 DEFAULT 0,   -- 1-hour TTL writes (2x input)
    stop_reason LowCardinality(String),
 
    -- Performance
    latency_ms UInt32,
    ttft_ms UInt32,           -- Time To First Token
    streaming UInt8,
 
    -- Outcome
    success UInt8,
    error_type LowCardinality(String) DEFAULT '',
    http_status UInt16,
 
    -- Application-layer context
    feature_id LowCardinality(String),  -- "summarize", "translate", etc.
    prompt_template_id LowCardinality(String),
    prompt_version UInt32 DEFAULT 0,
 
    -- Precomputed cost keeps dashboard queries cheap
    cost_usd Decimal64(6) DEFAULT 0,
    cost_source LowCardinality(String) DEFAULT 'priced',   -- priced / unpriced
    inference_geo LowCardinality(String) DEFAULT 'global'  -- global / us (us = 1.1x)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(ts)
ORDER BY (workspace_id, feature_id, ts)
TTL ts + INTERVAL 18 MONTH
SETTINGS index_granularity = 8192;

Four design decisions deserve emphasis.

First, pick the columns where LowCardinality(String) actually earns its keep. Columns with a small distinct-value count — model name, feature_id — should be declared LowCardinality. Compression improves and GROUP BY runs faster. After switching, our storage footprint shrank by about 35 percent.

Second, your ORDER BY choice drives query performance. ClickHouse uses the leading prefix of ORDER BY as a primary key for partial scans. The order "tenant → feature → time" is tuned for the typical dashboard query: "the last 24 hours of feature X for tenant Y."

Third, precompute cost at insertion time. With cost_usd already populated, dashboard queries collapse into a single SUM. Pricing changes can be absorbed by materialized views, as we will see below.

Fourth, store cache writes split by TTL. I kept them in a single column for about six months, and I may have been the last person on the team to notice. Prompt caching bills a 5-minute write at 1.25x the input rate, a 1-hour write at 2x, and a cache read at 0.1x. Collapse the two write types into one column and there is no multiplier that gives you the right answer. The API already hands you the split — usage.cache_creation carries ephemeral_5m_input_tokens and ephemeral_1h_input_tokens — so mirror that split in the table from day one.

The last two columns, cost_source and inference_geo, exist for the reconciliation section later on. The first records whether you should trust the number in the row at all; the second records whether the request travelled a path that carries a 1.1x multiplier.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Build the per-request cost tracking foundation that prevents the dreaded 'last month's API bill suddenly tripled' incident
✦Move from a Postgres telemetry table that chokes on aggregation queries to a ClickHouse + materialized view setup that returns dashboards in under one second
✦Learn concrete SQL patterns to detect hallucination outliers, retry storms, and latency regressions, so you catch incidents before users report them
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ API & SDK2026-04-30
Building a Production Claude API Pipeline on Cloudflare Queues: Fault Tolerance, Backpressure, and Cost Control
A practical, code-first walkthrough for routing Claude API calls through Cloudflare Queues — covering producer/consumer code, retry-vs-DLQ branching, priority lanes, and token budgeting for production workloads.
⬡ API & SDK2026-04-29
Production Semantic Cache for Claude API — Similarity Thresholds, Pollution Defense, and What to Track
A production playbook for adding a semantic cache in front of Claude API — threshold tuning, multi-tenant isolation, pollution prevention, fallbacks, how it stacks with Anthropic's prompt caching, and the metrics that actually prove it works.
⬡ API & SDK2026-05-26
Stabilizing Claude API Structured Responses in Production — Notes on tool_use, JSON Schema, and Layered Validation
Getting Claude to return JSON takes a few lines. Keeping that JSON usable in production is a different problem. Here is the layered design I landed on after running a wallpaper classification pipeline through Claude API, built around tool_use, JSON Schema, and domain validation.