Skip to main content
Perplexity

Search documentation

Type to search this documentation.

On this pageOverview

Analytics API

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.

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

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

  1. 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.

  2. Generate your API key

    In the row that appears below the toggle, select Generate key.

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.

Pass the key as a bearer token:

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

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

Parameter Required Description
dataset Yes One of credit_usage, query_volume, daily_active_users, connectors, artifacts, skills, spaces, workflows, task_durations. See 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.

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.

JSON
{
  "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. 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.

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.

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
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")

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

Bash
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.

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
{
  "error": {
    "message": "user_email does not match a member of this organization.",
    "type": "invalid_request",
    "code": 400
  }
}

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
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"
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.
JSON
{
  "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 caveat applies.

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
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 use a consistent envelope:

JSON
{
  "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.
  • 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.
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu