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

# Explore query distribution

> Executes an Explore query and returns its Channel Distribution, Language, or Domain breakdown: matching mention counts grouped by the `group_by` dimension, with each group's share of the total.

### Rate limits and quota

- **5 queries per user per minute**, shared with `POST /explore/analytics/timeline`. Exceeding it returns a `429` with `error: "rate_limited"`.
- **1,000 queries per user per month** for this endpoint, resetting at the start of each calendar month (UTC). Exceeding it returns a `429` with `error: "quota_exceeded"`.

Every request counts toward the per-minute limit; only successful (`200`) requests count toward the monthly quota. Both limits are shared with the Explore tools in the [PeakMetrics MCP server](/mcp/capabilities).




## OpenAPI

````yaml /api/api_docs.yaml post /explore/analytics/distribution
openapi: 3.1.0
info:
  title: PeakMetrics API
  description: >
    The PeakMetrics API provides programmatic access to narrative intelligence
    data.


    Access millions of mentions across news, social media, and online platforms,
    enriched with custom AI-powered

    smart categories and over 50 metadata properties. The API enables you to

    identify, analyze, and act on emerging narratives in real-time.
  version: 2.0.0
servers:
  - url: https://api.peakmetrics.com
    description: Main (production) server
security:
  - BearerAuth: []
tags:
  - name: Authorization
  - name: Workspaces
  - name: Explore
    description: >
      Run an ad-hoc [Explore](/help/get-started-with-peakmetrics/explore) query
      without creating a workspace, and get back Volume Over Time (`POST
      /explore/analytics/timeline`) or a Channel, Language, or Domain
      distribution (`POST /explore/analytics/distribution`).


      Both endpoints use the same Bearer JWT authentication as the rest of the
      API and take their parameters as a JSON body.


      ### Rate limits and quota


      - **5 queries per user per minute**, shared across both Explore endpoints.
      The minute is a fixed window of 60 seconds from the first request in the
      window. Exceeding it returns a `429` with `error: "rate_limited"` and
      `retryAfterSeconds`.

      - **1,000 queries per user per endpoint per month.** Each endpoint has its
      own monthly allowance, which resets at the start of each calendar month
      (UTC). Exceeding it returns a `429` with `error: "quota_exceeded"` and
      `retryAfterSeconds` set to the time until the reset.


      Every request counts toward the per-minute limit; only successful (`200`)
      requests count toward the monthly quota.


      Both limits are shared with the Explore tools in the [PeakMetrics MCP
      server](/mcp/capabilities), so queries your AI assistant runs there count
      toward the same totals.
  - name: Folders
  - name: Channels
  - name: Locations
    description: >
      PeakMetrics resolves a normalized location on mentions across every
      channel, using a

      six-level taxonomy: `region`, `subregion`, `country`, `state`, `county`,
      and `city`.

      `state` and `county` are United States only.


      Look locations up with `GET /locations`, read a mention's resolved
      location from the

      `location` object on `GET /workspaces/{workspaceId}/mentions`, filter the
      mentions,

      timeline, or distribution endpoints with the `locations.<level>`
      parameters, and

      break counts down with the matching `locations.<level>` `group_by` values.
  - name: Custom Channels
  - name: Custom Enrichments
    description: >
      Custom enrichments let you push external context about mentions into
      PeakMetrics,

      attached to individual mentions and used alongside all other mention
      metadata across

      the platform. Once written, enrichment values feed into narrative
      analysis, workspace

      summaries, and notifications, and are available through the customer API
      and the MCP

      server.


      Your team manages its own custom enrichments through the API: create one
      with

      `POST /customEnrichments`, list them with `GET /customEnrichments`, rename
      one or

      replace its option list with `PUT /customEnrichments/{enrichmentId}`,
      retire one with

      `DELETE /customEnrichments/{enrichmentId}`, and push values for individual
      mentions

      with `PUT /mentions/customEnrichments`.
paths:
  /explore/analytics/distribution:
    post:
      tags:
        - Explore
      summary: Explore query distribution
      description: >
        Executes an Explore query and returns its Channel Distribution,
        Language, or Domain breakdown: matching mention counts grouped by the
        `group_by` dimension, with each group's share of the total.


        ### Rate limits and quota


        - **5 queries per user per minute**, shared with `POST
        /explore/analytics/timeline`. Exceeding it returns a `429` with `error:
        "rate_limited"`.

        - **1,000 queries per user per month** for this endpoint, resetting at
        the start of each calendar month (UTC). Exceeding it returns a `429`
        with `error: "quota_exceeded"`.


        Every request counts toward the per-minute limit; only successful
        (`200`) requests count toward the monthly quota. Both limits are shared
        with the Explore tools in the [PeakMetrics MCP
        server](/mcp/capabilities).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/ExploreAnalyticsRequest'
                - type: object
                  required:
                    - group_by
                  properties:
                    group_by:
                      type: string
                      enum:
                        - channels
                        - domains
                        - languages
                      description: >
                        Breakdown dimension:


                        - `channels` — Counts by channel. Each group's `key` is
                        a channel id from your `channels` list; Reddit posts and
                        comments are counted together under `reddit`.

                        - `domains` — Counts by domain: the top 50 domains by
                        count.

                        - `languages` — Counts by language. English variants
                        (for example, `en-us` and `en-gb`) are reported as `en`.
                        When `channels` includes `twitter`, the breakdown
                        contains exactly two groups, `en` and `non_en`.
                        Otherwise it contains the full breakdown up to the top
                        100 languages per channel; mentions with no detected
                        language are not listed.


                        Required on this endpoint. A missing `group_by`, or any
                        other value, is rejected with a `400`.
                      example: channels
            example:
              query: '"Delta Airlines" AND delay'
              channels:
                - news
                - reddit
                - twitter
                - vk
                - weibo
              since: '2026-06-14T00:00:00Z'
              to: '2026-09-14T23:59:59Z'
              group_by: channels
              domains:
                - nytimes.com
                - reddit.com
                - twitter.com
                - vk.com
                - weibo.com
              languages:
                - en
                - non_en
      responses:
        '200':
          description: A JSON object containing the mention count distribution.
          content:
            application/json:
              schema:
                type: object
                required:
                  - totalCount
                  - groups
                properties:
                  groupBy:
                    type: string
                    enum:
                      - channels
                      - domains
                      - languages
                    description: >-
                      The dimension used for this distribution, as sent in
                      `group_by`.
                    example: channels
                  totalCount:
                    type: number
                    minimum: 0
                    description: >
                      Total number of matching mentions. Data for some channels
                      may be estimated from a large-scale firehose sample:
                      Reddit comments, VK, and Weibo are sampled (20%, 20%, and
                      10% of the firehose, respectively) and their counts are
                      extrapolated from the sample, while News, Reddit posts,
                      and Twitter are counted in full.
                    example: 13100
                  groups:
                    type: array
                    description: >
                      The breakdown by the `group_by` dimension, ordered by
                      count, highest first.


                      With `group_by: "channels"`, each entry's `key` is a
                      channel you requested, with one `reddit` entry for Reddit
                      posts and comments.


                      With `group_by: "domains"`, this contains the top 50
                      domains by count.


                      With `group_by: "languages"` and `twitter` in `channels`,
                      this contains exactly two entries, `en` and `non_en`.
                      Without `twitter`, it contains the full breakdown up to
                      the top 100 languages per channel; mentions with no
                      detected language are not listed. English variants (for
                      example, `en-us` and `en-gb`) are always reported as `en`.


                      Domains and languages beyond these caps are not listed,
                      and without `twitter` neither are mentions with no
                      detected language (with `twitter`, they are counted in
                      `non_en`). Each `percentage` is also rounded to one
                      decimal place, so the `percentage` values need not add up
                      to 100.
                    items:
                      type: object
                      properties:
                        key:
                          type: string
                          description: >
                            The value for this group: a channel you requested
                            (e.g., `"news"`), a domain (e.g., `"nytimes.com"`),
                            or a language (e.g., `"en"`, `"non_en"`).
                          example: news
                        name:
                          type: string
                          description: Human-readable label for this group.
                          example: News
                        count:
                          type: number
                          minimum: 0
                          description: >
                            The number of mentions in this group. Data for some
                            channels may be estimated from a large-scale
                            firehose sample: Reddit comments, VK, and Weibo are
                            sampled (20%, 20%, and 10% of the firehose,
                            respectively) and their counts are extrapolated from
                            the sample, while News, Reddit posts, and Twitter
                            are counted in full.
                          example: 5200
                        percentage:
                          type: number
                          format: float
                          minimum: 0
                          maximum: 100
                          description: >
                            Percentage of `totalCount` that this group
                            represents, calculated as (count / totalCount) * 100
                            and rounded to one decimal place.
                          example: 39.7
              example:
                groupBy: channels
                totalCount: 13100
                groups:
                  - key: news
                    name: News
                    count: 5200
                    percentage: 39.7
        '400':
          $ref: '#/components/responses/ExploreBadRequest'
        '401':
          description: Missing or invalid access token.
        '403':
          $ref: '#/components/responses/ExploreForbidden'
        '429':
          $ref: '#/components/responses/ExploreRateLimited'
        '500':
          $ref: '#/components/responses/ExploreInternalServerError'
      security:
        - BearerAuth: []
components:
  schemas:
    ExploreAnalyticsRequest:
      type: object
      description: >
        The JSON request body shared by `POST /explore/analytics/timeline` and
        `POST /explore/analytics/distribution`. Parameters are sent in the body
        rather than the query string because `query` can contain quotes,
        parentheses, and boolean operators that are awkward to URL-encode, and
        `channels` is an array.
      required:
        - query
        - channels
        - since
        - to
      properties:
        query:
          type: string
          description: >
            The search query to execute. Supports a reduced boolean syntax:
            `AND`, `OR`, `NOT`, parenthetical grouping, and the `title:`,
            `author:`, and `domain:` field scopes. An unparseable query (for
            example, unbalanced parentheses or an unsupported field-scope
            operator) is rejected with a `400`.


            A query can be up to 2,000 characters long and contain up to 100
            terms, where a quoted phrase counts as one term. A query over either
            limit is rejected with a `400`.


            `domain:` terms don't apply to Twitter. When `channels` includes
            `twitter`, Twitter counts ignore any `domain:` terms in the query,
            and a query with nothing left for Twitter without them (for example,
            a query made only of `domain:` terms) is rejected with a `400`.
          example: ("Delta Airlines" OR title:Delta) AND delay NOT domain:example.com
        channels:
          type: array
          minItems: 1
          items:
            type: string
            enum:
              - news
              - reddit
              - twitter
              - vk
              - weibo
          description: >
            The channels to search. Access to each channel is governed by your
            account's channel entitlements, consistent with the rest of the
            PeakMetrics platform. Requesting a channel your account is not
            entitled to is rejected with a `403`.


            `reddit` covers the Reddit posts and comments your account is
            entitled to: posts, comments, or both. It is rejected with a `403`
            only when your account is entitled to neither.
          example:
            - news
            - reddit
            - twitter
            - vk
            - weibo
        since:
          oneOf:
            - title: ISO 8601
              type: string
              format: date-time
            - title: Epoch milliseconds
              type: integer
          description: >
            The minimum published date of the mentions to include. Required,
            along with `to`. Send an ISO 8601 date-time or epoch milliseconds;
            any other value is rejected with a `400`. Dates are aligned to UTC
            day boundaries: the range starts at 00:00:00 UTC on the day `since`
            falls on.


            The range can reach back 12 months from the start of the current UTC
            day. A `since` up to one day earlier than that is clamped to the
            start of the 12-month range; a `since` further back is rejected with
            a `400`, and so is a `since` on a later UTC day than `to` or than
            today.
          example: '2026-06-14T00:00:00Z'
        to:
          oneOf:
            - title: ISO 8601
              type: string
              format: date-time
            - title: Epoch milliseconds
              type: integer
          description: >
            The maximum published date of the mentions to include. Required,
            along with `since`. Send an ISO 8601 date-time or epoch
            milliseconds; any other value is rejected with a `400`. Dates are
            aligned to UTC day boundaries: the range includes the whole UTC day
            `to` falls on. A `to` in the future is clamped to today, so the
            range ends with the current UTC day.
          example: '2026-09-14T23:59:59Z'
        domains:
          type: array
          items:
            type: string
          description: >
            Filter to mentions published on the provided domains. Multiple
            values are combined with OR logic. When omitted, all domains are
            included. Up to 50 values are accepted; more are rejected with a
            `400`.


            When `channels` includes `twitter` and you set `domains`, Twitter is
            counted only if `domains` includes `twitter.com`. Otherwise Twitter
            contributes `0` to every count, and the request still succeeds.
            `x.com` is not an alias for `twitter.com`: list `twitter.com` to
            keep Twitter in the results.
          example:
            - nytimes.com
            - reddit.com
            - twitter.com
            - vk.com
            - weibo.com
        languages:
          type: array
          items:
            type: string
          description: >
            Filter to mentions in the provided languages, specified as
            two-letter ISO 639-1 codes. Codes are case-insensitive, so `EN` is
            the same as `en`. Use `non_en` to match every language other than
            English; `en` also matches regional English variants such as `en-us`
            and `en-gb`. Multiple values are combined with OR logic. When
            omitted, all languages are included. Up to 50 values are accepted;
            more are rejected with a `400`.


            When `channels` includes `twitter`, `languages` may only contain
            `en`, `non_en`, or both. Any other value (for example, a specific
            non-English language code) is rejected with a `400`.
          example:
            - en
    ExploreError:
      type: object
      description: The error shape returned by the Explore analytics endpoints.
      required:
        - error
        - message
      properties:
        error:
          type: string
          enum:
            - invalid_request
            - forbidden
            - rate_limited
            - quota_exceeded
            - internal_server_error
          description: >
            A machine-readable error code: `invalid_request` (`400`),
            `forbidden` (`403`), `rate_limited` or `quota_exceeded` (`429`), or
            `internal_server_error` (`500`).
          example: invalid_request
        message:
          type: string
          description: A human-readable description of the error.
          example: >-
            When 'twitter' is included in channels, languages may only contain
            'en' and/or 'non_en'.
        details:
          type: array
          description: >
            Returned with a `400` only, when the problem is with the fields in
            the request body. Lists every problem found, one entry each;
            `message` describes the first of them.
          items:
            type: object
            required:
              - path
              - message
            properties:
              path:
                type: string
                description: >-
                  The field the problem is with (for example, `since` or
                  `languages`), or `body` when the problem is with the request
                  body as a whole.
                example: languages
              message:
                type: string
                description: A human-readable description of the problem.
                example: >-
                  When 'twitter' is included in channels, languages may only
                  contain 'en' and/or 'non_en'.
    ExploreRateLimitError:
      allOf:
        - $ref: '#/components/schemas/ExploreError'
        - type: object
          required:
            - retryAfterSeconds
          properties:
            error:
              enum:
                - rate_limited
                - quota_exceeded
              example: rate_limited
            message:
              example: >-
                Rate limit of 5 queries per minute exceeded. Try again in 42
                seconds.
            retryAfterSeconds:
              type: integer
              minimum: 0
              description: >
                Seconds to wait before retrying. For `quota_exceeded`, the
                seconds until the monthly quota resets at the start of the next
                calendar month (UTC).
              example: 42
  responses:
    ExploreBadRequest:
      description: >
        The request is invalid. Returned for:


        - missing or malformed required fields, or unknown fields

        - a `since`/`to` value that is not ISO 8601 or epoch milliseconds

        - an unparseable `query` (for example, unbalanced parentheses or an
        unsupported field-scope operator)

        - a `query` longer than 2,000 characters or with more than 100 terms

        - more than 50 values in `domains`, or more than 50 in `languages`

        - a query that X cannot run when `twitter` is included in `channels`
        (for example, a query made only of `domain:` terms, which have no X
        equivalent)

        - an unsupported `group_by` value, or a missing `group_by` on `POST
        /explore/analytics/distribution`

        - a `since` more than one day before the start of the trailing 12
        months, or a `since` on a later UTC day than `to` or than today (a `to`
        in the future is clamped to today rather than rejected)

        - a `languages` filter containing anything other than `en`/`non_en` when
        `twitter` is included in `channels`. Language codes are
        case-insensitive, so `EN` and `NON_EN` are accepted.


        `message` describes the first problem found. When the problem is with
        the fields in the request body, `details` lists every problem found, one
        entry each.


        Every request counts toward the per-minute limit; only successful
        (`200`) requests count toward the monthly quota.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ExploreError'
          example:
            error: invalid_request
            message: >-
              When 'twitter' is included in channels, languages may only contain
              'en' and/or 'non_en'.
            details:
              - path: languages
                message: >-
                  When 'twitter' is included in channels, languages may only
                  contain 'en' and/or 'non_en'.
    ExploreForbidden:
      description: >
        Your account's channel entitlements do not include one of the requested
        channels. The `message` lists the channels you are not authorized for;
        remove them from `channels` and retry. For `reddit`, this happens only
        when your account is entitled to neither Reddit posts nor Reddit
        comments.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ExploreError'
          example:
            error: forbidden
            message: 'not authorized for channels: twitter'
    ExploreRateLimited:
      description: >
        A rate limit or quota was exceeded. The `error` code says which:


        - `rate_limited`: you made more than 5 queries within one minute. The
        limit is per user. The minute is a fixed window of 60 seconds from the
        first request in the window, not a rolling one: once it ends, the count
        starts again. The limit is shared by `POST /explore/analytics/timeline`,
        `POST /explore/analytics/distribution`, and the Explore tools in the
        [PeakMetrics MCP server](/mcp/capabilities).

        - `quota_exceeded`: your monthly quota of 1,000 queries for this
        endpoint is used up. The quota is per user, and each endpoint has its
        own, which resets at the start of the next calendar month (UTC). Queries
        run through the Explore tools in the [PeakMetrics MCP
        server](/mcp/capabilities) count toward the same quotas.


        Every request counts toward the per-minute limit; only successful
        (`200`) requests count toward the monthly quota.


        Wait `retryAfterSeconds` before retrying. The same value is sent in the
        `Retry-After` header.
      headers:
        Retry-After:
          description: >-
            Seconds to wait before retrying, the same value as
            `retryAfterSeconds`.
          schema:
            type: integer
            minimum: 0
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ExploreRateLimitError'
          examples:
            rateLimited:
              summary: Per-minute rate limit exceeded
              value:
                error: rate_limited
                message: >-
                  Rate limit of 5 queries per minute exceeded. Try again in 42
                  seconds.
                retryAfterSeconds: 42
            quotaExceeded:
              summary: Monthly quota exceeded
              description: >-
                Returned at 2026-10-28T12:00:00Z, so `retryAfterSeconds` counts
                down to the reset at 2026-11-01T00:00:00Z.
              value:
                error: quota_exceeded
                message: >-
                  Monthly quota of 1000 queries for this endpoint exceeded.
                  Resets 2026-11-01T00:00:00Z.
                retryAfterSeconds: 302400
    ExploreInternalServerError:
      description: >
        Internal server error. Retry with backoff.


        Explore never returns partial results: a request that cannot be
        completed fails with a `500` rather than returning an undercount.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ExploreError'
          example:
            error: internal_server_error
            message: Internal server error.
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.