Run an analytics SQL 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:
SELECTorWITH(CTE) statements only- Reference tables unprefixed (e.g.
FROM posts) — theanalyticsschema is the defaultsearch_path - No semicolons, no writes, no DDL, no superuser functions
- Hard limit of 10,000 rows; anything over is truncated and
truncated: trueis 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.
apiKeyAuthorizationBearer <token>API key for authenticating protected endpoints. Pass as Bearer token in Authorization header.
Body
application/json- body
sql*stringA SELECT or WITH query against the analytics schema. The analytics.* prefix is implicit.
1 <= lengthQuery executed successfully
application/json- response
rows*array<>Result rows. Each row is a column-name → value map.
rowCount*integerNumber of rows returned (after truncation if any)
fields*array<>Column names in row order
truncated*booleanTrue if the result was truncated at the 10,000 row limit
curl -X POST "https://example.com/v1/analytics/query" \ -H "Content-Type: application/json" \ -d '{ "sql": "string" }'{ "rows": [ { "property1": null, "property2": null } ], "rowCount": 0, "fields": [ { "name": "string" } ], "truncated": true}Send a custom email POST
Send an email from your publication to a list of recipient addresses. **Eligibility:** - Publications must be approved by Paragraph before they can send custom emails. Ineligible publications receive a 403. Eligibility is managed by Paragraph and is not user-configurable. **Per-recipient filtering:** - Malformed addresses and known disposable domains are skipped. - Addresses that previously unsubscribed from this publication are skipped as `suppressed`. - Skipped recipients are returned in the response; nothing else is delivered to them. **Delivery:** - `body` is treated as Markdown and rendered to HTML server-side. - Each recipient receives the email individually (not as a BCC blast) with a mandatory unsubscribe footer. - Sends are queued asynchronously; a 200 response means recipients were accepted, not delivered. **Caps:** - Maximum of 10,000 addresses per call (request-level sanity check).
Describe the analytics schema GET
Returns column metadata for every table and view in the analytics schema. Use this to discover available tables/columns before writing an analytics query.