Skip to main content
POST /v1/analytics.query runs a read-only DuckDB SQL query over your Autumn data and streams the result back as newline-delimited JSON. Use it for reporting, reconciliation and one-off questions (“how many customers are on each plan?”) without exporting data or calling list endpoints page by page. Each query sees only your own organization and the environment of the key you use: a sandbox key reads sandbox data, a live key reads live data. Data is roughly 15 minutes behind the API, so a customer you created a moment ago may not show up yet.

Available tables

Every table’s columns are listed in the schema reference.

Discovering the schema

List the tables and describe their columns with DuckDB’s own commands:
information_schema.columns is empty, so use DESCRIBE instead.

Running a query

The TypeScript SDK parses the stream for you and yields typed 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 has Content-Type: application/x-ndjson: one JSON object per line, each with a type field.
How values are encoded in row lines:
  • BIGINT and DECIMAL values are strings, so large numbers and amounts keep their precision. Parse them with BigInt or a decimal library rather than Number when precision matters.
  • Lists are JSON arrays.
  • STRUCT columns come back as DuckDB’s text form. Select to_json(col) to get JSON you can parse.

End lines, error lines and dropped connections

A successful stream always ends with an end line. If the result goes over the 1 GB 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:
If the stream ends with neither an 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 with message, code, env and sometimes details. The SDKs raise them as API errors.

Limits

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’s Accept-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.