Skip to main content

The pattern

The metrics endpoint aggregates a table. If you group by the table’s primary key, every group contains exactly one record, so the “aggregate” is the record itself. Add the other columns you want to group_by and you have a row-level export with the same row-level security the dashboard applies. This is exactly how the Bilanc dashboard renders its own tables, PR drill-downs and the posthook session viewer, so anything you can see in the app you can pull this way. Three ingredients:
  1. A count metric for the table (see the table below). Counts don’t need an aggregation.
  2. The primary key plus the columns you want in group_by. Any column of the source table is allowed; see Tables and their columns.
  3. A date_field to window on, and start_date/end_date to pick the window.
Every row carries the count column (pr_count: 1 here) and, because no date_level was given, date_level_not_set: true. Both can be dropped by your pipeline.

Which metric and key to use per table

Some count metrics carry a definition. posthook-sessions-count only counts sessions that generated or committed AI code, so sessions with neither show posthook_sessions_count: 0 but are still returned. reviews-count counts distinct PRs, which is 1 per review row anyway. adopted-users counts active users, so inactive seat-days show 0. Set metric_min_value: 1 to keep only rows the dashboard would count, or leave it at 0 to export everything.

Paging through large tables

The single-metric endpoint caps limit at 200 rows and has no offset parameter. Two ways to get everything:
Shrink the window until each one returns fewer than 200 rows. Daily windows are usually enough; for very busy days add a member or repositories filter to split further.

Child records: fetch by parent id

Detail tables (posthook prompts, files and commits) are usually pulled per parent. Filter on the parent key and drop the date window down to the parent’s date range:
For PR-scoped tables use pr_ids; for issues use issue_ids; for CI use workflow_run_ids. Full worked examples for the posthook tables are on the Posthook Metrics page.

Incremental sync

The tables have no updated_at column exposed for change tracking, so the reliable approach is to re-pull a trailing window on each run (for example the last 3 days) and upsert on the primary key. Late-arriving data is common: PRs get merged days after creation, posthook commits are attributed after the session ends, and CI runs complete after they start. Choose the date_field that matches when your downstream cares about the record (e.g. pr_merged_at for merged PRs, date for sessions) and re-pull generously. Results are cached for five minutes per unique request body, and the underlying tables refresh hourly, so polling more often than hourly returns the same data.

Things to know

  • Row-level security applies. A key created by an Engineer exports only that engineer’s rows. Use an Owner’s key for an org-wide export.
  • team_id / team_name / department fan out. A person in two teams produces two rows for the same record. Group by team_names (the array) instead if you need one row per record with team context.
  • Durations are seconds and rates are 0–1. Interval columns such as coding_time come back as ISO-style strings when placed in group_by; the metric versions (cycle-time etc.) return seconds.
  • Text columns are safe to request. pr_body, comment, message_text and commit_message are returned in full.
  • Invalid group_by fields return a 400 that lists every valid column for the metric’s table. Use it as a live schema lookup.