MenuRead API

API reference

Read API

Read overview, timeseries, breakdowns, revenue, profit, cohorts and path to purchase with a read-scoped key, the same numbers as the dashboard.

The Read API returns the numbers behind the dashboard: traffic, revenue, profit, cohorts and path to purchase. It uses the same queries as the dashboard, so a figure from the API matches the figure on screen for the same range and filters.

Authentication

Create a key with the read scope in Settings → API keys, then send it as a Bearer token. Read keys are on the Starter plan and above. See Authentication and API keys.

Shell
curl "https://api.productlense.app/api/v1/stats/overview?site_id=SITE_ID&from=2026-09-01&to=2026-09-30" \
  -H "Authorization: Bearer $PL_READ_KEY"

A read key can read; it cannot send events or payments, and it cannot change anything.

Endpoints

EndpointReturns
GET /api/v1/stats/overviewVisitors, pageviews, sessions, bounce rate, average duration, signups, customers, revenue, fees, net revenue, spend, profit, CAC, ROAS, conversion rate, revenue per visitor and profit per visitor
GET /api/v1/stats/timeseriesPoints { t, v } for one metric: metric=visitors, pageviews, revenue_usd, profit_usd or signups, with interval=hour, day, week or month
GET /api/v1/stats/breakdownRows for one dimension: dim=channel, source, medium, campaign, term, content, ref_domain, path, entry_path, exit_path, hostname, country, region, city, device, browser, os or event, with limit and offset
GET /api/v1/stats/revenueMRR, new, expansion and churned MRR, refunds, revenue, fees, net revenue, customers, ARPU and LTV
GET /api/v1/stats/profitOne row per channel, source, medium and campaign with customers, revenue, fees, net revenue, spend, profit, CAC, ROAS and payback
GET /api/v1/stats/cohortsNet revenue retention cohorts, by=channel, source or campaign
GET /api/v1/stats/purchase-pathDays and visits to purchase, a weekday × hour heatmap of payments and revenue by device.

profit, cohorts and purchase-path need a plan that includes those reports (Growth and above); on a smaller plan they answer 403 with the code plan_required.

Parameters

ParameterRequiredMeaning
site_idyesThe site id from Settings → Sites. The key must belong to this site.
from, toyesDates as YYYY-MM-DD, inclusive, within your plan's history window
tznoAn IANA time zone; defaults to the site's reporting time zone
filtersnokey:operator:value triples separated by commas, such as channel:is:paid,path:contains:/blog.

Responses

Every response uses one envelope:

JSON
{ "ok": true, "data": { "visitors": 18240, "revenue_usd": 2407, "fees_usd": 152, "spend_usd": 1518, "profit_usd": 737 }, "meta": {} }

Amounts are USD at the payment date's ECB rate, with the definitions in How ProductLense counts. Unique visitor counts carry the approximation stated there. Errors use the shape on Errors.

Rate limit

Each key can make 120 requests a minute. Past that, the API answers 429 with the code rate_limited and a Retry-After header in seconds.

Example

const params = new URLSearchParams({ site_id: 'SITE_ID', from: '2026-09-01', to: '2026-09-30', dim: 'channel', limit: '10' });
const res = await fetch(`https://api.productlense.app/api/v1/stats/breakdown?${params}`, {
  headers: { Authorization: `Bearer ${process.env.PL_READ_KEY}` },
});
const { ok, data, error } = await res.json();
if (!ok) throw new Error(`${error.code}: ${error.message}`);
import os
import requests

res = requests.get(
    "https://api.productlense.app/api/v1/stats/breakdown",
    params={"site_id": "SITE_ID", "from": "2026-09-01", "to": "2026-09-30", "dim": "channel", "limit": 10},
    headers={"Authorization": f"Bearer {os.environ['PL_READ_KEY']}"},
    timeout=10,
)
body = res.json()
if not body["ok"]:
    raise RuntimeError(body["error"]["code"])

To ask questions in plain language from Claude, Cursor or ChatGPT instead, see Connect an AI assistant.

    ↑ ↓ to move, Enter to open