Metrics & Monitoring
Monitor compression performance, cost savings, and system health with Headroom's built-in metrics, Prometheus endpoint, and SDK APIs.
Headroom provides comprehensive metrics for monitoring compression performance, cost savings, and system health through both the proxy server and the SDK.
Proxy Endpoints
Stats Endpoint
curl http://localhost:8787/stats{
"persistent_savings": {
"lifetime": {
"tokens_saved": 12500,
"compression_savings_usd": 0.04
}
},
"requests": {
"total": 42,
"cached": 5,
"rate_limited": 0,
"failed": 0
},
"tokens": {
"input": 50000,
"output": 8000,
"saved": 12500,
"savings_percent": 25.0
},
"cost": {
"total_cost_usd": 0.15,
"total_savings_usd": 0.04
},
"cache": {
"entries": 10,
"total_hits": 5
}
}Persistent savings are stored at ~/.headroom/proxy_savings.json and survive proxy restarts. Override the path with HEADROOM_SAVINGS_PATH.
Historical Savings
curl http://localhost:8787/stats-historyReturns durable compression history with hourly, daily, weekly, and monthly rollups. Supports CSV export:
curl "http://localhost:8787/stats-history?format=csv&series=daily"
curl "http://localhost:8787/stats-history?format=csv&series=monthly"Prometheus Metrics
curl http://localhost:8787/metrics# HELP headroom_requests_total Total requests processed
headroom_requests_total{mode="optimize"} 1234
# HELP headroom_tokens_saved_total Total tokens saved
headroom_tokens_saved_total 5678900
# HELP headroom_persistent_savings_tokens_saved_total Durable lifetime input tokens saved by proxy compression
headroom_persistent_savings_tokens_saved_total 5678900
# HELP headroom_compression_ratio Compression ratio histogram
headroom_compression_ratio_bucket{le="0.5"} 890
headroom_compression_ratio_bucket{le="0.7"} 1100
headroom_compression_ratio_bucket{le="0.9"} 1200
# HELP headroom_latency_seconds Request latency histogram
headroom_latency_seconds_bucket{le="0.01"} 800
headroom_latency_seconds_bucket{le="0.1"} 1150
# HELP headroom_cache_hits_total Cache hit counter
headroom_cache_hits_total 456OpenTelemetry (OTLP) Export
The proxy can also push its counters to any OTLP/HTTP endpoint. Install the extra and set four variables:
pip install "headroom-ai[proxy,otel]"HEADROOM_OTEL_METRICS_ENABLED=1
HEADROOM_OTEL_METRICS_ENDPOINT=http://127.0.0.1:4318/v1/metrics
HEADROOM_OTEL_SERVICE_NAME=headroom-proxy
HEADROOM_OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod| Variable | Default | Purpose |
|---|---|---|
HEADROOM_OTEL_METRICS_ENABLED | 0 | Enable Headroom-managed OTLP metric export |
HEADROOM_OTEL_METRICS_EXPORTER | otlp_http | otlp_http or console (local debugging) |
HEADROOM_OTEL_METRICS_ENDPOINT | unset | Full OTLP metrics URL — Headroom does not append /v1/metrics for you |
HEADROOM_OTEL_METRICS_HEADERS | unset | Comma-separated key=value auth headers |
HEADROOM_OTEL_METRICS_EXPORT_INTERVAL_MS | 10000 | Export interval |
HEADROOM_OTEL_SERVICE_NAME | headroom-proxy | OTEL service.name |
HEADROOM_OTEL_RESOURCE_ATTRIBUTES | unset | Comma-separated resource attributes |
Exported counters include headroom.proxy.requests, headroom.proxy.tokens.input, and
headroom.proxy.tokens.output. headroom.proxy.tokens.saved is the all-layer total:
message/compression savings plus tool-schema deferral savings. The component counter
headroom.proxy.tokens.tool_schema_saved exposes the deferral portion separately;
headroom.compression.tokens.saved remains the compression-pipeline component.
Confirm the exporter is live with curl -s http://localhost:8787/stats | jq .otel.
If your application already configures a global OTEL meter provider, leave HEADROOM_OTEL_* unset — Headroom records into the ambient provider automatically.
Dynatrace
Point the exporter at your environment's OTLP API and add the API token as a header. The token needs the metrics.ingest scope.
HEADROOM_OTEL_METRICS_ENABLED=1
HEADROOM_OTEL_METRICS_ENDPOINT="https://<env-id>.live.dynatrace.com/api/v2/otlp/v1/metrics"
HEADROOM_OTEL_METRICS_HEADERS="Authorization=Api-Token dt0c01.XXXX"
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA
HEADROOM_OTEL_SERVICE_NAME=headroom-proxyDelta temporality is not optional
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA is required. Dynatrace only ingests delta counters and rejects cumulative ones with UNSUPPORTED_METRIC_TYPE_MONOTONIC_CUMULATIVE_SUM, while the OTEL SDK default is cumulative. Without this line, every Headroom metric is dropped at ingest and the proxy logs no error.
Restart the proxy, then search the Dynatrace metric explorer for headroom.proxy.tokens.saved — data appears within ~30s.
For an ActiveGate deployment, swap the base URL for https://<activegate>:9999/e/<env-id>/api/v2/otlp/v1/metrics. If you already run an OpenTelemetry Collector, send Headroom to it instead and add the cumulativetodelta processor — then the temporality variable is unnecessary and the collector holds the token.
Trace export is separate: Headroom's self-configured tracing targets Langfuse only. To land its spans in Dynatrace, leave HEADROOM_LANGFUSE_* unset and run the proxy under opentelemetry-instrument with the standard OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_HEADERS / OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf variables; Headroom records into the ambient tracer provider.
Health Check
curl http://localhost:8787/health{
"status": "healthy",
"version": "0.1.0",
"uptime_seconds": 3600
}SDK Metrics
Proxy Stats
The TypeScript SDK queries the proxy for stats:
import { } from 'headroom-ai';
const = new ();
// Get proxy stats
const = await .proxyStats();
.(`Tokens saved: ${.tokens.saved}`);
.(`Savings: ${.tokens.savings_percent}%`);Compression Result Metrics
Every compress() call returns metrics:
import { } from 'headroom-ai';
const = await (messages, { : 'gpt-4o' });
.(`Tokens: ${.tokensBefore} -> ${.tokensAfter}`);
.(`Saved: ${.tokensSaved} (${(.compressionRatio * 100).(1)}%)`);
.(`Transforms: ${.transformsApplied.join(', ')}`);Session Stats
Quick stats for the current session (no database query):
stats = client.get_stats()
print(f"Mode: {stats['config']['mode']}")
print(f"Tokens saved: {stats['session']['tokens_saved_total']}")
print(f"Avg compression: {stats['session']['compression_ratio_avg']:.1%}")Returns:
{
"session": {
"requests_total": 10,
"tokens_input_before": 50000,
"tokens_input_after": 35000,
"tokens_saved_total": 15000,
"tokens_output_total": 8000,
"cache_hits": 3,
"compression_ratio_avg": 0.70,
},
"config": {
"mode": "optimize",
"provider": "openai",
"cache_optimizer_enabled": True,
"semantic_cache_enabled": False,
},
"transforms": {
"smart_crusher_enabled": True,
"cache_aligner_enabled": True,
"rolling_window_enabled": True,
},
}Historical Metrics
Query stored metrics from the database:
from datetime import datetime, timedelta
metrics = client.get_metrics(
start_time=datetime.utcnow() - timedelta(hours=1),
limit=100,
)
for m in metrics:
print(f"{m.timestamp}: {m.tokens_input_before} -> {m.tokens_input_after}")Summary Statistics
Aggregate statistics across all stored metrics:
summary = client.get_summary()
print(f"Total requests: {summary['total_requests']}")
print(f"Total tokens saved: {summary['total_tokens_saved']}")
print(f"Average compression: {summary['avg_compression_ratio']:.1%}")
print(f"Total cost savings: ${summary['total_cost_saved_usd']:.2f}")Logging
import logging
# INFO level shows compression summaries
logging.basicConfig(level=logging.INFO)
# DEBUG level shows detailed transform decisions
logging.basicConfig(level=logging.DEBUG)Example output:
INFO:headroom.transforms.pipeline:Pipeline complete: 45000 -> 4500 tokens (saved 40500, 90.0% reduction)
INFO:headroom.transforms.smart_crusher:SmartCrusher applied top_n strategy: kept 15 of 1000 items
DEBUG:headroom.transforms.smart_crusher:Kept items: [0,1,2,42,77,97,98,99] (errors at 42, warnings at 77)# Log to file
headroom proxy --log-file headroom.jsonl
# Increase verbosity
headroom proxy --log-level debugCost Tracking
Budget Alerts
Set a budget limit in the proxy:
headroom proxy --budget 10.00When the budget is exceeded, requests return a budget exceeded error, the /stats endpoint shows budget status, and logs indicate the budget state.
Measured vs Estimated Spend
Every cost record carries a basis — where its input-token count came from. When a provider response includes a usage breakdown, the basis is measured. When it doesn't, Headroom substitutes its own tokens_sent count so input cost isn't dropped from the budget, and the record's basis is estimated. Headroom logs one warning per model the first time this happens.
/stats keeps the two separable under cost.budget_basis:
{
"cost": {
"budget_limit_usd": 10.0,
"budget_period": "daily",
"budget_estimated_basis": "count",
"budget_basis": {
"total_usd": 3.1400,
"measured_usd": 2.9000,
"estimated_usd": 0.2400,
"estimated_pct": 7.6,
"records": 412,
"estimated_records": 31
}
}
}An estimate can drift in either direction, so you choose what it does to the hard limit:
headroom proxy --budget 10.00 --budget-estimated-basis count # default| Value | Effect |
|---|---|
count | Estimated spend consumes the budget like measured spend. The default; matches historical behavior. |
ignore | Estimated spend is still booked and reported, but only provider-reported spend consumes the budget. |
block | Refuse requests once the period holds any estimated spend, rather than enforcing a hard limit against a guess. |
Env: HEADROOM_BUDGET_ESTIMATED_BASIS. headroom doctor reports the estimated share alongside the budget check.
Key Metrics to Monitor
| Metric | What It Tells You | Target |
|---|---|---|
headroom_tokens_saved_total | Runtime tokens saved since this proxy process started | Higher is better |
headroom_persistent_savings_tokens_saved_total | Durable lifetime tokens saved from /stats.persistent_savings | Higher is better |
compression_ratio_avg | Efficiency | 0.7--0.9 typical |
cache_hit_rate | Cache effectiveness | >20% is good |
latency_p99 | Performance impact | <10ms |
failed_requests | Reliability | 0 |
Grafana Dashboard
A ready-to-import dashboard ships in
examples/grafana/headroom-dashboard.json.
Import it in Grafana (Dashboards → New → Import → Upload) and pick your Prometheus
datasource — it renders tokens saved, request throughput, and processing overhead
(headroom_overhead_ms_sum / headroom_overhead_ms_count) straight from the metrics
the /metrics endpoint exposes.
Example ad-hoc queries against the same metric family:
| Panel | PromQL |
|---|---|
| Runtime Tokens Saved | headroom_tokens_saved_total |
| Lifetime Tokens Saved | headroom_persistent_savings_tokens_saved_total |
| Compression Ratio (median) | histogram_quantile(0.5, headroom_compression_ratio_bucket) |
| Request Latency (p99) | histogram_quantile(0.99, headroom_latency_seconds_bucket) |
| Cache Hit Rate | headroom_cache_hits_total / (headroom_cache_hits_total + headroom_cache_misses_total) |
Savings Tracking
Durable, over-time compression savings — cost avoided plus Today / Last 7 days / All time and per-model / per-client breakdowns via `headroom savings`.
Simulation
Preview compression results without making an LLM call. Use simulation for cost estimation, debugging, and understanding waste signals.