> ## Documentation Index
> Fetch the complete documentation index at: https://paragraph.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 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:**
- `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.



## OpenAPI

````yaml /paragraph-api/openapi.json post /v1/analytics/query
openapi: 3.1.0
info:
  title: Paragraph API
  version: 1.0.0
  description: >-
    Public API for interacting with Paragraph publications, posts, users, and
    coined writing.


    ## Rate Limiting

    API requests are rate-limited to ensure fair usage. Contact
    support@paragraph.com for higher limits.


    ## Pagination

    List endpoints support cursor-based pagination using `cursor` and `limit`
    parameters.
  contact:
    name: Paragraph Support
    email: support@paragraph.com
    url: https://paragraph.com/support
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://public.api.paragraph.com/api
    description: Production server
security:
  - {}
tags:
  - name: publications
    description: Operations related to publications
  - name: posts
    description: Operations related to posts and content
  - name: users
    description: Operations related to users and authors
  - name: coins
    description: Operations related to tokenized content
  - name: subscribers
    description: Operations related to subscriber management (requires API key)
paths:
  /v1/analytics/query:
    post:
      tags:
        - analytics
      summary: Run an analytics SQL query
      description: >-
        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.
      operationId: analyticsQuery
      parameters: []
      requestBody:
        description: Body
        content:
          application/json:
            schema:
              type: object
              properties:
                sql:
                  type: string
                  minLength: 1
                  description: >-
                    A SELECT or WITH query against the analytics schema. The
                    analytics.* prefix is implicit.
              required:
                - sql
      responses:
        '200':
          description: Query executed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  rows:
                    type: array
                    items:
                      type: object
                      additionalProperties: {}
                    description: Result rows. Each row is a column-name → value map.
                  rowCount:
                    type: integer
                    description: Number of rows returned (after truncation if any)
                  fields:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                      required:
                        - name
                    description: Column names in row order
                  truncated:
                    type: boolean
                    description: True if the result was truncated at the 10,000 row limit
                required:
                  - rows
                  - rowCount
                  - fields
                  - truncated
        '400':
          description: Invalid SQL — check the validator message
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                    description: Always false for error responses
                  msg:
                    type: string
                    description: Human-readable error message
                required:
                  - success
                  - msg
        '401':
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                    description: Always false for error responses
                  msg:
                    type: string
                    description: Human-readable error message
                required:
                  - success
                  - msg
        '404':
          description: Publication not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                    description: Always false for error responses
                  msg:
                    type: string
                    description: Human-readable error message
                required:
                  - success
                  - msg
        '408':
          description: >-
            Query timed out (30s). Try a more targeted query or a materialized
            view.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                    description: Always false for error responses
                  msg:
                    type: string
                    description: Human-readable error message
                required:
                  - success
                  - msg
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - false
                    description: Always false for error responses
                  msg:
                    type: string
                    description: Human-readable error message
                required:
                  - success
                  - msg
      security:
        - apiKey: []
      x-codeSamples:
        - lang: typescript
          label: Top posts by views in the last 30 days
          source: |-
            import { ParagraphAPI } from "@paragraph-com/sdk"

            const api = new ParagraphAPI({ apiKey: "your-api-key" })
            const { rows } = await api.analytics.query({
              sql: `SELECT title, total_views
                    FROM post_analytics_summary
                    WHERE published_at > now() - interval '30 days'
                    ORDER BY total_views DESC
                    LIMIT 10`,
            })
        - lang: bash
          label: Query via curl
          source: >-
            curl -X POST
            "https://public.api.paragraph.com/api/v1/analytics/query" \
              -H "Authorization: Bearer your-api-key" \
              -H "Content-Type: application/json" \
              -d '{"sql": "SELECT COUNT(*) FROM subscribers"}'
components:
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        API key for authenticating protected endpoints. Pass as Bearer token in
        Authorization header.

````