# Stats API

URL: https://pulsora.co/docs/api/stats

Read visitors, pageviews, breakdowns, goals, funnels and revenue for your sites with a team API key.

```text
GET https://pulsora.co/api/v1/...
```

The stats API is read-only. It returns the same numbers as the dashboard, as JSON.

## Authentication [#authentication]

Create a key in **Team settings → API keys**. Only owners and admins can create or revoke keys. Pulsora shows the key once, so copy it right away.

Send it as a bearer token:

```bash
curl https://pulsora.co/api/v1/sites \
  -H "Authorization: Bearer pls_your_key"
```

A key reads every site in its team. Store it like a password, and revoke it in team settings when you no longer need it. A revoked key stops working on the next request.

## Date range [#date-range]

Every site endpoint takes one of these:

- `period`: `today`, `7d`, `30d`, `90d` or `365d`. Defaults to `30d`.
- `start_date` and `end_date`: a custom range as `YYYY-MM-DD`, up to 366 days. It wins over `period`.

Every response from a site endpoint includes the site and the resolved range in `meta`:

```json
{
  "data": { "...": "..." },
  "meta": {
    "site": "example.com",
    "range": { "period": "7d", "start": "2026-10-30", "end": "2026-11-06" }
  }
}
```

## Endpoints [#endpoints]

| Endpoint | Returns |
| --- | --- |
| `GET /api/v1/sites` | The team's sites: `domain`, `name`, `timezone`. |
| `GET /api/v1/sites/{domain}/stats` | Visitors, pageviews, sessions, bounce rate, average duration in seconds, new and returning visitors. Visitors and pageviews include `previous` and `change` against the previous range of the same length. |
| `GET /api/v1/sites/{domain}/timeseries` | One row per day: `date`, `visitors`, `pageviews`. |
| `GET /api/v1/sites/{domain}/breakdown/{dimension}` | The top values of one dimension. Takes `limit`, 1 to 100, default 10. |
| `GET /api/v1/sites/{domain}/goals` | Every [goal](https://pulsora.co/docs/core/goals-and-funnels) with visitors, conversions, conversion rate and revenue. |
| `GET /api/v1/sites/{domain}/funnels` | Every funnel with visitors, conversion, drop-off and revenue per step. |
| `GET /api/v1/sites/{domain}/revenue` | Revenue, transactions, average order value, refunds, MRR and customers, with the previous range. |

Breakdown dimensions are `pages`, `entry_pages`, `exit_pages`, `sources`, `referrers`, `utm_campaigns`, `countries`, `devices`, `browsers`, `os`, `events`, `outbound_links` and `file_downloads`. Each row has a `value` plus that dimension's metrics:

```bash
curl "https://pulsora.co/api/v1/sites/example.com/breakdown/sources?period=7d&limit=3" \
  -H "Authorization: Bearer pls_your_key"
```

```json
{
  "data": [
    { "value": "Direct", "pageviews": 593, "visitors": 580 },
    { "value": "google", "pageviews": 80, "visitors": 79 },
    { "value": "twitter", "pageviews": 73, "visitors": 71 }
  ],
  "meta": { "site": "example.com", "range": { "period": "7d", "start": "2026-10-30", "end": "2026-11-06" } }
}
```

Entry and exit pages count `sessions` instead of `visitors`. Events add `count` and `revenue`. Outbound links and file downloads count `clicks`, from [automatic events](https://pulsora.co/docs/core/auto-tracking). Pages add `avg_scroll_depth` and `avg_engaged_seconds` when [engagement](https://pulsora.co/docs/core/auto-tracking#scroll-depth-and-time-on-page) is on, and `null` otherwise.

## Errors and limits [#errors-and-limits]

Errors are JSON with a stable `code`:

| Status | `error.code` | When |
| --- | --- | --- |
| 401 | `unauthenticated` | The key is missing, unknown or revoked. |
| 404 | `not_found` | The domain isn't a site in the key's team. |
| 422 | `invalid_request` | A parameter is invalid. `error.fields` says which. |
| 429 | `rate_limited` | More than 120 requests a minute with the same key. |

```json
{ "error": { "code": "invalid_request", "message": "period must be one of: today, 7d, 30d, 90d, 365d." } }
```
