Sign inGet started

Get the campaigns a watched brand sent

GET
/v1/email/competitive/watchlist/brands/{watchlist_brand_id}/campaigns
// Requires Insights preview access for the organization.
const watchlist = await bird.email.competitive.watchlist.get({ range: 30 });
const entry = watchlist.data.find((row) => row.name === "Everlane" && row.watchlist_brand_id);
if (!entry?.watchlist_brand_id) throw new Error("Add Everlane to the watchlist first");
const watchlistBrandId = entry.watchlist_brand_id;
for await (const campaign of bird.email.competitive.watchlist.brands.campaigns.list(watchlistBrandId, { range: 30, limit: 25 })) {
  console.log(campaign.id);
}
Réponse200
{
  "period": {
    "days": 30,
    "from": "2026-07-13T09:00:00Z",
    "to": "2026-08-12T09:00:00Z"
  },
  "panel_status": "ok",
  "captured": 38,
  "promo_rate": 0.64,
  "truncated": false,
  "data": [
    {
      "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
    }
  ],
  "next_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9",
  "prev_cursor": null,
  "refresh_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"
}
Returns a page of campaigns an email panel observed a watched brand sending over the period, in the requested order. Sampled statistics describe eligible campaigns in the first 300 newest panel rows for each tracked domain, independently of the page.
Each campaign is one send the panel saw reach real inboxes, so the subject and timing are what the brand's subscribers received rather than anything the brand published. Volume and read rate are panel estimates, fetched while the request runs.
The panel folds a day's low-volume sending into a single synthetic entry with no creative and no volume. Those are left out, so the count here is lower than the number of rows the panel holds and describes campaigns a person would recognise as campaigns.
API-key calls require Insights preview access for your organization.
Paramètres
watchlist_brand_id
string
The watchlist entry whose campaigns to return.
Paramètres de requête
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
sort
string
Field to sort campaigns by.
Possible values: sent_at
order
string
Sort direction. Defaults to desc, which sorts from newest to oldest or largest to smallest, depending on the selected sort field.
Possible values: asc, desc
limit
integer
Maximum number of items to return per page.
starting_after
string
Cursor from the next_cursor field of a previous list response. Returns items immediately after the cursor position in the current sort order.
ending_before
string
Cursor from the prev_cursor or refresh_cursor field of a previous list response. Returns items immediately before the cursor position in the current sort order. prev_cursor returns the preceding page. refresh_cursor anchors at the first row of that response, which on a newest-first sort is how to fetch the items that have appeared since.
Contenu de la réponse
period
object
obligatoire
The rolling period used for this request.
Afficher les attributs enfants
period.days
integer
obligatoire
Length of the period in days.
period.from
string
obligatoire
Start of the period, inclusive.
period.to
string
obligatoire
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
obligatoire
For this campaign feed, no_data means the requested page is empty; it does not mean the whole period has no campaigns.
Possible values: ok, not_in_panel, no_data, unavailable
captured
integer
obligatoire
Number of eligible campaigns in the first 300 newest panel rows for each tracked domain. This sampled value is independent of the returned page.
promo_rate
nullable number
obligatoire
Fraction of captured campaigns whose subject leads with a discount. Null when captured is zero. This sampled value is independent of the returned page.
truncated
boolean
obligatoire
Whether the sampled statistics or returned page omit part of the requested collection. Use next_cursor to determine whether another page is available.
data
array of object
obligatoire
Campaigns in this page, in the requested order.
Afficher les attributs enfants
data.id
string
obligatoire
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.subject
string
obligatoire
The subject line the panel saw on this campaign.
data.sent_at
string
obligatoire
When the panel first saw this campaign arrive.
data.image_url
nullable string
obligatoire
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.reach
nullable integer
obligatoire
Estimated recipients this campaign reached, null when the panel observed the campaign but published no estimate for it.
data.read_rate
nullable number
obligatoire
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.has_creative
boolean
obligatoire
Whether the panel captured the rendered email for this campaign.
data.discount_percent
nullable number
obligatoire
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.inbox_rate
nullable number
obligatoire
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.spam_rate
nullable number
obligatoire
Share of this campaign that was filed as spam, null on the same terms.
next_cursor
nullable string
obligatoire
Cursor for the next page. Pass back as starting_after to advance forward. null when no next page exists.
prev_cursor
nullable string
obligatoire
Cursor for the previous page. Pass back as ending_before to step backward. null when no previous page exists.
refresh_cursor
nullable string
obligatoire
Refresh anchor, the first row of this response. Pass back as ending_before to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-null whenever data is non-empty; null only on an empty page. Distinct from prev_cursor.