Skip to content
APIs / Stats API

APIs

Stats API

Query any metric, dimension and filter from your dashboard with a single JSON endpoint.

The Stats API gives you programmatic access to the same query engine that powers the dashboard — build internal reports, feed a data warehouse or show numbers in your own app.

Authentication

Create a key in Account settings → API keys → New API key and choose the Stats API type. The key is shown once, so store it safely. Send it as a bearer token:

Authorization: Bearer YOUR_KEY

A key can read every site its owner can access. Each key allows 600 requests per hour by default; responses include X-RateLimit-Limit and X-RateLimit-Remaining headers, and you'll get 429 with a Retry-After header when the limit is reached.

Query endpoint

curl -X POST https://plainwebanalytics.com/api/v2/query \
  -H 'Authorization: Bearer YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "site_id": "example.com",
    "date_range": "7d",
    "metrics": ["visitors", "pageviews", "bounce_rate"],
    "dimensions": ["visit:source"],
    "filters": [["is", "visit:country", ["GB"]]],
    "order_by": [["visitors", "desc"]],
    "pagination": { "limit": 10 }
  }'
{
  "results": [
    { "metrics": [812, 1904, 41], "dimensions": ["Google"] },
    { "metrics": [233, 501, 52], "dimensions": ["Direct / None"] }
  ],
  "meta": { "imports_included": false },
  "query": { "site_id": "example.com", "date_range": ["2026-10-19T00:00:00Z", "2026-10-25T23:59:59Z"], "...": "..." }
}

Each result lists metric values in the order you asked for them, followed by the dimension values.

Request fields

Field Description
site_id The site's domain. Required.
date_range "day", "24h", "7d", "28d", "30d", "91d", "month", "year", "12mo", "all", "realtime" (last 30 minutes), any "<n>d", or a custom range ["2026-01-01", "2026-01-31"]. Required.
relative_date Anchor date (YYYY-MM-DD) for relative ranges. Defaults to today in the site's timezone. "7d" means the seven full days before it.
metrics One or more metrics (below). Required.
dimensions Group results by up to several dimensions (below).
filters A list of filters (below).
order_by e.g. [["visitors", "desc"], ["visit:source", "asc"]]. Defaults to the first metric, descending.
include Options: imports (merge imported data), time_labels, total_rows, and compare ("previous_period", "year_over_year" or a date range).
pagination { "limit": 100, "offset": 0 }.

Metrics

Metric Meaning
visitors Unique visitors
visits Visits (sessions)
pageviews Pageviews
events All events except engagement pings
views_per_visit Pageviews per visit
bounce_rate Percentage of single-page visits
visit_duration Average visit length, in seconds
percentage Share of total visitors (with a dimension)
conversion_rate Share of visitors who converted (with a goal filter or event:goal dimension)
group_conversion_rate Conversion rate within each group
exit_rate Exits as a share of pageviews (with visit:exit_page)
time_on_page Average engaged time on a page, in seconds
scroll_depth Average maximum scroll depth, in percent
total_revenue, average_revenue Revenue for revenue goals

Dimensions

  • Event: event:page, event:hostname, event:name, event:goal, event:props:<name>
  • Visit: visit:source, visit:referrer, visit:channel, visit:utm_medium, visit:utm_source, visit:utm_campaign, visit:utm_content, visit:utm_term, visit:entry_page, visit:exit_page, visit:entry_page_hostname, visit:exit_page_hostname
  • Device: visit:device, visit:browser, visit:browser_version, visit:os, visit:os_version
  • Location: visit:country, visit:region, visit:city, plus visit:country_name, visit:region_name, visit:city_name
  • Time: time (interval chosen automatically), time:minute, time:hour, time:day, time:week, time:month

Filters

A filter is [operator, dimension, values]:

[
  ["is", "visit:country", ["GB", "IE"]],
  ["contains", "event:page", ["/blog"]],
  ["is_not", "visit:source", ["Direct / None"]]
]

Operators: is, is_not, contains, contains_not, matches and matches_not (regular expressions), and matches_wildcard / matches_wildcard_not (* and **). Add {"case_sensitive": false} as a fourth element to ignore case. Filters in the list are combined with AND; you can also nest ["and", [...]], ["or", [...]] and ["not", filter], and use ["has_done", filter] / ["has_not_done", filter] to select visitors by events they did or didn't perform.

To filter by a goal, use ["is", "event:goal", ["Signup"]].

Errors

Invalid queries return 400 with {"error": "…"} describing the problem. A missing or wrong key, or a site the key can't access, returns 401.

Version 1 endpoints

For existing integrations, the older GET endpoints are also available with the same keys: /api/v1/stats/realtime/visitors, /api/v1/stats/aggregate, /api/v1/stats/timeseries and /api/v1/stats/breakdown. They take site_id, period, date, metrics, filters (e.g. event:page==/blog;visit:country==GB), and for breakdowns property, limit and page. New integrations should use /api/v2/query.

Sites API

Keys of the Sites API type can manage sites, goals, shared links, guests and custom properties under /api/v1/sites — for example, PUT /api/v1/sites/goals creates a goal if it doesn't already exist.

Something unclear or missing? Tell us.