Get send volume over time for watched brands
/v1/email/competitive/volume-series// 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;
const report = await bird.email.competitive.volumeSeries({ range: 30, brand_ids: [watchlistBrandId] });
console.log(report);# Requires Insights preview access for the organization.
watchlist = client.email.competitive.watchlist.get(range=30)
entry = next((row for row in watchlist.data if row.name == "Everlane" and row.watchlist_brand_id), None)
if entry is None or entry.watchlist_brand_id is None:
raise ValueError("Add Everlane to the watchlist first")
watchlist_brand_id = entry.watchlist_brand_id
report = client.email.competitive.volume_series(range=30, brand_ids=[watchlist_brand_id])
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()
watchlist, err := client.Email.Competitive.Watchlist.Get(ctx, bird.EmailCompetitiveWatchlistGetParams{Range: 30})
if err != nil {
log.Fatal(err)
}
watchlistBrandID := ""
if watchlist.Data != nil {
for _, row := range *watchlist.Data {
if row.Name != nil && *row.Name == "Everlane" && row.WatchlistBrandId != nil {
watchlistBrandID = string(*row.WatchlistBrandId)
break
}
}
}
if watchlistBrandID == "" {
log.Fatal("Add Everlane to the watchlist first")
}
report, err := client.Email.Competitive.VolumeSeries(ctx, bird.EmailCompetitiveVolumeSeriesParams{Range: 30, BrandIDs: []bird.CompetitiveWatchlistBrandID{bird.CompetitiveWatchlistBrandID(watchlistBrandID)}})
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.
$watchlist = $bird->email->competitive->watchlist->get(['range' => 30]);
$watchlistBrandId = null;
foreach ($watchlist->getData() ?? [] as $row) {
if ($row->getName() === 'Everlane' && $row->getWatchlistBrandId() !== null) {
$watchlistBrandId = $row->getWatchlistBrandId();
break;
}
}
if ($watchlistBrandId === null) {
throw new \RuntimeException('Add Everlane to the watchlist first');
}
$report = $bird->email->competitive->volumeSeries(['range' => 30, 'brand_ids' => [$watchlistBrandId]]);
var_dump($report);bird email competitive volume-seriescurl -X GET "https://us1.platform.bird.com/v1/email/competitive/volume-series" \
-H "Authorization: Bearer $TOKEN" \
--url-query "range=30"{
"period": {
"days": 30,
"from": "2026-07-13T09:00:00Z",
"to": "2026-08-12T09:00:00Z"
},
"data": [
{
"watchlist_brand_id": "cwb_01krdgeqcxet5s7t44vh8rt9mg",
"is_workspace": false,
"name": "Everlane",
"sending_domains": [
"everlane.com"
],
"panel_status": "ok",
"source": "panel",
"points": [
{
"date": "2026-08-09",
"sends": 41800
}
]
}
]
}
Returns daily send volume for the watched brands you name, plus a line for your own sending, over one shared axis. Intended for a chart comparing a handful of competitors against yourself rather than the whole watchlist: each brand you name and each month of range adds to how long the request takes, so ask for the few you are plotting.
Competitor volume is an estimate from an email panel, fetched while the request
runs. Your own line counts messages accepted for delivery. The source field on each line records which of the two it is, and
the two are not measuring the same thing, so a chart putting them on one axis
should say so.
Every line carries one point per day of the period, oldest first, with a 0 for
a day nothing was observed, so the lines need no aligning before plotting. The last
point is the last whole UTC day, not the one in progress, so your own line and a
competitor's cover the same days. A line with no points at all has a panel_status
saying why.
API-key calls require Insights preview access for your organization.
Query Parameters
rangeintegerHow 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
brand_idsarrayWhich watched brands to plot, in the order you want the lines. Omit to get only your own line. An id your workspace does not watch is rejected rather than skipped, so a chart cannot quietly lose a line.
Response Payload
dataYour own line first, then the requested brands in the order they were asked for. Your line is present once your workspace has sent email. Every line carries the same days in the same order, so they can be plotted against one axis without aligning them first.
Related resources
Continue with the documentation, guides and examples for this topic.