Reports
Custom reports across your traffic, events and sources.
Aggregation#
GET/aggregations
Build a custom report. Group and filter on the fields you care about.
field_grouping) default to 500 rows unless you set limit. The maximum is 1000, and this endpoint has no pagination. BI tools such as Looker Studio / Data Studio that fire many aggregations at once will hit the concurrency cap, not the row cap. Space those requests, cache one extract per period, or raise the API plan.Goal codes are no longer available
entity_id with entity=event) is no longer available. Report on events using site_id and entity_name instead. Pageview reporting, where entity_id is the site id, is unaffected.Query parameters
- entitystringrequired
- The entity you want to report on. Events are treated separately from pageviews. Supported values:
pageviewandevent. - entity_idstringRequired when entity is "pageview"
- When
entityispageview, this is theidof the site you want to aggregate on. Whenentityisevent, do not pass a goal code here. Usesite_idandentity_nameinstead. Pageview usage is unchanged. - site_idstringRequired when entity is "event" and entity_id is omitted
- The
idof the site the event belongs to. - entity_namestringRequired when entity is "event" and entity_id is omitted
- The name of the event you want to report on. Example:
purchase. - aggregatesstringrequired
- The aggregates you wish to include, separated by a comma.
Supported values for pageview entities:visits,uniques,pageviews,avg_durationandbounce_rate. The difference between "visits" and "uniques" is that visits are unique site visits whilst uniques are unique page visits.
Supported values for event entities:conversions,unique_conversionsandvalue(value is returned in cents). - date_groupingstringoptional
- By default, we don't do any date grouping and return total aggregations. Override this with
hour,day,monthoryear. Note:hourgrouping is only supported for date ranges of up to 7 days. - field_groupingstringoptional
- The fields you want to group by, separated by a comma (e.g.
hostname,pathname). Supported values:hostname,pathname,entry_page,exit_page,referrer_hostname,referrer_pathname,referrer_source,ai_source,browser,country_code,city,state,region,device_type,operating_system,utm_campaign,utm_content,utm_medium,utm_source,utm_term,keywordandref. - sort_bystringoptional
- The field you want to sort by, in the format
field:asc|desc. You can use any field present inaggregatesorfield_grouping. When usingdate_grouping, you can also sort bytimestamp:ascortimestamp:desc.
Example:pageviews:desc - date_fromstringoptional
- Timestamp (e.g.
2022-04-01 15:31:00). Should match the timezone you're reporting in. Defaults to the entity's first recorded data. - date_tostringoptional
- Timestamp (e.g.
2022-04-01 15:31:00). Should match the timezone you're reporting in. Default: now. - timezonestringoptionaldeprecated
- Deprecated. We now report using each site's configured timezone by default. If provided, this TZ database name overrides the site's timezone for this request. We'll be removing this parameter in the future.
- limitintegeroptional
- Optional integer from
1to1000inclusive.
Default. Whenfield_groupingis set and you omitlimit, we return at most 500 rows. Whenfield_groupingis omitted, there is no default row cap.date_groupingalone does not trigger the 500 default.
Maximum. Alimitabove 1000 returns HTTP 400 with anerrors.limitvalidation message. There is no pagination on this endpoint, so you cannot walk past 1000 grouped rows with a cursor. Narrow the result withfiltersand/or a shorterdate_from/date_to, then issue more requests. - filtersarrayoptional
- A JSON-encoded array of filter objects. See the filtering reference below for the full list of supported properties and operators. Each filter's
valuemust be a string.
Filtering
Filters are supplied as a JSON array. Each filter is an object with a property, an operator and a string value. You can add as many filters as you like; see the examples in the code panel.
We support the following operators:
is: exact matchis not: everything except an exact matchis like: contains the term (supports wildcards*)is not like: does not contain the termmatching: matches a regular expression (regex) patternnot matching: does not match a regex pattern
Operator availability depends on the field. Text-style fields support all six operators; categorical fields support only is and is not:
- All six operators:
domain,hostname,pathname,entry_page,exit_page,referrer_hostname,referrer_pathname,referrer_source,ai_source,ref,utm_campaign,utm_source,utm_medium,utm_content,utm_term is/is notonly:device_type,operating_system,browser,country_code,city,state,region
Note: domain can be filtered on but not grouped by, while keyword can be grouped by but not filtered on.
Entry and exit pages
entry_page is the pathname of the first pageview in a visit. exit_page is the pathname of the last pageview before the visitor leaves. Both are session-level fields. They mirror the Entry Pages and Exit Pages reports on your dashboard and work for both field_grouping and filters.
When you filter by entry_page, only visits that entered on that page are included. A visitor who lands on /home and later views /pricing is excluded by {"property": "entry_page", "operator": "is", "value": "/pricing"}, but included when filtering on pathname instead.
AI tools
referrer_source returns AI for visits from AI tools such as ChatGPT, Claude and Perplexity. ai_source returns the name of the AI tool, the same names you see in the AI tab on your dashboard (for example ChatGPT, Claude, Perplexity or Gemini). Each name combines the tool's referrers and its utm_source links, so ChatGPT includes chatgpt.com, chat.openai.com and utm_source=chatgpt.com. Both work for field_grouping and filters, for pageviews and events, and on all of your history.
When you group by ai_source alone, you get one row for each AI tool and visits from anywhere else are left out. When you group it with other fields, those other visits come back with an empty ai_source. Read Traffic sources for how we decide a visit came from AI.
Regex examples
With matching / not matching you can build sophisticated filters:
^/(about|contact|pricing)$: match only /about, /contact and /pricing^/(about|contact|pricing): match paths starting with those^/blog/\d{4}/\d{2}/: match blog URLs like /blog/2025/07/my-post^/products/[^/]+/$: match product category pages
Returns
Returns an array of objects. The properties of each object vary based on the aggregates and groupings you've asked for. All numeric values are returned as strings.
curl "https://api.usefathom.com/v1/aggregations?entity=pageview&aggregates=pageviews" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"[
{
"browser": "Chrome",
"device_type": "Desktop",
"pageviews": "30000"
},
{
"browser": "Chrome",
"device_type": "Mobile",
"pageviews": "28000"
},
{
"browser": "Chrome",
"device_type": "Tablet",
"pageviews": "24000"
},
{
"browser": "Firefox",
"device_type": "Desktop",
"pageviews": "12000"
},
{
"browser": "Firefox",
"device_type": "Tablet",
"pageviews": "5000"
},
{
"browser": "Firefox",
"device_type": "Mobile",
"pageviews": "300"
}
][
{
"property": "pathname",
"operator": "is",
"value": "/about"
}
][
{
"property": "pathname",
"operator": "is not",
"value": "/register"
},
{
"property": "pathname",
"operator": "is not",
"value": "/login"
}
][
{
"property": "country_code",
"operator": "is",
"value": "GB"
}
][
{
"property": "pathname",
"operator": "is not",
"value": "/register"
},
{
"property": "device_type",
"operator": "is not",
"value": "Desktop"
}
][
{
"property": "entry_page",
"operator": "is",
"value": "/pricing"
}
][
{
"property": "ai_source",
"operator": "is",
"value": "ChatGPT"
}
]Current visitors#
GET/current_visitors
Returns the total number of current visitors on a site. The detailed view also returns the top 150 pages and top 150 referrers.
Query parameters
- site_idstringrequired
- The
idof the site.
Example:CDBUGS - detailedbooleanoptional
- Set to
truefor a detailed breakdown of pages and referrers. Otherwise you'll only get a count.
Returns
The current visitor count, with an optional detailed breakdown.
curl "https://api.usefathom.com/v1/current_visitors?site_id=CDBUGS" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"{
"total": 14
}{
"total": 144,
"content": [
{
"hostname": "bugsbunny.com",
"pathname": "/spacejam",
"total": 100
},
{
"hostname": "bugsbunny.com",
"pathname": "/blog/being-a-wabbit",
"total": 44
}
],
"referrers": [
{
"referrer_hostname": "https://usefathom.com",
"referrer_pathname": "/why-we-love-bugs-bunny",
"total": 50
},
{
"referrer_hostname": "https://daffyduck.com",
"referrer_pathname": "/blog/i-am-sick-of-his-antics",
"total": 32
}
]
}