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

# Analytics API

> Query your organization's Perplexity usage analytics programmatically with a free, org-scoped API key

## Overview

The Analytics API gives Perplexity Enterprise organizations programmatic access to organization usage analytics across Perplexity — credits, query volume, active members, connectors, artifacts, skills, spaces, workflows, and task durations — as bucketed time series you can pull into BI tools or internal reporting. Credit usage and query volume can also be broken down [per member](#per-member-usage).

Analytics requests are free and don't consume API credits.

<Info>
  This API is available to Perplexity Enterprise organizations. If you don't see the Analytics API toggle described below, the feature may not be enabled for your organization yet — contact your Perplexity representative.
</Info>

## Getting Access

Access is managed by your organization's admins from the Perplexity web app:

<Steps>
  <Step title="Enable the Analytics API">
    In the Perplexity web app, open your organization's **Computer** settings and find the **Analytics** section. Turn on **Analytics API** to provision API access for your organization.
  </Step>

  <Step title="Generate your API key">
    In the row that appears below the toggle, select **Generate key**.
  </Step>
</Steps>

A few properties of analytics API keys:

* **Org-scoped, admin-managed** — the key belongs to your organization, not to the admin who created it. Any org admin can view the current key and regenerate it.
* **One active key** — your organization has at most one active analytics key. Regenerating revokes the previous key.
* **Analytics-only** — the key authenticates analytics endpoints only; it cannot call other Perplexity APIs and has no billing attached.
* **Disabling revokes** — turning the Analytics API toggle off permanently revokes the key.

## Authentication

Pass the key as a bearer token:

```bash theme={null}
curl --request GET \
  --url "https://api.perplexity.ai/v1/analytics/computer/usage?dataset=credit_usage&start_time=1746057600" \
  --header "Authorization: Bearer YOUR_API_KEY"
```

## Querying Usage

`GET /v1/analytics/computer/usage` returns one time series per request, for a single dataset.

### Parameters

| Parameter      | Required | Description                                                                                                                                                           |
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dataset`      | Yes      | One of `credit_usage`, `query_volume`, `daily_active_users`, `connectors`, `artifacts`, `skills`, `spaces`, `workflows`, `task_durations`. See [Datasets](#datasets). |
| `start_time`   | Yes      | Window start, unix seconds (UTC). Inclusive. Snapped down to the bucket grid.                                                                                         |
| `end_time`     | No       | Window end, unix seconds (UTC). Exclusive. Defaults to now; values in the future are capped at now.                                                                   |
| `bucket_width` | No       | `1d` (default) or `1h`. Buckets align to the UTC grid. `query_volume` and `daily_active_users` accept `1d` only.                                                      |
| `limit`        | No       | Buckets per page. `1d`: default 7, max 31. `1h`: default 24, max 168.                                                                                                 |
| `page`         | No       | Opaque pagination cursor from a previous response's `next_page`. Valid only with the same query parameters it was issued for.                                         |
| `user_email`   | No       | Restrict results to a single member of your organization. Not available for `query_volume` or `daily_active_users`. See [Filtering by member](#filtering-by-member).  |

### Datasets

Each dataset measures something different. Most accept both bucket widths and a member filter; `query_volume` and `daily_active_users` are aggregated daily for the whole organization and accept neither.

| Dataset              | Counts                           | Breakdown axes                                         | Grain and scope         |
| -------------------- | -------------------------------- | ------------------------------------------------------ | ----------------------- |
| `credit_usage`       | Credits                          | `Model`, `Credit Source`                               | `1d` or `1h`            |
| `query_volume`       | Queries                          | `Feature`, `Model`, `Model Family`, `Project`, `Comet` | `1d`, organization only |
| `daily_active_users` | Distinct members active that day | None                                                   | `1d`, organization only |
| `connectors`         | Threads                          | `Connector`                                            | `1d` or `1h`            |
| `artifacts`          | Threads                          | `Artifact Type`                                        | `1d` or `1h`            |
| `skills`             | Threads                          | `Skill`                                                | `1d` or `1h`            |
| `spaces`             | Threads                          | `Space`                                                | `1d` or `1h`            |
| `workflows`          | Threads                          | `Workflow`                                             | `1d` or `1h`            |
| `task_durations`     | Completed threads                | `Task Duration`                                        | `1d` or `1h`            |

Requesting `bucket_width=1h` for a daily dataset, or `user_email` for an organization-only dataset, returns `400` rather than an empty series. For per-member query volume, use the [v2 endpoint](#per-member-usage).

### Response

```json theme={null}
{
  "categories": ["Model", "Credit Source"],
  "data": [
    {
      "start_time": 1746057600,
      "end_time": 1746144000,
      "count": 350,
      "by_categories": {
        "Model": [
          { "category": "claude-opus-4-8", "count": 210 },
          { "category": "claude-sonnet-4-6", "count": 120 }
        ],
        "Credit Source": [
          { "category": "paid", "count": 250 },
          { "category": "promo", "count": 80 }
        ]
      }
    }
  ],
  "has_more": false,
  "next_page": null
}
```

* `categories` lists the breakdown axes the dataset surfaces, in canonical render order — see [Datasets](#datasets). Most datasets carry one axis, `credit_usage` carries two, `query_volume` carries five, and `daily_active_users` carries none (`categories` is `[]` and each bucket's `by_categories` is `{}`).
* `data` contains every bucket in the page's window, in chronological order. Buckets without data carry `count: 0` and an empty list under each axis in `by_categories`.
* `by_categories` maps each axis label to its breakdown for the bucket. Categories within an axis are the dataset's dimension values — connector names for `connectors`, artifact types for `artifacts`, duration bands for `task_durations`, `paid`/`promo` for `credit_usage`'s `Credit Source`, model names for `credit_usage`'s `Model`, and the titles your members gave the items for `spaces`/`skills`/`workflows`.
* For `query_volume`, `Feature` splits every query into `Search` or `Computer`, and `Model`/`Model Family` name the model that answered it (`Model` is the exact selection, `Model Family` the coarser grouping). `Project` and `Comet` are overlapping subsets, not partitions — a query run in a project is also a `Search` or `Computer` query — so those axes do not sum to `count`.
* For `credit_usage`, `count` can exceed the sum of any single breakdown — usage without a category attribution counts toward the total but not the breakdown.

### Time Windows and Buckets

The API serves **complete UTC grid buckets only**:

* `start_time` snaps down to the start of its bucket, so the first bucket can include usage from before your requested start.
* `end_time` is exclusive and is capped at the current time, then snapped down — the in-progress bucket is never returned. With `bucket_width=1d`, today's bucket is not included; use `1h` for intraday data.
* A valid window that contains no complete buckets returns `200` with empty `data`.

<Note>
  **Data freshness.** The analytics store is synced periodically, not in real time. A `count` of `0` in a recent bucket can mean the data hasn't synced yet rather than zero usage. When `has_more` is `false` you have reached the current data frontier — poll again later for newer buckets.
</Note>

### Pagination

When the window spans more buckets than `limit`, the response sets `has_more: true` and a `next_page` cursor. Pass it back as `page`, keeping every other parameter identical — cursors are strictly validated and a cursor used with different parameters returns `400`.

```python theme={null}
import requests

BASE_URL = "https://api.perplexity.ai/v1/analytics/computer/usage"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}

params = {
    "dataset": "credit_usage",
    "start_time": 1746057600,
    "bucket_width": "1d",
}

buckets = []
while True:
    response = requests.get(BASE_URL, headers=HEADERS, params=params)
    response.raise_for_status()
    payload = response.json()
    buckets.extend(payload["data"])
    if not payload["has_more"]:
        break
    params["page"] = payload["next_page"]

print(f"Fetched {len(buckets)} buckets")
```

### Filtering by Member

Pass `user_email` to restrict the series to a single member of your organization:

```bash theme={null}
curl --request GET \
  --url "https://api.perplexity.ai/v1/analytics/computer/usage?dataset=credit_usage&start_time=1746057600&user_email=member@example.com" \
  --header "Authorization: Bearer YOUR_API_KEY"
```

Every dataset accepts `user_email` except `query_volume` and `daily_active_users`, which are aggregated per organization and reject it with a `400`. For per-member query volume, use the [v2 endpoint](#per-member-usage).

The email must belong to a current member of your organization. Any other value — including emails with no Perplexity account — returns the same generic `400`:

```json theme={null}
{
  "error": {
    "message": "user_email does not match a member of this organization.",
    "type": "invalid_request",
    "code": 400
  }
}
```

## Per-Member Usage

`GET /v2/analytics/computer/usage` breaks a dataset down **per member** instead of as a single org total. It returns daily buckets, each listing one row per member with usage that day, keyed by email. It uses the same API key, error envelope, and limits as v1.

Two datasets support per-member grouping today, at daily granularity:

* `credit_usage` — the member's credits, with the same `Model` and `Credit Source` breakdown v1 carries.
* `query_volume` — the member's queries, with `Feature`, `Project` and `Comet` breakdowns.

`group_by` is a parameter so more groupings can be added later without a breaking change.

```bash theme={null}
curl --request GET \
  --url "https://api.perplexity.ai/v2/analytics/computer/usage?dataset=credit_usage&group_by=user_email&start_time=1746057600" \
  --header "Authorization: Bearer YOUR_API_KEY"
```

### Parameters

| Parameter    | Required | Description                                                                                                                   |
| ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `dataset`    | Yes      | `credit_usage` or `query_volume` — the datasets with per-member grouping today.                                               |
| `group_by`   | Yes      | Must be `user_email` — the only grouping today.                                                                               |
| `start_time` | Yes      | Window start, unix seconds (UTC). Inclusive. Snapped down to the day grid.                                                    |
| `end_time`   | No       | Window end, unix seconds (UTC). Exclusive. Defaults to now; values in the future are capped at now.                           |
| `limit`      | No       | Member rows per page. Default 50, max 100. A page can span multiple days.                                                     |
| `page`       | No       | Opaque pagination cursor from a previous response's `next_page`. Valid only with the same query parameters it was issued for. |

### Response

```json theme={null}
{
  "categories": ["Model", "Credit Source"],
  "data": [
    {
      "start_time": 1746057600,
      "end_time": 1746144000,
      "results": [
        {
          "user_email": "alice@example.com",
          "count": 900,
          "by_categories": {
            "Model": [{ "category": "claude-opus-4-8", "count": 900 }],
            "Credit Source": [{ "category": "paid", "count": 900 }]
          }
        },
        {
          "user_email": "bob@example.com",
          "count": 500,
          "by_categories": {
            "Model": [{ "category": "claude-sonnet-4-6", "count": 500 }],
            "Credit Source": [{ "category": "promo", "count": 500 }]
          }
        }
      ]
    }
  ],
  "has_more": true,
  "next_page": "eyJwayI6ICJPUkcjLi4uIn0="
}
```

* Each `data` entry is one UTC day; `results` holds one row per member with usage that day, in `(day, member)` order. Days with no usage are omitted rather than returned empty.
* `user_email` identifies the member. `count` is the member's credits or queries for the day, depending on `dataset`, and `by_categories` carries that dataset's axes — listed in the response's top-level `categories`.
* Time-window and bucket semantics match v1's `1d` mode — complete UTC days only, and the same [freshness](#time-windows-and-buckets) caveat applies.

### Paginating Members

v2 paginates over **member rows**, not days — a single page can span several days, and each member's day counts as one row toward `limit`. Page until `has_more` is `false`, passing `next_page` back as `page` with every other parameter unchanged:

```python theme={null}
import requests

BASE_URL = "https://api.perplexity.ai/v2/analytics/computer/usage"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}

params = {
    "dataset": "credit_usage",
    "group_by": "user_email",
    "start_time": 1746057600,
}

days = []
while True:
    response = requests.get(BASE_URL, headers=HEADERS, params=params)
    response.raise_for_status()
    payload = response.json()
    days.extend(payload["data"])
    if not payload["has_more"]:
        break
    params["page"] = payload["next_page"]

print(f"Fetched {len(days)} daily buckets")
```

## Errors

Errors use a consistent envelope:

```json theme={null}
{
  "error": {
    "message": "Human-readable description.",
    "type": "machine_readable_type",
    "code": 400
  }
}
```

| Status | `type`                        | Meaning                                                                                                                                                                                                                                                                |
| ------ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `bad_request`                 | A parameter failed validation (wrong type, malformed value).                                                                                                                                                                                                           |
| `400`  | `invalid_request`             | Parameters are well-formed but semantically invalid — e.g. `start_time` after `end_time`, a window over 90 days, a misused pagination cursor, a `user_email` that doesn't match a member, or a `dataset` used with an unsupported `bucket_width` or with `user_email`. |
| `401`  | `unauthorized`                | Missing or invalid API key.                                                                                                                                                                                                                                            |
| `404`  | `feature_disabled`            | The Analytics API is not enabled for your organization.                                                                                                                                                                                                                |
| `429`  | `request_rate_limit_exceeded` | Rate limit exceeded. Requests are limited per organization across all of its keys; retry with backoff.                                                                                                                                                                 |

## Limits

* Time windows are limited to **90 days** per request (paginate within the window for long ranges).
* Requests are rate-limited **per organization** — all keys minted by your org share one allowance.
