POST /v1/events.query runs a read-only ClickHouse SQL query over your usage events and rollups. It streams compressed newline-delimited JSON using the same AnalyticsQueryLine contract as analytics.query, but the SQL dialect is ClickHouse, not DuckDB.
Each query sees only your organization and the environment of the key you use: a sandbox key reads sandbox events, and a live key reads live events.
This endpoint is disabled by default. Contact support@useautumn.com to enable it. Its limits and concurrency pool are separate from
analytics.query.Available tables
Always aggregate rollup rows with
GROUP BY; multiple rows can represent the same group before background merges. Daily and monthly columns are aggregate states: selecting them without sumMerge returns unreadable state bytes. Rollup total_value uses approximate Float64 values. For exact decimal totals, query sum(value) on events.
Submit a single SELECT query, optionally starting with a CTE (WITH), without a terminating semicolon. Multiple statements, user FORMAT clauses and % templates are not supported; Autumn controls the output format.
SETTINGS clauses are rejected anywhere in the query, including subqueries, CTEs and UNION branches. You cannot override the server’s limits. FINAL and PREWHERE are not supported. Use ordinary WHERE filters and aggregates instead.
Running a query
AnalyticsQueryLine objects. If you already hold a raw response body, for example from fetch, readAnalyticsQueryLines(body) from the same package does the same parsing. Breaking out of the loop early cancels the request.
The Python SDK returns the raw streaming httpx response in res.result. Iterate it with iter_lines() and close it when you’re done, which the with block does for you.
The response stream
The response hasContent-Type: application/x-ndjson: one JSON object per line, each with a type field.
How values are encoded in
row lines:
UInt64,Int64, wider integers andDecimalvalues are strings, so counts and amounts keep their precision. Parse them withBigIntor a decimal library rather thanNumberwhen precision matters.- Arrays and unnamed tuples are JSON arrays. Named tuples may be JSON objects. Maps and JSON columns are JSON objects.
- Date and timestamp values are strings. Event timestamps are UTC.
- Nullable values are JSON
null. Non-finite floats are alsonull.
End lines, error lines and dropped connections
A successful stream always ends with anend line. If the result goes over the 1 GiB output cap, the stream stops early and the end line has truncated: true and stopped_by: "byte_cap".
If the query fails after rows have started streaming, the last line is an error line instead, for example:
end nor an error line, the connection dropped and the result is incomplete. Treat it as a failure and retry rather than using the partial rows.
Errors before streaming
Errors that happen before the first line are regular JSON error responses withmessage, code, env and sometimes details. The SDKs raise them as API errors.
Limits
These are the default limits. There is no row-count cap beyond the byte cap.
A single row or header larger than 8 MiB cannot be decoded and fails with
tb_unavailable; it is not a successful truncated result. Select fewer columns or aggregate large JSON values in SQL to stay under this bound.
A query that runs past 30 seconds fails with statement_timeout: a 408 response if no rows were sent yet, or a final error line if they were. Narrow the query with WHERE and LIMIT, or aggregate in SQL instead of fetching every row. If you need a higher limit, contact support@useautumn.com.
Compression
Responses are always compressed: with zstd when your client’sAccept-Encoding includes it, and with gzip otherwise. Both SDKs handle this for you. The TypeScript SDK asks for zstd on every server runtime (Node 20 and later, Bun and Deno) and decodes it itself where fetch doesn’t. In browsers it sends no Accept-Encoding of its own, so the browser picks the encoding and decodes it. The Python SDK installs zstandard, so httpx asks for and decodes zstd. With curl, pass --compressed and curl decodes whichever encoding it gets.
Query examples
Exact usage by customer
Daily totals
Hourly totals across customers
Schema reference
events — usage event log
events — usage event log
One row per: event. Key:
org_id, env, id.events_customer_hourly_mv — hourly totals per customer
events_customer_hourly_mv — hourly totals per customer
One group per: organization, environment, customer, entity, event name, UTC hour and plan version. Use
sum and GROUP BY.events_customer_daily_mv — daily totals per customer
events_customer_daily_mv — daily totals per customer
One group per: organization, environment, customer, entity, event name and UTC day. Use
sumMerge and GROUP BY. This table has no plan version column.events_customer_monthly_mv — monthly totals per customer
events_customer_monthly_mv — monthly totals per customer
One group per: organization, environment, customer, entity, event name, UTC month and plan version. Use
sumMerge and GROUP BY.events_org_hourly_mv — hourly totals across customers
events_org_hourly_mv — hourly totals across customers
One group per: organization, environment, event name and UTC hour. Use
sum and GROUP BY. Customer, entity, plan and property dimensions are not available.event_names_mv — event-name counts
event_names_mv — event-name counts
One group per: organization, environment and event name. Use
sum(event_count) and GROUP BY event_name.