Skip to main content

Analytics

Channel quality

How each traffic source behaves once it arrives — not how much it sends.

GET/sites/{siteId}/channels
Channel quality
curl --request GET \
  --url 'https://truestat.io/api/v1/sites/{siteId}/channels' \
  --header 'Authorization: Bearer <token>'
{
  "data": [
    {
      "channel": "Organic Search",
      "sessions": 237,
      "visitors": 205,
      "bounce_rate": 81,
      "return_rate": 13.5,
      "avg_session_seconds": 44,
      "avg_pages_per_session": 1.39
    }
  ],
  "meta": {
    "page": {
      "limit": 100,
      "offset": 0,
      "has_more": false
    },
    "range": {
      "to": "2026-09-16T00:00:00.000Z",
      "tz": "UTC",
      "from": "2026-09-09T00:00:00.000Z",
      "clamped": false
    },
    "site_id": "6fed8980-a119-4a9c-a4d4-53e448ef9f9e"
  }
}

What people from each source actually did: how long they stayed, how many pages they read, how many bounced, how many came back.

Volume is the less useful half

Breakdowns by channel

How much each source sent. Useful for "where does my traffic come from".

Channel quality

What they did after arriving. Useful for "which source is worth more effort".

A channel sending four hundred visitors who all leave immediately is worth less than one sending forty who read three pages each. A volume ranking puts those two in exactly the wrong order, which is why this report exists and why it is the one almost no analytics tool publishes.

Filters do not apply here

The shared filter_* parameters are accepted and ignored, deliberately.

The report's whole job is to compare channels. Narrow it to one and you have a single row with nothing to compare against — a chart with one bar. When you want a filtered view of one channel, use breakdowns with dimension=channel.

Direct is a real row

Traffic with no referrer is labelled Direct rather than left blank. It is a mixture: a bookmark, a typed address, a link opened from an app, a referrer the browser stripped for privacy.

None of that is an error, so it gets a name. A blank label reads as a bug in your own code.

Rates are percentages

bounce_rate and return_rate are 0–100, not fractions. That is different from ctr on the search endpoints, which is a fraction because Google reports it that way.

The two are deliberately not made to match: silently rescaling one of them by a hundred is a bug waiting to land in somebody's chart. Read each field as the reference describes it.

What the numbers say together

{ "channel": "Organic Search", "visitors": 205, "sessions": 237,
  "bounce_rate": 81, "return_rate": 13.5,
  "avg_pages_per_session": 1.39, "avg_session_seconds": 44 }

A high bounce rate with a short session is a mismatch between what the page promised and what it delivered. The same bounce rate with a long session usually is not — people found their answer on one page and left satisfied, which is a normal shape for documentation and tools.

Authorizations

Authorizationstringheaderrequired

An API key from Settings → API keys. Scopes available: analytics:read, sites:read, sites:write, settings:read, settings:write, goals:read, goals:write, funnels:read, funnels:write, notes:read, notes:write, alerts:read, alerts:write, team:read, team:write, revenue:read, revenue:write.

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Path parameters

siteIdstring (uuid)pathrequired
The site's id, from `GET /sites`. A site that is not yours and a site that does not exist both answer 403, so this cannot be used to discover other people's ids.

Query parameters

periodenum<string>query
A named window, resolved in `tz`. Cannot be combined with `from`/`to` — a request carrying both is refused rather than guessed at.
options:todayyesterdaylast24hlast7dlast30dlast12mmonthyearall
fromstring (date-time)query
Start of the window, ISO 8601 with an offset. Must be given together with `to`.
tostring (date-time)query
End of the window. Must be given together with `from`.
tzstringquerydefault: UTC
IANA timezone for day boundaries. `period=today&tz=Europe/Istanbul` means the reader's today, not the server's. Defaults to UTC.
botsenum<string>querydefault: excluded
Whether crawler traffic is counted. Excluded by default — the `/ai/*` endpoints report it separately and always include it.
options:excludedincluded
limitintegerquerydefault: 100
Rows per page, 1 to 1000.
offsetintegerquerydefault: 0
Rows to skip.

Response

application/json

The answer.

dataunknown
See the example.
metaobject