---
title: "Run an analytics SQL query"
method: POST
path: "/v1/analytics/query"
tags: ["analytics"]
---

# Run an analytics SQL query

`POST /v1/analytics/query`

Execute a read-only SQL query against the analytics schema, scoped to your publication.

**Auth:** Requires an API key. Queries run as the publication that owns the API key; Row-Level Security guarantees that only that publication's rows are visible even if the SQL is unscoped.

**SQL requirements:**
- `SELECT` or `WITH` (CTE) statements only
- Reference tables unprefixed (e.g. `FROM posts`) — the `analytics` schema is the default `search_path`
- No semicolons, no writes, no DDL, no superuser functions
- Hard limit of 10,000 rows; anything over is truncated and `truncated: true` is returned
- 30-second statement timeout

**Discovering the schema:** call `GET /v1/analytics/schema` for column metadata.

Common queries: open rate, subscriber count, top posts by views, engagement over time, click-through rate.

For raw post-scoped tables, join through `posts.draft_of` to roll draft/version rows up to the canonical published post.

## Request body

- object
  - `sql` string, required — A SELECT or WITH query against the analytics schema. The analytics.* prefix is implicit.

## Response `200`

Query executed successfully

- object
  - `rows` object[], required — Result rows. Each row is a column-name → value map.
  - `rowCount` integer, required — Number of rows returned (after truncation if any)
  - `fields` object[], required — Column names in row order
    - `name` string, required
  - `truncated` boolean, required — True if the result was truncated at the 10,000 row limit

## Other responses

- `400` — Invalid SQL — check the validator message
- `401` — Invalid or missing API key
- `404` — Publication not found
- `408` — Query timed out (30s). Try a more targeted query or a materialized view.
- `500` — Internal server error

## Changes

- **2026-05-07** `90de04b87133` — 5 warning
  - removed the optional property `error` from the response with the `400` status
  - removed the optional property `error` from the response with the `401` status
  - removed the optional property `error` from the response with the `404` status
  - removed the optional property `error` from the response with the `408` status
  - …1 more
- **2026-04-23** `18a309176581` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/paragraph-xyz/apis/paragraph-api/changes/v1/analytics/query/post.md)

---

[API](https://skmtc.dev/paragraph-xyz/apis/paragraph-api.md) · [All operations](https://skmtc.dev/paragraph-xyz/apis/paragraph-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/paragraph-xyz/paragraph-api/revisions/8108f2a53d2e/schema)
