Get the notable campaigns across watched brands
GET
/v1/email/competitive/watchlist/notable
// Requires Insights preview access for the organization.
const report = await bird.email.competitive.watchlist.notable({ range: 30 });
console.log(report);# Requires Insights preview access for the organization.
report = client.email.competitive.watchlist.notable(range=30)
print(report.model_dump_json())// Requires Insights preview access for the organization.
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
report, err := client.Email.Competitive.Watchlist.Notable(ctx, bird.EmailCompetitiveWatchlistNotableParams{Range: 30})
if err != nil {
log.Fatal(err)
}
encoded, err := json.MarshalIndent(report, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(encoded))// Requires Insights preview access for the organization.
$report = $bird->email->competitive->watchlist->notable(['range' => 30]);
var_dump($report);bird email competitive watchlist notablecurl -X GET "https://us1.platform.bird.com/v1/email/competitive/watchlist/notable" \
-H "Authorization: Bearer $TOKEN" \
--url-query "range=30"Response200
{
"period": {
"days": 30,
"from": "2026-07-13T09:00:00Z",
"to": "2026-08-12T09:00:00Z"
},
"panel_status": "ok",
"data": [
{
"watchlist_brand_id": "cwb_01krdgeqcxet5s7t44vh8rt9mg",
"brand_name": "Allbirds",
"signal": "read_rate_standout",
"claim": {
"text": "Biggest send in 7 days"
},
"evidence": {
"ratio_to_median": 29.61,
"read_rate_observations": 66,
"mailbox_provider_spam_rate": 0.79,
"mailbox_provider_observations": 199
},
"mailbox_provider": "gmail",
"campaign": {
"id": "3914827265",
"subject": "The Summer Sale: 40% off everything",
"sent_at": "2026-08-09T14:02:00Z",
"image_url": "https://images.example.com/creatives/c154c8c4-6356-40e6-92d2-7c6727ec36ca.jpg",
"reach": 410000,
"read_rate": 0.228,
"has_creative": true,
"discount_percent": 40,
"inbox_rate": 0.879,
"spam_rate": 0.121
}
}
],
"truncated": false
}
Returns the campaigns worth a second look across every brand the workspace watches,
surfaced for what they did rather than for when they were sent.
Each campaign carries the signal that surfaced it: an unusually big send for its brand,
a campaign read unusually well for its brand, or heavy spam placement at one mailbox
provider. Every signal compares a campaign against its own brand's history, never
against your other brands, so several brands can carry the same signal in one period.
Up to 100 findings are returned. Selection takes turns across brands in watchlist order
until the response is full, prioritizing spam placement, biggest sends, then read-rate
standouts within a brand. Returned findings retain watchlist, signal, domain, and source
order. Your own sending is never included.
An empty list is an ordinary answer, not a failure: a signal only fires on a campaign
that stands out for its own brand, and a watchlist of steady senders produces nothing.
Check panel_status to tell that apart from the panel being unreachable.
This is a separate request from the watchlist on purpose, so a slow or degraded panel
read cannot delay the watchlist itself.
API-key calls require Insights preview access for your organization.
Query Parameters
range
integer
How many days back the response covers, counting from now. One of three fixed trend windows rather than an open date range, matching how a competitive-intelligence chart is read. Defaults to 30.
Possible values: 7, 30, 90
Response Payload
period
object
required
The period the campaigns were observed in, as the panel resolved it.
Two things differ from the other competitive reads. It ends at the last instant of
the previous whole day rather than at the moment of the request, so a campaign sent
this morning is never among these. And the panel holds its selection for a period
once it has made it, so two requests a minute apart return the same campaigns rather
than differing slightly.
Show child attributes
period.days
integer
required
Length of the period in days.
period.from
string
required
Start of the period, inclusive.
period.to
string
required
End of the period, exclusive. Daily figures therefore run through the previous whole UTC day and never include the one in progress.
panel_status
string
required
Whether the panel could be read for this feed, and when it could not, why.
Possible values: ok, not_in_panel, no_data, unavailable
data
array of object
required
Up to 100 campaigns selected across watched brands. Selection takes turns across
brands in watchlist order until the response is full, prioritizing spam placement,
biggest sends, then read-rate standouts within each brand. Within one signal, rows
compare the matching spam rate, volume ratio, or read rate descending; missing values
sort last and ties retain tracked-domain and source order. Selected rows are returned
in watchlist order, then biggest-send, read-rate, and spam signal order, followed by
tracked-domain and source order.
One campaign may appear once per signal because each row carries different evidence.
Empty when nothing qualified; check panel_status to distinguish that from an
unavailable panel.
Show child attributes
data.watchlist_brand_id
string
required
The watchlist entry that sent it.
data.brand_name
string
required
The brand's name.
data.signal
string
required
Why this campaign was surfaced. The set is open and grows as new signals are added.
Every signal describes the campaign against its own brand's history, never against the
other brands you watch, so several brands can carry the same signal in one period and
none of them is the top of anything.
biggest_send is a send far above that brand's own median: unusual for the brand, not
merely large. read_rate_standout is a campaign read unusually well for its brand.
landing_in_spam is one heavily filed as spam at a single mailbox provider, named in
mailbox_provider, which is worth seeing even when the brand's overall placement looks healthy.
Possible values (may grow over time): biggest_send, read_rate_standout, landing_in_spam
data.claim
nullable object
required
The panel's own headline for this campaign, or null where it surfaced the campaign without making one. Null is the common case and is not a fault.
Show child attributes
data.claim.text
string
required
The panel's own phrasing, which may name the window the claim was measured over ("Biggest send in 7 days") or not ("Best-read campaign"). Show it as written rather than rebuilding it from the signal, and do not parse a window out of it.
data.evidence
object
required
The figures behind the signal, for ordering or filtering the list.
Show child attributes
data.evidence.ratio_to_median
nullable number
required
How many times the brand's own median volume this send was. A value of 29 means the send was twenty-nine times the brand's typical volume for the period. Null on every signal other than biggest_send, and on a biggest_send campaign the panel published no ratio for.
data.evidence.read_rate_observations
nullable integer
required
How many panel observations campaign.read_rate was measured over. A rate over thirty observations and one over a hundred and forty are not equally worth showing, and this is what separates them. Null on every signal other than read_rate_standout, and on a read_rate_standout campaign the panel published no denominator for.
data.evidence.mailbox_provider_spam_rate
nullable number
required
Share of this campaign filed as spam at the one provider named in mailbox_provider, as a value between 0 and 1. A different measurement from the campaign's overall spam_rate, and the one this signal is about. Null on every signal other than landing_in_spam, and on a landing_in_spam campaign whose provider counts the panel did not publish, so a spam entry can arrive without the rate behind it.
data.evidence.mailbox_provider_observations
nullable integer
required
How many observations at that provider mailbox_provider_spam_rate was measured over. Null on the same terms.
data.mailbox_provider
nullable string
required
The provider a landing_in_spam campaign was heavily filed as spam at: spam placement is measured per provider, and this campaign's problem is at one of them. Null on every other signal.
data.campaign
object
required
The campaign itself. Two of its fields behave differently here than on the brand's
campaign feed, because the panel sends less about a campaign it surfaced this way.
has_creative reports whether a capture came back with this entry rather than whether
the panel ever captured the email, and captures are frequently absent here by design, so
expect false on campaigns the panel did image. reach is null on every entry, because
the panel does not estimate an audience for the campaigns it surfaces.
Show child attributes
data.campaign.id
string
required
The identifier for this campaign. Use it to fetch this one campaign on its own.
It is a string, and it needs to stay one. The values are long enough that
JavaScript, and any other language that stores every number as a floating point
value, will round them, and a rounded identifier matches no campaign at all.
Compare it and pass it back as text.
data.campaign.subject
string
required
The subject line the panel saw on this campaign.
data.campaign.sent_at
string
required
When the panel first saw this campaign arrive.
data.campaign.image_url
nullable string
required
Where the panel's capture of the rendered email can be fetched, null when it captured none. Panels image only some of what they observe, so an absent creative is an ordinary outcome rather than a failed one. The image is served from the panel's own host rather than from ours, so a page embedding it has to allow that host.
data.campaign.reach
nullable integer
required
Estimated recipients this campaign reached, null when the panel observed the campaign but published no estimate for it.
data.campaign.read_rate
nullable number
required
Estimated share of recipients who read this campaign, null when the panel published no rate for it. Panel read rates count dwell time, so they do not move with the automatic opens that inflate a sender's own open rate.
data.campaign.has_creative
boolean
required
Whether the panel captured the rendered email for this campaign.
data.campaign.discount_percent
nullable number
required
The discount the subject line leads with, null when it names none. Read from the subject text, so it finds a stated offer and not one revealed inside the email.
data.campaign.inbox_rate
nullable number
required
Share of this campaign that reached an inbox, null when the panel observed it without recording where it landed. It describes this send rather than the brand's domain, so a single bad campaign is visible against a brand whose overall placement still looks healthy.
data.campaign.spam_rate
nullable number
required
Share of this campaign that was filed as spam, null on the same terms.
truncated
boolean
required
Whether Bird omitted eligible panel findings to keep this response to 100 rows. False does not promise that the panel observed every qualifying campaign in the period.
Related resources
Continue with the documentation, guides and examples for this topic. Resources are in English.
Understand the conceptShould I use a Bird SDK or call the API directly?Follow the learning pathBuild your first integrationImplementation guideSend your first email
Get an implementation brief