Skip to main content

AI

Traffic split

Every operator's share of your traffic, with people counted as one of them.

GET/sites/{siteId}/ai/split
Traffic split
curl --request GET \
  --url 'https://truestat.io/api/v1/sites/{siteId}/ai/split' \
  --header 'Authorization: Bearer <token>'
{
  "data": [
    {
      "views": 3143,
      "operator": "bytedance"
    },
    {
      "views": 752,
      "operator": "gpt"
    },
    {
      "views": 518,
      "operator": "human"
    }
  ],
  "meta": {
    "range": {
      "to": "2026-09-16T13:40:00.000Z",
      "tz": "UTC",
      "from": "2026-09-09T13:40:00.000Z",
      "clamped": false
    },
    "site_id": "6fed8980-a119-4a9c-a4d4-53e448ef9f9e"
  }
}

How your traffic divides between the machines and the people — as one list, with human among the rows.

human is a row, and that is the point

The response looks odd at first: bytedance, gpt, claude, and then human sitting in the same list.

It is there because of the question this endpoint answers. "How much of what reached my site was a person" is a ratio, and a ratio needs both sides in the same response. Return only the crawlers and every percentage a caller computes is a crawler's share of the crawlers — a number that looks like an answer and is not.

That is not hypothetical. This report was once built on a query that filtered to crawler traffic, and the dashboard card showed each bot's share of the bots while presenting it as a share of the traffic. The human side was not small; it was absent.

Every operator comes back

limit and offset are ignored here. Everywhere else in the API a page cut short is a shorter answer, and meta.page.has_more tells you to ask again.

In a ratio it is worse than that: a row dropped from the bottom does not shorten the result, it silently changes every percentage above it. So the list is complete and there is no meta.page on this endpoint.

There is no purpose here

Rows group by operator alone. That is what lets human sit among them — a person has no crawl purpose, and inventing one for them would be the sort of tidy schema that stops describing reality.

For the split by purpose — answering, indexing, training — call crawlers, which groups by both. The two are consistent: an operator's views here equals the sum of its rows there.

A worked example

{
  "data": [
    { "operator": "bytedance", "views": 3143 },
    { "operator": "gpt",       "views": 752  },
    { "operator": "human",     "views": 518  }
  ]
}

Bytedance alone fetched six times what every human visitor viewed. That is a common shape on a small site, and it is the number that explains why a page can feel busy in the logs and quiet in the analytics.

Crawler traffic is always counted here, whatever bots says. See crawlers.

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
filter_pathstringquery
Narrow the result to one `path`.
filter_path_patternstringquery
Narrow the result to one `path_pattern`.
filter_hostnamestringquery
Narrow the result to one `hostname`.
filter_entry_pathstringquery
Narrow the result to one `entry_path`.
filter_exit_linkstringquery
Narrow the result to one `exit_link`.
filter_file_downloadstringquery
Narrow the result to one `file_download`.
filter_goalstringquery
Narrow the result to one `goal`.
filter_browserstringquery
Narrow the result to one `browser`.
filter_osstringquery
Narrow the result to one `os`.
filter_devicestringquery
Narrow the result to one `device`.
filter_countrystringquery
Narrow the result to one `country`.
filter_regionstringquery
Narrow the result to one `region`.
filter_citystringquery
Narrow the result to one `city`.
filter_referrerstringquery
Narrow the result to one `referrer`.
filter_channelstringquery
Narrow the result to one `channel`.
filter_utm_sourcestringquery
Narrow the result to one `utm_source`.
filter_utm_mediumstringquery
Narrow the result to one `utm_medium`.
filter_utm_campaignstringquery
Narrow the result to one `utm_campaign`.
filter_gad_campaign_idstringquery
Narrow the result to one `gad_campaign_id`.
filter_utm_termstringquery
Narrow the result to one `utm_term`.
filter_bot_kindstringquery
Narrow the result to one `bot_kind`.

Response

application/json

The answer.

dataunknown
See the example.
metaobject