Open sourceApache 2.0

ClickHouse Python analytics

Build ClickHouse analytics in Python once, then use it everywhere

Revenue, paying customers, and average order value start in one script and end up in jobs, notebooks, customer dashboards, and AI agents. hypequery lets you define each metric once in Python, next to your application code, so every consumer gets the same number.

Defined in

Python 3.11+

Used from

Jobs, APIs, React, agents

Tenancy

Enforced on every query

The same metric, five implementations

Revenue lives in a cron job, a notebook, an API handler, and a dashboard query. Each copy filters completed orders slightly differently, and the numbers stop matching.

Joins and ratios get rebuilt by hand

Revenue by customer plan needs a join. Average order value needs a guarded division. Every new report rewrites both, and every rewrite is a chance to get them wrong.

Customer-facing numbers need hard boundaries

Once analytics reach customers, a missing tenant filter is a data leak. A convention in SQL strings is not enough when several services query the same tables.

Define

Write the metrics as Python, next to your app

A dataset maps a ClickHouse table to named dimensions and measures. Filtered measures, derived ratios, and to-one joins are part of the definition, so callers ask for average_order_value instead of rebuilding it.

  • Sums, counts, exact distinct counts, percentiles, and filtered measures
  • Derived measures with null-safe division, like average order value
  • belongs_to relationships, so callers can group by customer.plan
  • Tenant and time keys declared once and enforced on every query

Dataset

Orders, with the metrics your product reports

analytics/datasets.py

The completed filter is defined once and shared by every measure that needs it.

Use

One client for jobs, notebooks, dashboards, and agents

Query measures by name with a time grain, and hypequery plans parameterized ClickHouse SQL with the join and the tenant predicate on both tables. Serve the same datasets over HTTP when a dashboard or agent needs them.

Results are cached under opaque, tenant-keyed entries, so a dashboard reloading the same chart does not rescan ClickHouse, and two customers never share an entry.

The HTTP endpoint authenticates every request and takes the tenant from the caller, not the request body. include_related lets callers group by customer.plan over HTTP, and client_profile plus the client-contract route are what generated React hooks and the MCP bridge read. See React and MCP clients for exporting the contract.

Consumers

A monthly report and an authenticated API, from one definition

analytics/app.py

ClickHouseConnection.from_env() reads CLICKHOUSE_URL or the CLICKHOUSE_* variables.

Where teams usually get stuck

Questions teams ask

ClickHouse analytics with Python

Use a driver like clickhouse-connect for ad hoc SQL. Use hypequery datasets for the metrics other code depends on, so there is one definition to review and change.

Product analytics in a Python backend

Serve tenant-scoped metrics from FastAPI, Django, Flask, or any ASGI or WSGI app, and render them with typed React hooks.

Not a BI tool

There is no separate server to run or YAML to maintain. Definitions are Python in your repository, reviewed and tested like the rest of your code.

What is TypeScript-only for now

Window measures (trailing, to-date, cumulative) and period comparisons are TypeScript only. The Python SDK is in beta; see feature status for the full list.

Next step

Move one shared metric into a dataset

Pick the number your team argues about most, define it once, and point one job and one dashboard at it.

Open source, and it runs in your own backend. Want it hosted?

Get early access to hypequery cloud.

Cloud hosts the same model you run in your backend, with a managed API and MCP server, versions, and rollback. We're letting teams in a few at a time. Join the waitlist and we'll reach out when your project can go live.

How do you want to build your analytics layer? (Optional)
What features do you most need? (Optional)

Pick as many as you like.

Only your email is required. All other questions are optional.