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

# Product analytics API

> Read web analytics, product events, funnels, and goals, filter them, find the errors behind a funnel drop, and see AI crawler traffic.

<Note>
  Product analytics is coming soon. It is available on plans with product analytics. Until it
  launches, a project can use it only when early access is on for the project. Other projects get
  `403` with the code `coming_soon` (or `plan_upgrade_required` on a plan without product
  analytics). Call `analytics.status` to see the state of a project.
</Note>

Product analytics reads the consented page views, custom events, and identify calls that the
[Browser SDK](/sdks/browser#product-analytics-and-consent) sends. The SDK removes query strings,
fragments, and ID-like path segments before it sends an event, and Squasher keeps a country code,
never an IP address.

## Operations

| Operation | Method and path | Access | Purpose |
| - | - | - | - |
| `analytics.status` | `GET /v1/projects/{project_id}/analytics/status` | `project:read` | Availability for the project: plan, launch, and early access. |
| `analytics.overview` | `GET /v1/projects/{project_id}/analytics/overview` | `analytics:read` | Visitors, sessions, page views, pages per session, bounce rate, average session, traffic series, top values. |
| `analytics.events` | `GET /v1/projects/{project_id}/analytics/events` | `analytics:read` | Event names with counts and visitors, and the newest events. |
| `analytics.breakdown` | `GET /v1/projects/{project_id}/analytics/breakdown` | `analytics:read` | One breakdown with up to 100 rows. |
| `analytics.live` | `GET /v1/projects/{project_id}/analytics/live` | `analytics:read` | Visitors, page views, and top pages in the last 5 minutes. |
| `analytics.installation` | `GET /v1/projects/{project_id}/analytics/installation` | `analytics:read` | Check that events arrive: counts, hosts, and environments of the last 24 hours. |
| `analytics.properties` | `GET /v1/projects/{project_id}/analytics/properties` | `analytics:read` | Property keys of one event, with their most common values. |
| `analytics.goalConversions` | `GET /v1/projects/{project_id}/analytics/goals/conversions` | `analytics:read` | Visitors who completed each goal, and the conversion rate. |
| `analytics.listGoals` | `GET /v1/projects/{project_id}/analytics/goals` | `analytics:read` | Conversion goals. |
| `analytics.getGoal` | `GET /v1/projects/{project_id}/analytics/goals/{goal_id}` | `analytics:read` | One goal. |
| `analytics.createGoal` | `POST /v1/projects/{project_id}/analytics/goals` | `analytics:write` | Create a goal (at most 50 per project). |
| `analytics.updateGoal` | `PUT /v1/projects/{project_id}/analytics/goals/{goal_id}` | `analytics:write` | Replace a goal. |
| `analytics.deleteGoal` | `DELETE /v1/projects/{project_id}/analytics/goals/{goal_id}` | `analytics:write` | Delete a goal. |
| `analytics.aiCrawlers` | `GET /v1/projects/{project_id}/analytics/ai-crawlers` | `analytics:read` | Requests from known AI crawlers and AI agents, from your server spans. |
| `analytics.runFunnel` | `POST /v1/projects/{project_id}/analytics/funnels/run` | `analytics:read` | Run an ad hoc or saved funnel. |
| `analytics.funnelDropoffErrors` | `POST /v1/projects/{project_id}/analytics/funnels/dropoff-errors` | `analytics:read` | Error groups in the sessions of people who dropped after a step. |
| `analytics.listFunnels` | `GET /v1/projects/{project_id}/analytics/funnels` | `analytics:read` | Saved funnels. |
| `analytics.getFunnel` | `GET /v1/projects/{project_id}/analytics/funnels/{funnel_id}` | `analytics:read` | One saved funnel. |
| `analytics.createFunnel` | `POST /v1/projects/{project_id}/analytics/funnels` | `analytics:write` | Save a funnel (at most 50 per project). |
| `analytics.updateFunnel` | `PUT /v1/projects/{project_id}/analytics/funnels/{funnel_id}` | `analytics:write` | Replace the name, steps, and window of a saved funnel. |
| `analytics.deleteFunnel` | `DELETE /v1/projects/{project_id}/analytics/funnels/{funnel_id}` | `analytics:write` | Delete a saved funnel. |
| `analytics.exportIdentity` | `GET /v1/projects/{project_id}/analytics/identities/export` | `analytics:read` | Export one distinct or account ID in bounded JSON pages. |
| `analytics.deleteIdentity` | `DELETE /v1/projects/{project_id}/analytics/identities` | `analytics:write` | Idempotently delete one distinct or account ID and its mappings. |

Reads take `period` (`24h`, `7d`, `30d`, or `90d`; default `7d`) or `from` and `to` (ISO 8601, at
most 90 days), and an optional `environment`. The overview compares each number with the previous
period of the same length. Its `breakdowns` have the top values of `page`, `entry_page`,
`exit_page`, `referrer`, `channel`, `utm_source`, `utm_medium`, `utm_campaign`, `country`,
`device`, `browser`, and `os`. For `channel`, `entry_page`, and `exit_page`, the `pageviews` field
of a row counts sessions. Call `analytics.breakdown` with `breakdown=<key>` and `limit` (1 to 100,
default 100) to get more rows of one breakdown.

## Filters

The overview, events, breakdown, live, properties, and goal conversion reads, the funnel run, and
the funnel drop-off errors accept filters. A filter is `dimension:value`:

| Dimension | Example | What the filter keeps |
| - | - | - |
| `page` | `page:/pricing` | Sessions with a page view of this path. |
| `event` | `event:signup` | Sessions with an event of this name. |
| `prop.<key>` | `prop.plan:pro` | Sessions with an event that has this property value. |
| `referrer`, `utm_source`, `utm_medium`, `utm_campaign` | `utm_source:newsletter` | Sessions whose first event has this value. |
| `channel` | `channel:Search` | Sessions whose first event has this channel. |
| `country`, `device`, `browser`, `os` | `country:DE` | Events with this value. |

Values match exactly. A referrer is a host name, for example `news.ycombinator.com`. A country is a
two-letter code. A channel is one of `Direct`, `Search`, `Social`, `Referral`, `Paid`, `Email`, or
`Campaign`. Filters on the same dimension match any of their values; filters on different
dimensions must all match. Send at most 10. In a `GET` request, repeat the `filter` query
parameter:

```bash theme={null}
curl "https://api.squasher.ai/v1/projects/$PROJECT_ID/analytics/overview?period=30d&filter=channel:Search&filter=country:DE&filter=country:FR" \
  -H "Authorization: Bearer $SQUASHER_API_KEY"
```

In a `POST` body, send `"filters": ["channel:Search", "country:DE"]`. A filter that keeps whole
sessions keeps every event of those sessions, so session counts, bounce rate, and funnels stay
correct.

## How numbers are counted

* **Page views and event counts** are exact: each event has an ID, and an event that arrives twice
  counts once.
* **Visitors** are approximate unique anonymous IDs.
* **Sessions** use the browser session ID that errors and session replays also use. A bounce is a
  session with one page view.
* **People** in funnels: a visitor who called `identify` counts as the same person as every other
  visitor with that user ID.

## Funnels

A step is a page view by path or an event by name:

```bash theme={null}
curl -X POST "https://api.squasher.ai/v1/projects/$PROJECT_ID/analytics/funnels/run" \
  -H "Authorization: Bearer $SQUASHER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "period": "30d",
    "window_seconds": 3600,
    "steps": [
      { "kind": "pageview", "name": "/pricing" },
      { "kind": "custom", "name": "signup_started" },
      { "kind": "custom", "name": "signup_completed" }
    ]
  }'
```

Each step returns `people`, `conversion_from_start`, `conversion_from_previous` (percent), and
`dropped` (people who did not reach the next step). `median_seconds_from_previous` and
`p90_seconds_from_previous` are the median and 90th percentile time from the previous step, for the
people who reached the step; they are `null` on the first step and when no one reached the step.
Send `funnel_id` instead of `steps` to run a saved funnel, and `filters` to run the funnel on part
of your traffic.

To see what went wrong for people who dropped after a step, send the same steps to
`analytics.funnelDropoffErrors` with `step_index` (0-based). The response lists the error groups in
their sessions, the number of sessions with each error, sample session IDs for session replay, and
the matching issue.

## Goals

A goal is an event or a page that counts as a conversion. A project can have at most 50 goals;
creating one more returns `409`.

```json theme={null}
{
  "kind": "event",
  "name": "Pro sign-up",
  "event_name": "signup_completed",
  "property_key": "plan",
  "property_value": "pro"
}
```

```json theme={null}
{ "kind": "page", "name": "Read the docs", "path_pattern": "/docs/*" }
```

* An event goal needs `name` (1 to 120 characters) and `event_name` (1 to 200 characters).
  `property_key` (letters, digits, `_`, `$`, `.`, `-`; at most 64 characters) and `property_value`
  (1 to 1024 characters) are optional, and you set them together.
* A page goal needs `name` and `path_pattern`: a path that starts with `/`, where `*` matches any
  characters. Other characters match only themselves.

`analytics.goalConversions` returns, for each goal, `conversions` (approximate visitors who
completed the goal), `completions` (matching events), and `conversion_rate` (percent of all
visitors in the range), with the total `visitors`. It accepts the range and filters.

## Live visitors, installation check, and properties

* `analytics.live` returns `visitors`, `pageviews`, and `top_pages` for the last 5 minutes. It
  accepts `environment` and filters; it has no range.
* `analytics.installation` shows whether events arrive. For the last 24 hours it returns event
  counts by kind, `receiving`, and the page hosts and environments that sent events. `last_event_at`
  is the newest event in the last 90 days, or `null`.
* `analytics.properties?event=<name>` returns the property keys of that event with their event
  counts and their most common values. It accepts the range and filters.

## AI crawler traffic

`analytics.aiCrawlers` reads the `user_agent.original` attribute of server spans. Each crawler row
has a `confidence`:

* `user_agent`: only the user agent matched. Anyone can send that user agent.
* `verified`: every client address is in the operator's published IP ranges.
* `mismatch`: some client address is outside those ranges.

Squasher checks addresses only for operators that publish their IP ranges (today OpenAI and
Perplexity). Other crawlers stay at `user_agent`. To check addresses, the span must carry the
`client.address` attribute.

## CLI and MCP

```bash theme={null}
squasher analytics status --project <project_id>
squasher analytics overview --project <project_id> --period 30d --filter channel:Search
squasher analytics events --project <project_id> --kind custom
squasher analytics breakdown entry_page --project <project_id> --filter country:DE
squasher analytics live --project <project_id>
squasher analytics install-check --project <project_id>
squasher analytics properties --project <project_id> signup_completed
squasher analytics goals create --project <project_id> --name "Pro sign-up" --event signup_completed --property plan=pro
squasher analytics goals create --project <project_id> --name "Read the docs" --page "/docs/*"
squasher analytics goals conversions --project <project_id> --period 30d
squasher analytics funnels run --project <project_id> --step pageview:/pricing --step custom:signup_completed
squasher analytics funnels dropoff-errors --project <project_id> --step pageview:/pricing --step custom:signup_completed --step-index 0
squasher analytics ai-crawlers --project <project_id>
```

Repeat `--filter dimension:value` for more than one filter; the overview, events, breakdown, live,
properties, goal conversions, funnel run, and drop-off errors commands accept it.

In MCP Code Mode, call `search("analytics")` before you execute an operation. Send filters of a
read as a list, for example `query: { filter: ["channel:Search", "country:DE"] }`.

### Identity export and deletion

Pass exactly one `distinct_id` or `account_id`. Export responses contain at most 500 events and a `next_cursor`; send that cursor to continue. Deletion is safe to retry and records an organization audit event. These privacy operations are intentionally hidden from MCP Code Mode.

## Agent handoff

```text theme={null}
Find why sign-ups drop for Squasher project <project_id>. Call analytics.status first. Then run
analytics.runFunnel for the sign-up steps over 30d, find the step with the largest drop, and call
analytics.funnelDropoffErrors for that step. Return each error group with its session count, a
sample session ID, and the issue ID.
```

Related: [Browser SDK](/sdks/browser), [Web Vitals API](/api-reference/vitals),
[Plans and billing](/features/plans-and-billing).


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