REST API
Usage, Latency & Cost
Workspace-level rollups. These power the cost and speed dashboard.
Usage summary
GET /v1/observ/orgs/{org}/workspaces/{ws}/usage/summary
Headline totals for one period — traces, tokens, cost, error rate.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
period | string | ➖ | day | day | week | month |
Response 200 — not schema-modelled
The payload is data, containing totals plus two breakdowns:
{
"data": {
"period": "day",
"workspace_id": "3f2b1c8e-9a4d-4f11-b7c2-1e5a8d3f6b90",
"totals": { "trace_count": 12480, "cost_usd": 412.87 },
"by_model": [ { "model": "claude-opus-4-6", "cost_usd": 311.02 } ],
"by_project": [ { "project_id": "checkout-agent", "cost_usd": 208.44 } ]
},
"meta": { "request_id": "9f2c4b1e…", "scorer_version": "2.1.0" }
}
Exact keys inside
totals,by_model, andby_projectare computed and not schema-modelled — inspect one live response before typing against them.
Examples
r = evigauge.get("/v1/observ/orgs/{org}/workspaces/{ws}/usage/summary",
params={"period": "week"})
d = r["data"]
print(d["totals"])
for row in d["by_model"]:
print(" ", row)
const r = await evigauge.get(
"/v1/observ/orgs/{org}/workspaces/{ws}/usage/summary", { period: "week" });
const d = r.data;
console.log(d.totals);
d.by_model.forEach((row: any) => console.log(" ", row));
Usage timeseries
GET /v1/observ/orgs/{org}/workspaces/{ws}/usage/timeseries
Bucketed usage over time, for charting.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
period | string | ➖ | day | day only. v1 emits one row per calendar day; week / month return 400. |
days | integer | ➖ | 30 | How many days back to cover |
Unlike the other rollup endpoints, this one accepts only
period=day. Aggregate to weeks or months client-side from the daily buckets.
Response 200 — not schema-modelled
{
"period": "day",
"days": 30,
"points": [
{ "ts": "2026-08-03T00:00:00Z", "trace_count": 401, "total_tokens": 610233, "total_cost_usd": 13.94 }
]
}
Examples
ts = evigauge.get("/v1/observ/orgs/{org}/workspaces/{ws}/usage/timeseries",
params={"period": "day", "days": 14})
for p in ts["points"]:
print(p["ts"][:10], f"${p['total_cost_usd']:.2f}")
const ts = await evigauge.get(
"/v1/observ/orgs/{org}/workspaces/{ws}/usage/timeseries", { period: "day", days: 14 });
ts.points.forEach((p: any) =>
console.log(p.ts.slice(0, 10), `$${p.total_cost_usd.toFixed(2)}`));
Latency by phase
GET /v1/observ/orgs/{org}/workspaces/{ws}/usage/latency
Latency percentiles broken down by execution phase. Tells you which stage is slow, not just that the whole trace is slow.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
period | string | ➖ | day | day | week | month |
Response 200 — not schema-modelled
{
"period": "day",
"phases": [
{ "phase": "retrieval", "p50_ms": 210, "p95_ms": 890, "p99_ms": 1400, "call_count": 12480, "error_count": 12 },
{ "phase": "llm_call", "p50_ms": 940, "p95_ms": 5200, "p99_ms": 9100, "call_count": 51203, "error_count": 340 }
]
}
Examples
lat = evigauge.get("/v1/observ/orgs/{org}/workspaces/{ws}/usage/latency",
params={"period": "day"})
worst = max(lat["phases"], key=lambda p: p["p95_ms"])
print(f"Slowest phase: {worst['phase']} @ p95 {worst['p95_ms']}ms")
const lat = await evigauge.get(
"/v1/observ/orgs/{org}/workspaces/{ws}/usage/latency", { period: "day" });
const worst = lat.phases.reduce((a: any, b: any) => (b.p95_ms > a.p95_ms ? b : a));
console.log(`Slowest phase: ${worst.phase} @ p95 ${worst.p95_ms}ms`);
Phase trace drill-down
GET /v1/observ/orgs/{org}/workspaces/{ws}/usage/latency/phase/traces
The individual traces behind one phase's percentile or error count. This is the drill-down from a slow bar in the latency chart to the actual traces that caused it.
Auth: API key or JWT · Role: viewer
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
phase | string | ✅ | — | Phase name, exactly as returned by /usage/latency |
metric | string | ✅ | — | errors (a span in the phase errored) or slow (span duration ≥ the phase's live percentile) |
period | string | ➖ | day | day | week | month |
percentile | string | ➖ | p95 | p95 | p99. Applies only when metric=slow. |
limit | integer | ➖ | 100 | Max traces returned, 1–500 |
The values are
errorsandslow— noterror/latency. An invalidmetric,period, orpercentilereturns400with the accepted list indetail.
threshold_msis computed over the same population as the latency panel, so the number matches what the chart showed. An emptytracesarray is a valid healthy state, not an error.
Response 200 — not schema-modelled
{
"phase": "llm_call",
"metric": "slow",
"percentile": "p95",
"threshold_ms": 5200,
"traces": [
{ "trace_id": "4bf92f...", "duration_ms": 9840, "start_time": "2026-09-01T14:23:05Z", "status": "ok" }
]
}
Examples
slow = evigauge.get(
"/v1/observ/orgs/{org}/workspaces/{ws}/usage/latency/phase/traces",
params={"phase": "llm_call", "metric": "slow", "percentile": "p99", "limit": 20},
)
print(f"Traces above {slow['threshold_ms']}ms:")
for t in slow["traces"]:
print(" ", t["trace_id"], t["duration_ms"])
const slow = await evigauge.get(
"/v1/observ/orgs/{org}/workspaces/{ws}/usage/latency/phase/traces",
{ phase: "llm_call", metric: "slow", percentile: "p99", limit: 20 },
);
console.log(`Traces above ${slow.threshold_ms}ms:`);
slow.traces.forEach((t: any) => console.log(" ", t.trace_id, t.duration_ms));
Errors
| Status | Cause |
|---|---|
400 | Unknown phase, or invalid metric / period / percentile value |
422 | phase or metric omitted — both are required |