curl --request GET \
--url https://brew.new/api/v1/analytics/events \
--header 'Authorization: Bearer <token>'import requests
url = "https://brew.new/api/v1/analytics/events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://brew.new/api/v1/analytics/events', 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://brew.new/api/v1/analytics/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://brew.new/api/v1/analytics/events"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://brew.new/api/v1/analytics/events")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/analytics/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "evt_abc",
"occurredAt": "2026-04-08T12:34:56.789Z",
"domain": "email",
"eventType": "opened",
"recipientEmail": "jane@example.com",
"sendId": "r4Kq8ZxL2mW7nT1vPb9Yc",
"emailId": "qkE3pWv2xN9dLmR4tYbZ7",
"emailName": "Spring Launch"
}
],
"pagination": {
"limit": 50,
"cursor": null,
"hasMore": false
},
"range": {
"from": "2026-04-01T12:34:56.789Z",
"to": "2026-04-08T12:34:56.789Z"
}
}Unified events feed
Read-only feed of the brand’s analytics events (email, automation, trigger, inbound), newest first, over a from/to window that defaults to the last 7 days. Email rows carry the send behind them (sendId, emailId), so a page joins back to GET /v1/sends/{sendId}.
Use when you need per-event or per-recipient detail. For windowed totals and rates use getAnalyticsOverview; for one send’s feed use listSends with ?sendId=&include=events.
Input Equality filters: eventType, sendId, emailId, automationId, messageClass (marketing or transactional; absent stamps count as marketing). Run scope: automationRunId returns one run’s events across every domain (its lifecycle rows and the deliveries it produced), newest first and not windowed unless from/to are given; only eventType combines with it. Recipient rules recipient (csv, max 10): a full address matches exactly, @domain matches everyone on the domain, other text matches as a substring, a ! prefix excludes (recipient=@clay.com,!ceo@clay.com); includes OR together, excludes always apply. Send facets source (csv), audienceId (csv, max 20), domain (sending domain), triggerEventId (csv, max 10, resolved to the automations they wire). Any facet or recipient rule narrows the feed to email events and adds sendSource, sendContext and messageClass to each row (plus triggerProvider and triggerTitle on triggered rows). Machine-classified opens and clicks (scanner detonation, Apple proxy prefetch) are excluded unless includeMachineOpens=true or includeMachineClicks=true (audit only).
Grouped counts groupBy (one or two csv fields: eventType, emailId, automationId, sendId, source, link, recipientDomain, unsubscribeReason) and/or bucket (day, week or month, UTC, Monday weeks) count the email events the same filters list instead of returning them: link with eventType=clicked is clicks per link, unsubscribeReason counts an unsubscribe once per reason it gave. The count reads the newest 20,000 events; a busier window answers truncated: true with coveredFrom (narrow from/to or add eventType). Events, not unique recipients: getAnalyticsOverview opened/clicked count unique human recipients.
Returns 200 { data: Event[], pagination: { limit, cursor, hasMore }, range }; page with cursor until it is null. Rows carry the run join keys when stamped: automationRunId, triggerInstanceId, audienceRunId (plus sendId, nodeId). With groupBy/bucket: 200 { count, groups: [{ key, bucket?, count }], otherCount, range, truncated, coveredFrom? }, the largest 200 groups first. Requires the emails scope.
Errors 400 INVALID_REQUEST for an inverted window, an unknown eventType, or more than 10 recipient rules or 20 audience ids; with groupBy/bucket, for an unknown or repeated field, more than two fields, a non-email eventType, cursor or automationRunId (limit is ignored).
See also getAnalyticsOverview, listSends, listAutomationRuns.
curl --request GET \
--url https://brew.new/api/v1/analytics/events \
--header 'Authorization: Bearer <token>'import requests
url = "https://brew.new/api/v1/analytics/events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://brew.new/api/v1/analytics/events', 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://brew.new/api/v1/analytics/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://brew.new/api/v1/analytics/events"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://brew.new/api/v1/analytics/events")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/analytics/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "evt_abc",
"occurredAt": "2026-04-08T12:34:56.789Z",
"domain": "email",
"eventType": "opened",
"recipientEmail": "jane@example.com",
"sendId": "r4Kq8ZxL2mW7nT1vPb9Yc",
"emailId": "qkE3pWv2xN9dLmR4tYbZ7",
"emailName": "Spring Launch"
}
],
"pagination": {
"limit": 50,
"cursor": null,
"hasMore": false
},
"range": {
"from": "2026-04-01T12:34:56.789Z",
"to": "2026-04-08T12:34:56.789Z"
}
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Headers
The brand this request acts on. REQUIRED for organization-scoped credentials (otherwise 400 BRAND_ID_REQUIRED — there is no default brand); list ids with GET /v1/brands. Brand-scoped credentials may omit it, and sending a different brand returns 403 BRAND_SCOPE_MISMATCH. A brand outside your organization returns 404 BRAND_NOT_FOUND.
1 - 64Query Parameters
CSV of recipient rules (max 10) matching who RECEIVED the email: a full address matches exactly, @domain matches the domain, any other text matches as a substring; prefix ! to exclude (e.g. @clay.com,!ceo@clay.com). Includes OR together; excludes always apply. Narrows the feed to email events.
1 - 20481 - 641 - 641 - 64One automation run’s events across every domain, newest first. Not windowed unless from/to are given; only eventType combines with it.
1 - 64Admission snapshot on the send behind the event (marketing | transactional). Same event object for both classes; absent stamps match as marketing. Narrows the feed to email events.
marketing, transactional Send-object facet: CSV of send sources (valid values: audience, api, automation_manual, automation_integration, automation_custom). Narrows the feed to email events.
1 - 256Send-object facet: CSV of audience ids (max 20) — events from the audiences' sends. Narrows the feed to email events.
1 - 2048Send-object facet: one email design id. Narrows the feed to email events.
1 - 64Send-object facet: a sending domain (fromEmail match). Narrows the feed to email events.
1 - 255Send-object facet: CSV of integration trigger-event ids (max 10), resolved to their wired automations. Narrows the feed to email events.
1 - 2048Machine/bot-classified clicked rows are excluded by default. Pass true to include the raw rows (they carry machineGenerated: true + a clickBotReason; audit/debug only).
Machine/bot-classified opened rows (security-scanner pixel detonation, Apple-proxy prefetch) and still-classifying opens are excluded by default. Pass true to include the raw rows (they carry machineGenerated + an openBotReason and openFetchSource; audit/debug only).
Page size (1-100). Defaults to 100.
1 <= x <= 1001 - 8192Count the email events these filters list per value of one or two comma-separated fields instead of returning them: eventType, emailId, automationId, sendId, source, link, recipientDomain, unsubscribeReason.
1 - 200Count the email events these filters list per UTC day, week (Monday start) or month; combines with groupBy.
day, week, month Was this page helpful?