# Get the competitor watchlist with its latest figures

`GET /v1/email/competitive/watchlist`

Returns every competitor brand on the workspace's watchlist, plus a row for
your own sending, each with estimated send volume and how it changed against
the previous period, how often the brand sends, inbox placement, estimated
read rate, audience overlap with you, and the most recent campaign seen.

Figures about a competitor are estimates from an email panel, which observes a
sample of real inboxes and scales what it sees up to a whole audience. They
are fetched while the request runs, so they are current rather than cached,
and two requests minutes apart can differ slightly. Figures about your own
sending are counted rather than estimated wherever that is possible. The
`provenance` object on each row records which source each figure came from.

A figure reads `null` when it is unavailable for that brand, so a `0` always
means a real measurement. When a whole row has no figures, `panel_status` says
why: the panel may not track the brand's sending domain, may track it but have
seen no mail in the period, or may have been briefly unreachable.

`esp` and `list_size` are the exception, and are always `null` here. Read a
single brand to get them.

API-key calls require Insights preview access for your organization.

## Code samples

### TypeScript

```ts
// Requires Insights preview access for the organization.
const report = await bird.email.competitive.watchlist.get({ range: 30 });
console.log(report.data);
```

### Python

```py
# Requires Insights preview access for the organization.
report = client.email.competitive.watchlist.get(range=30)
print(report.model_dump_json())
```

### Go

```go
// 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.Get(ctx, bird.EmailCompetitiveWatchlistGetParams{Range: 30})
if err != nil {
	log.Fatal(err)
}
encoded, err := json.MarshalIndent(report, "", "  ")
if err != nil {
	log.Fatal(err)
}
fmt.Println(string(encoded))
```

### PHP

```php
// Requires Insights preview access for the organization.
$report = $bird->email->competitive->watchlist->get(['range' => 30]);
var_dump($report->getData());
```

### CLI

```sh
bird email competitive watchlist get
```

### cURL

```sh
curl -X GET "https://us1.platform.bird.com/v1/email/competitive/watchlist" \
  -H "Authorization: Bearer $TOKEN" \
  --url-query "range=30"
```

## Example response `200`

```json
{
  "period": {
    "days": 30,
    "from": "2026-07-13T09:00:00Z",
    "to": "2026-08-12T09:00:00Z"
  },
  "summary": {
    "share_of_volume_percent": 10.5,
    "share_of_volume_change_points": -1.2,
    "competitor_sends": 4240000,
    "competitor_sends_change_percent": 12,
    "peer_cadence_median_per_week": 4.4,
    "peer_inbox_placement_median_rate": 0.892
  },
  "data": [
    {
      "watchlist_brand_id": "cwb_01krdgeqcxet5s7t44vh8rt9mg",
      "is_workspace": false,
      "name": "Everlane",
      "industry": "DTC Apparel",
      "sending_domains": [
        "everlane.com"
      ],
      "esp": "Klaviyo",
      "list_size": 1240000,
      "panel_status": "ok",
      "sends": 1240000,
      "sends_change_percent": 18,
      "cadence_per_week": 5.2,
      "inbox_placement_rate": 0.889,
      "read_rate": 0.192,
      "audience_overlap_rate": 0.24,
      "last_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"
      },
      "provenance": {
        "sends": "panel",
        "cadence_per_week": "panel",
        "inbox_placement_rate": "panel",
        "read_rate": "panel",
        "audience_overlap_rate": "panel",
        "last_campaign": "panel"
      }
    }
  ]
}
```

## 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 body

- `period` (object, required): The period every figure covers.
- `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.
- `summary` (object, required): Where your sending sits against the brands you watch.
- `summary.share_of_volume_percent` (nullable number, required): Your share of everything the watched set sent over the period, your own sending included in the total. Your half of the ratio is an exact count of your own sending while the rest is the panel's estimate, so the two sides are measured differently.
- `summary.share_of_volume_change_points` (nullable number, required): How that share moved against the period immediately before, in percentage points. A share that went from 11.7 to 10.5 reports -1.2.
- `summary.competitor_sends` (nullable integer, required): Estimated volume the watched brands sent between them, excluding your own sending. A panel estimate, so read it as an order of magnitude rather than a count.
- `summary.competitor_sends_change_percent` (nullable number, required): Change in that volume against the period immediately before.
- `summary.peer_cadence_median_per_week` (nullable number, required): Median campaigns per week across the brands you watch, per sending domain. Your own row is excluded, since it is the figure being held against this one.
- `summary.peer_inbox_placement_median_rate` (nullable number, required): Median inbox placement across the brands you watch. Your own row is excluded, as with the cadence median.
- `data` (array of object, required): Your own row first, then each watched brand in the order it was added. Your row is present once your workspace has sent email, since before that there is no sending of yours to compare against. Empty for a workspace that has neither sent nor added a brand.
- `data.watchlist_brand_id` (string): The watchlist entry, for removing the brand. Absent on your own row, which is not a watchlist entry.
- `data.is_workspace` (boolean, required): True on the row describing your own workspace's sending.
- `data.name` (string, required): The brand's name as it was when the brand was added to the watchlist.
- `data.industry` (nullable string, required): The brand's industry as it was when the brand was added, or null when the brand is not classified.
- `data.sending_domains` (array of string, required): The domains the brand's figures describe. Always one domain today: a brand is tracked by the single one the panel sees the most of its mail from, so a brand that splits its mail across several domains reports less than its full volume.
- `data.esp` (nullable string, required): A sending platform observed on the domain, or null when the panel has none on record. A brand sending through more than one platform reports one of them rather than the list. This is frequently unavailable and updates monthly at best, so treat its absence as normal rather than as pending. Populated only when you read a single brand; on the watchlist it is always null.
- `data.list_size` (nullable integer, required): Estimated number of addresses the brand mails, or null when the panel has no estimate. Populated only when you read a single brand; on the watchlist it is always null.
- `data.panel_status` (string, required)

  Whether panel figures were available for this row, and when they were not, why.

  Possible values: `ok`, `not_in_panel`, `no_data`, `unavailable`
- `data.sends` (nullable integer, required): Messages sent in the period.
- `data.sends_change_percent` (nullable number, required): Change in send volume against the period immediately before this one, as a percentage. Null when the earlier period has nothing to compare against.
- `data.cadence_per_week` (nullable number, required): Average campaigns sent per week over the period.
- `data.inbox_placement_rate` (nullable number, required): Share of the brand's observed mail that reached an inbox rather than a spam folder.
- `data.read_rate` (nullable number, required): Share of delivered mail that was read.
- `data.audience_overlap_rate` (nullable number, required): Share of your own audience the panel also sees receiving this brand's mail. Null on your own row, and null for a competitor the panel measured no overlap with, which is an answer rather than a gap.
- `data.last_campaign` (nullable object, required): The most recent campaign observed in the period, or null when none was. Always null on your own row.
- `data.last_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.last_campaign.subject` (string, required): The subject line the panel saw on this campaign.
- `data.last_campaign.sent_at` (string, required): When the panel first saw this campaign arrive.
- `data.last_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.provenance` (object, required): Where each figure on this row came from.
- `data.provenance.sends` (string, required)

  Source of `sends` and of `sends_change_percent`, which is derived from it.

  Possible values: `measured`, `panel`, `none`
- `data.provenance.cadence_per_week` (string, required)

  Source of `cadence_per_week`.

  Possible values: `measured`, `panel`, `none`
- `data.provenance.inbox_placement_rate` (string, required)

  Source of `inbox_placement_rate`.

  Possible values: `measured`, `panel`, `none`
- `data.provenance.read_rate` (string, required)

  Source of `read_rate`.

  Possible values: `measured`, `panel`, `none`
- `data.provenance.audience_overlap_rate` (string, required)

  Source of `audience_overlap_rate`.

  Possible values: `measured`, `panel`, `none`
- `data.provenance.last_campaign` (string, required)

  Source of `last_campaign`.

  Possible values: `measured`, `panel`, `none`

## Related resources

- [Should I use a Bird SDK or call the API directly?](/explained/platform/should-i-use-an-sdk-or-call-the-api-directly) (answer)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=api-basics)
