Glai

Docs

Stats API

Pull a site's numbers as JSON: totals, a time series, breakdowns by source, page, country and more, and goals. The same filters as the dashboard, so what you build matches what you see.

Authentication

Create a key under Settings → API keys and send it as a bearer token. A key belongs to one workspace, can read every site in it, and is shown once, when it is created. The API is included in every paid plan.

Header
Authorization: Bearer gl_live_xxxxxxxxxxxxxxxxxxxxxxxx

Base URL: https://glai.io

GET

/api/v1/stats

Everything the dashboard shows for one site and one date range, in one call. Name the site by its public id or its domain. Add one or more filters and every number in the answer is narrowed to the sessions that match (a source=Google filter shows Google's pages, countries and revenue).

FieldTypeDescription
siterequiredstringThe site's public id or its domain (with or without www).
rangestringtoday, yesterday, 7d, 30d (default), 90d, 12m, month, last_month, all, or custom with from and to. Days are in the site's time zone.
from, toYYYY-MM-DDThe first and last day of a custom range.
dimsstringComma-separated breakdowns to return (see Dimensions). Up to 20.
limitnumberRows per breakdown, 1 to 200. Default 30.
<filter>stringAny filter key with one value, e.g. source=ChatGPT&country=AU (see Filters).

Filters

One value per key, exact match. Use (none) to select sessions where the value is unknown. Keys: source, source_type, referrer_host, country, region, city, device, browser, os, utm_source, utm_medium, utm_campaign, utm_content, utm_term, entry_path, exit_path, path, event. path selects sessions that viewed that page and event sessions that fired that event.

Dimensions

dims is a comma-separated list of the breakdowns to return, up to 20: source, source_type, referrer_host, path, entry_path, exit_path, country, region, city, device, browser, os, language, utm_source, utm_medium, utm_campaign, utm_content, utm_term, event, outbound. Without it the dashboard's default set is returned.

Request
curl "https://glai.io/api/v1/stats?site=acme.com&range=30d&dims=source,path&source_type=ai" \
  -H "Authorization: Bearer $GLAI_API_KEY"
Response (shortened)
{
  "statusCode": 200,
  "success": true,
  "message": "Stats.",
  "data": {
    "site": { "id": "k3m9p2x7q4wz", "name": "Acme", "domain": "acme.com", "time_zone": "Australia/Sydney", "currency": "USD" },
    "range": { "key": "30d", "from": "2026-09-15T14:00:00.000Z", "to": "2026-10-14T23:12:08.113Z", "bucket": "day" },
    "filters": { "source_type": "ai" },
    "totals": {
      "current":  { "visitors": 3317, "sessions": 4021, "pageviews": 9870, "events": 412, "bounce_rate": 38.2, "avg_duration_s": 94,
                    "revenue_cents": 152000, "payments": 12, "customers": 11, "revenue_per_visitor_cents": 46, "conversion_rate": 0.33 },
      "previous": { "visitors": 2480, "…": "…" }
    },
    "series": { "bucket": "day", "points": [ { "date": "2026-09-15", "visitors": 88, "sessions": 101, "pageviews": 240, "revenue": 4900, "payments": 1 }, "…" ] },
    "breakdowns": {
      "source": [ { "key": "ChatGPT", "label": "ChatGPT", "visitors": 2210, "sessions": 2704, "pageviews": 6410, "revenue_cents": 98000, "customers": 7, "share": 66.6, "icon": "logos:openai-icon", "type": "ai" }, "…" ],
      "path":   [ { "key": "/pricing", "label": "/pricing", "visitors": 1402, "pageviews": 1890, "revenue_cents": 0, "customers": 0, "share": 42.3 }, "…" ]
    },
    "goals": [ { "id": "…", "name": "Signed up", "type": "EVENT", "event_name": "signup", "path_pattern": null, "is_active": true, "value_cents": null,
                 "visitors": 61, "conversions": 63, "value_cents_total": 0, "conversion_rate": 1.84 } ]
  }
}

The response

The body is { statusCode, success, message, data }. Inside data:

FieldTypeDescription
siterequiredobject{ id, name, domain, time_zone, currency }. Revenue is in the site's currency, in cents.
rangerequiredobject{ key, from, to, bucket }: the range as resolved, with ISO instants.
filtersrequiredobjectThe filters that were applied, as sent.
totalsrequiredobject{ current: Totals, previous: Totals }.
seriesrequiredobject{ bucket, points: Point[] }.
breakdownsrequiredobject{ <dim>: BreakdownRow[] } for every dimension asked for.
goalsrequiredGoal[]The site's goals with their conversions in the range.

Totals

totals.current and totals.previous (the same length of time just before the range) both hold:

FieldTypeDescription
visitorsrequirednumberDistinct visitors in the range.
sessionsrequirednumberVisits. A visit ends after the site's session gap without an event.
pageviewsrequirednumberPageviews.
eventsrequirednumberCustom events and outbound clicks.
bounce_raterequirednumberPercent of visits with one pageview and no event (0 to 100).
avg_duration_srequirednumberAverage visit length in seconds.
revenue_centsrequirednumberPayments minus refunds, in cents of the site's currency.
paymentsrequirednumberNumber of payments.
customersrequirednumberDistinct paying customers.
revenue_per_visitor_centsrequirednumberrevenue_cents divided by visitors.
conversion_raterequirednumberCustomers as a percent of visitors (0 to 100).

Series

series.bucket is hour for one day, day up to about 13 months, month beyond. series.points has every bucket of the range, empty ones included:

FieldTypeDescription
daterequiredstring"2026-10-14" for a day, "2026-10-14T09" for an hour, "2026-10" for a month, in the site's time zone.
visitors, sessions, pageviewsrequirednumberCounts in that bucket.
revenue, paymentsrequirednumberRevenue in cents (minus refunds) and number of payments in that bucket.

Breakdown rows

Each breakdowns.<dim> is a list of rows sorted by visitors, with an unknown value shown as (none):

FieldTypeDescription
keyrequiredstringThe raw value: "Google", "/pricing", "AU", "desktop", "(none)".
labelrequiredstringThe value to show ("Unknown" for (none)).
visitorsrequirednumberDistinct visitors with that value.
sessions, pageviews, eventsnumberWhichever the dimension counts: sessions for session dimensions, pageviews for path, events for event and outbound.
revenue_cents, customersrequirednumberRevenue and paying customers whose first visit had that value (source, source_type, country, device, utm_source, utm_medium, utm_campaign; 0 elsewhere). For the event dimension, the value passed with the event.
sharerequirednumberPercent of the range's visitors (0 to 100).
icon, typestring | nullFor the source dimension: the source's icon name and type (search, social, ai, email, paid, referral).

Goals

One entry per goal of the site, with its conversions in the range:

FieldTypeDescription
id, name, typerequiredstringtype is EVENT (an event name) or PAGE (a path pattern such as /thanks*).
event_name, path_patternrequiredstring | nullWhat the goal matches.
is_active, value_centsrequiredboolean, number | nullWhether it counts, and a fixed value per conversion when set.
visitors, conversionsrequirednumberDistinct visitors who converted, and the number of conversions.
value_cents_total, conversion_raterequirednumberValue of the conversions, and converting visitors as a percent of all visitors.

Errors and limits

Errors use standard HTTP status codes and a JSON body with a readable message.

StatusMeaning
400A parameter is missing or invalid (an unknown range, a bad date). The body names it.
401The API key is missing, wrong, revoked or expired.
402The workspace's plan does not include the API.
404No site with that id or domain in the key's workspace.
429Rate limit reached. Wait a minute and retry.

Rate limit: 300 requests a minute per workspace. Each request may name up to 20 dimensions and return up to 200 rows per dimension (limit), so one call a minute is usually enough for a dashboard.