Get send volume over time for watched brands
GET
/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"Réponse200
{
"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.
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
brand_ids
array
Which 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.
Contenu de la réponse
period
object
obligatoire
The period every line covers.
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.
data
array of object
obligatoire
Your 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.
Afficher les attributs enfants
data.watchlist_brand_id
string
The watchlist entry this line describes. Absent on your own line, which is not a watchlist entry.
data.is_workspace
boolean
obligatoire
True on the line describing your own workspace's sending.
data.name
string
obligatoire
Label for the line: the brand's name, or your sending domain on your own line.
data.sending_domains
array of string
obligatoire
The sending domains the line's figures describe. Always one domain today.
data.panel_status
string
obligatoire
Why a line has no volume in it. Always ok on your own line, which is counted rather than read from the panel.
Possible values: ok, not_in_panel, no_data, unavailable
data.source
string
obligatoire
Where the line came from. Your own is an exact count of what was accepted for delivery; a competitor's is the panel's estimate of everything they sent. The two share an axis while resting on different measurements, so a chart that compares them should say so.
Possible values: measured, panel, none
data.points
array of object
obligatoire
One point per day of the period, oldest first, ending with the last whole UTC day rather than the one in progress. A domain the panel tracks but observed nothing for plots as zeros, which is a measured silence rather than a missing measurement. Points are empty only when there was nothing to plot at all, reported by panel_status as not_in_panel or unavailable.
Afficher les attributs enfants
data.points.date
string
obligatoire
The UTC day this point covers.
data.points.sends
integer
obligatoire
Volume for the day. An estimate for a competitor and an exact count for your own line; source on the series records which. A day nothing was observed is 0 rather than a missing point, so every line shares one axis.
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet. Les ressources sont en anglais.
Comprendre le conceptShould I use a Bird SDK or call the API directly?Suivre le parcours d'apprentissageBuild your first integrationGuide d'implémentationSend your first email
Obtenir un guide d'implémentation