Querying your data

Your traces live in your database, so your own views of them can look like your product. bc.api is the headless, typed query surface — call it from a Server Component, a route handler, or anywhere server-side. No HTTP, no client.

const { items, nextCursor } = await bc.api.listSessions({
  environment: "production",
  status: "error",
  limit: 50,
});

Methods

MethodReturns
listTraces(filter?)Page<TraceSummary> — one row per run, newest first.
listSessions(filter?)Page<SessionSummary> — traces grouped by session.
listRuns({ sessionKey })RunSummary[] — the runs within a session.
getTrace({ id })SpanRecord[] — every span in a trace.
getSpan({ id })SpanRecord | null.
stats(filter?)Stats — runs, error rate, cost, tokens, latency.
costSummary(opts?)CostSummary — the Cost view’s data.
listEnvironments()string[].

Filters & pagination

Every list and stats call takes the same filter. Each dimension selects traces (a trace matches if any of its spans qualifies), so a filtered list still aggregates each trace’s full span set — counts and cost stay whole.

type TraceFilter = {
  environment?: string;
  userId?: string;
  model?: string;
  status?: "ok" | "error";
  since?: number; // epoch ms
  until?: number;
};

Lists are keyset-paginated: pass limit and the nextCursor from the previous page. nextCursor is null at the end.

Beyond the built-in queries

bc.api is a curated surface. When you need an aggregation it doesn’t cover — “cost by tenant,” “p95 by model” — remember it’s your database. Query the breadcrumb_spans table directly with your own Drizzle/Prisma/pg client. A trace is spans sharing a trace_id; the root is the span with parent_span_id IS NULL, or — when another tracer owns the span above yours and it never reaches breadcrumb — the trace’s earliest span; a session is session_id. Group cost and latency by function_id to attribute them to a named call wherever it sits. Migrations are additive-only, so the schema is a stable contract.

To read the same data from the browser instead of the server, use the typed client and React hooks. To add a view to the shipped dashboard rather than replace it, see custom pages.