chmonitor
GuideFeatures

Query Insights API

Programmatic access to slow query patterns — normalized_query_hash aggregation, calls, duration percentiles, and per-pattern executions.

Read the same normalized query-pattern data the Slow Query Patterns page shows, from two REST endpoints — a list of patterns and a per-pattern detail with recent executions.

Prop

Type

What it does

system.query_log rows are aggregated by normalized_query_hash (mirroring ClickHouse Cloud's Query Insights "Slow Patterns" table) — one row per distinct query shape, with call counts, duration percentiles (p50/p95/p99), resource usage (memory, rows/bytes read, rows/bytes written), and error counts. The list endpoint returns this aggregation; the detail endpoint returns one pattern plus the individual system.query_log rows (executions) that contributed to it, most-recent first.

Both endpoints reuse the exact SQL the Slow Query Patterns page runs (buildQueryPatternsSql in lib/query-config/queries/slow-query-patterns.ts) — the numbers always match what the page shows.

Pages

PageRouteWhat it showsSystem tables
Slow Query Patterns/slow-query-patternsThe same pattern aggregation, with filtering; clicking a row opens a detail flyout (aggregate stats, recent executions, notable runs, an Explain/Explorer shortcut, and an Advisor recommendation for that pattern)system.query_log
Recent Queries/recent-queriesReverse-chronological, non-aggregated system.query_log — the per-execution counterpart to Slow Query Patterns, sharing the same time/user/query-kind/database/client filterssystem.query_log

Using it

List patterns

GET /api/v1/insights/query-patterns?hostId=0&range=24&sort=total_duration:desc

Prop

Type

{
  "success": true,
  "data": [
    {
      "normalized_query_hash": "1234567890123456789",
      "normalized_query": "SELECT * FROM t WHERE id = ?",
      "calls": 42,
      "total_duration": 12.5,
      "avg_duration": 0.3,
      "p50_duration": 0.25,
      "p95_duration": 0.8,
      "p99_duration": 1.1,
      "max_duration": 1.4,
      "errors": 0,
      "user": "default",
      "query_kind": "Select",
      "database": "default"
    }
  ],
  "metadata": { "queryId": "...", "duration": 45, "rows": 1, "host": "0", "sql": "..." }
}

Get one pattern + its recent executions

GET /api/v1/insights/query-patterns/1234567890123456789?hostId=0&range=24

Prop

Type

{
  "success": true,
  "data": {
    "pattern": { "normalized_query_hash": "1234567890123456789", "calls": 42, "total_duration": 12.5 },
    "executions": [
      { "event_time": "2026-07-04 10:00:00", "query_id": "...", "query_duration_ms": 310, "user": "default" }
    ],
    "notable": [
      { "event_time": "2026-07-03 22:00:00", "query_id": "...", "query_duration_ms": 4200, "reason": "slowest" }
    ]
  },
  "metadata": { "host": "0", "rangeHours": 24, "rows": 1 }
}

executions is capped at 200 rows, most-recent first. notable is a separate, server-ranked query (5 rows per category: slowest by duration, largest_result by result rows, errored by exception, most-recent first) over the full rangeHours window — not a client-side slice of executions — so it stays correct for patterns with more than 200 executions in the window.

MCP / programmatic consumption

Both endpoints return plain JSON ({ success, data, metadata } / error { success: false, error: { type, message } }) with no HTML or client-only shaping, so they can be called directly by scripts, the AI agent's query tool (as system.query_log SQL), or any external MCP client that talks to /api/mcp — the AI agent itself has no dedicated tool for these endpoints (it reaches the same data with the general-purpose query tool), but the endpoints are safe to call from outside the dashboard.

Notes & limitations

Requires query logging

All data comes from system.query_log. If query logging is disabled, both endpoints return an empty pattern list / 404s.

  • normalized_query_hash values are ClickHouse UInt64 — always compare/pass them as strings to avoid JS float precision loss.
  • The range window is capped at 90 days (2160 hours) to keep the underlying system.query_log scan bounded.

On this page