curl --request GET \
--url https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"data": [
{
"assistant": "ChatGPT",
"platform": "OpenAI",
"source": "chatgpt.com",
"medium": "referral",
"country": "DE",
"device": "mobile",
"event_name": "page_view",
"landing_page": "/pricing",
"page_path": "/blog/*/overview",
"host": "www.example.com",
"time_bucket": "2026-03-01",
"events": 4820,
"session_starts": 1402,
"conversions": 37,
"revenue": 4210.5
}
],
"columns": [
"assistant",
"session_starts"
],
"total_count": 12,
"currency": "EUR"
}{
"message": "<string>"
}Get AI Referrals
AI-referral traffic from the project’s connected Google Analytics property, scoped to the hosts the project owns and to referrals from AI assistants. There is no way to ask this endpoint for non-AI traffic.
To list the values a dimension takes, pass it as the only group_by and read the rows — that is how you discover event names, countries, pages and the assistants that actually sent traffic. When populating a filter, do not filter by the dimension you are listing, or the options the user has not already picked disappear.
Metric definitions, because the names are shorter than the meanings:
events: GA4 event count.session_starts: GA4session_startevents, which is not GA4’ssessionsmetric. Most are visits that entered on the site; the rest resumed there after idling past GA4’s session timeout. It cannot be combined withgroup_by=event_name.conversions: GA4 key events, so one visit can contribute several, and which events count follows whatever the property marked as a key event.revenue: GA4 purchase revenue in the property’s reporting currency, returned ascurrency.
Unknown dimension values come back as (not set). It is a real row, and the free-string filters (sources, devices, event_names, mediums) accept it back; countries does not, because it takes ISO codes only. Figures are a lower bound on AI referrals: only referrer-visible traffic reaches GA4, so native apps and no-referrer policies land as Direct, and Gemini is often folded into Organic Search.
curl --request GET \
--url https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.peec.ai/customer/v1/agent-analytics/google-analytics/referrals")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"data": [
{
"assistant": "ChatGPT",
"platform": "OpenAI",
"source": "chatgpt.com",
"medium": "referral",
"country": "DE",
"device": "mobile",
"event_name": "page_view",
"landing_page": "/pricing",
"page_path": "/blog/*/overview",
"host": "www.example.com",
"time_bucket": "2026-03-01",
"events": 4820,
"session_starts": 1402,
"conversions": 37,
"revenue": 4210.5
}
],
"columns": [
"assistant",
"session_starts"
],
"total_count": 12,
"currency": "EUR"
}{
"message": "<string>"
}Authorizations
Query Parameters
Required if using a company api key
"or_f45b94ba-5e35-4982-93ed-285e72ee14eb"
Inclusive bound, as full-date notation per RFC 3339 section 5.6. Required. A range spans at most 366 days.
^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$"2026-09-22"
Inclusive bound, as full-date notation per RFC 3339 section 5.6. Required. A range spans at most 366 days.
^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))$"2026-09-22"
Dimension(s) to break the numbers down by, at most 3. Omit for period totals only. Pass a single dimension to list the values it takes.
assistant, platform, source, medium, country, device, event_name, landing_page, page_path, host ["assistant"]
Metrics to return. Defaults to all of them, minus session_starts when grouping by event_name. Unrequested metrics are left out of the query.
events, session_starts, conversions, revenue ["session_starts", "conversions"]
Adds a time_bucket column holding the bucket's start date. There is no hourly bucket: the source data is daily. Buckets at either end of the range cover only the days inside it.
day, week, month "week"
Filter by assistant display name. An unrecognised name is rejected rather than ignored.
["ChatGPT"]
Filter by raw GA4 sessionSource host.
["chatgpt.com"]
Filter by ISO 3166-1 alpha-2 country code.
AE, AL, AM, AR, AT, AU, BA, BE, BG, BH, BO, BR, BS, BY, CA, CH, CL, CN, CO, CR, CY, CZ, DE, DK, DO, EC, EE, EG, ES, FI, FR, GB, GE, GH, GR, GT, HK, HN, HR, HU, ID, IE, IL, IN, IQ, IS, IT, JO, JP, KR, KW, LB, LI, LT, LU, LV, MA, MD, ME, MK, MN, MT, MX, MY, NG, NI, NL, NO, NZ, OM, PK, PA, PE, PH, PL, PT, PY, PS, QA, RO, RS, SA, SE, SG, SI, SK, SV, TH, TN, TR, TW, UA, US, UY, VE, VN, ZA, AD, AF, AS, AZ, BB, BQ, CG, CI, CM, CW, DM, DZ, FO, GF, GP, JM, KG, KH, KI, KZ, LK, LR, LS, MW, NC, PG, TD, TF, UG, VU, ZW ["DE"]
Filter by GA4 session medium. Use ai-assistant to isolate traffic identified by medium rather than by a known referrer host.
["ai-assistant"]
Filter by device category. Property-specific and case-sensitive: an unmatched value yields zeros rather than an error.
["mobile"]
Filter by GA4 event name. Property-specific: a name this property never sends yields zeros rather than an error, so discover them with group_by=event_name first.
["purchase"]
Field to sort by, which must be one of the requested metrics (or time_bucket when it is set). Defaults to the first requested metric.
events, session_starts, conversions, revenue, time_bucket "session_starts"
asc, desc Set false to skip total_count. Counting is a second pass over the same rows, so it doubles the cost of the call.
true, false "false"
1 <= x <= 10000 <= x <= 9007199254740991Response
Success
Success
Show child attributes
Show child attributes
Column order for the rows, derived from the request so a zero-row answer still carries its shape.
["assistant", "session_starts"]
Number of grouped rows the filters match — not a sum of any metric. Null when include_total_count was false.
12
Reporting currency of revenue, null when revenue was not requested. Values are not converted, so revenue is not comparable across projects.
"EUR"
