# Get sending health and deliverability limits

`GET /v1/email/health`

Returns your workspace's sending-health verdict for the requested window, together with reference deliverability limits and the boundaries used to classify risk. Use it to render a health badge, label the bounce-rate and complaint-rate limits, and draw the risk lines on a deliverability chart without hard-coding thresholds that we may retune.

The overall `status` is `healthy`, `watching`, or `throttled`, taken as the worst of the delivery-rate, bounce-rate, and complaint-rate signals. It describes deliverability risk and never pauses your sending on its own. For the counts and rates the verdict is derived from, call [Get aggregate email statistics](/docs/api/reference/get-email-stats-summary) over the same window.

Rates follow each event's occurrence time. A bounce or complaint that occurred during the window counts toward it even when the message was sent earlier. When you omit both dates the window ends today (UTC) and starts 7 days earlier. A window longer than 365 days returns `422`.

## Code samples

**TypeScript**

```ts
const health = await bird.email.health({ from: "2026-05-01", to: "2026-05-31" });
console.log(health.status);
for (const signal of health.signals) {
  console.log(signal.metric, signal.value, signal.status);
}
```

Examples: [TypeScript](/docs/api/reference/get-email-health.ts.md) · [Python](/docs/api/reference/get-email-health.py.md) · [Go](/docs/api/reference/get-email-health.go.md) · [PHP](/docs/api/reference/get-email-health.php.md) · [CLI](/docs/api/reference/get-email-health.cli.md) · [MCP](/docs/api/reference/get-email-health.mcp.md) · [cURL](/docs/api/reference/get-email-health.curl.md)

## Example response `200`

```json
{
  "period": {
    "data_as_of": null,
    "from": "2026-05-25",
    "to": "2026-06-01"
  },
  "status": "watching",
  "signals": [
    {
      "metric": "delivery_rate",
      "value": 0.995,
      "limit": null,
      "status": "healthy",
      "thresholds": {
        "direction": "below",
        "throttled": 0.984,
        "watching": 0.99
      }
    },
    {
      "metric": "open_rate",
      "value": 0.20100503,
      "limit": null,
      "status": "healthy"
    },
    {
      "metric": "bounce_rate",
      "value": 0.005,
      "limit": 0.005,
      "status": "watching",
      "thresholds": {
        "direction": "above",
        "throttled": 0.006,
        "watching": 0.004
      }
    },
    {
      "metric": "complaint_rate",
      "value": 0.00010050251,
      "limit": 0.003,
      "status": "healthy",
      "thresholds": {
        "direction": "above",
        "throttled": 0.001,
        "watching": 0.0006
      }
    }
  ]
}
```

## Query parameters

- `from` (string): Start date (inclusive) in `YYYY-MM-DD`, UTC. Defaults to 7 days before `to` when omitted.
- `to` (string): End date (inclusive) in `YYYY-MM-DD`, UTC. Defaults to today (UTC) when omitted. Window may not exceed 365 days. Day boundaries are always UTC; unlike the statistics reads, this one takes no `timezone`.

## Response body

- `period` (object, required): The date range the verdict was computed over, echoed back from the request.
- `period.from` (string, required): Inclusive start date the response covers (YYYY-MM-DD).
- `period.to` (string, required): Inclusive end date the response covers (YYYY-MM-DD).
- `period.data_as_of` (nullable string): The instant the statistics in this response are current to: events recorded up to roughly this time are reflected, while more recent events may not be yet. Statistics are served from a rolling aggregation that refreshes every few seconds, so a response reflects data from up to a few seconds ago. Use this field to label data freshness (for example "as of 14:03") rather than assuming the numbers are to-the-second. Null when the freshness boundary is not being reported.
- `status` (string, required)

  Overall sending-health verdict for the window, taken as the worst status among the bounce-rate, complaint-rate, and delivery-rate signals. The open-rate signal, which can be `strong`, is not part of this roll-up. The overall verdict is one of `healthy`, `watching`, or `throttled`. It is `healthy` when the other three signals are each healthy or better. It is `watching` when at least one is watching, and `throttled` when at least one is throttled. This verdict describes deliverability risk. It never pauses your sending on its own.

  Possible values: `healthy`, `watching`, `throttled`
- `signals` (array of object, required): The per-rate signals include `delivery_rate`, `open_rate`, `bounce_rate`, and `complaint_rate`. Read a signal by matching on its `metric`. Each entry carries its current value, a reference deliverability limit (null where no limit applies), and its own verdict. Delivery rate, bounce rate, and complaint rate also carry the thresholds their verdict was classified against; open rate does not, because a high open rate is never a risk.
- `signals.metric` (string, required)

  Which rate this signal reports.

  Possible values: `delivery_rate`, `open_rate`, `bounce_rate`, `complaint_rate`
- `signals.value` (nullable number, required): The current rate over the window, as a fraction. Null when its denominator is zero.
- `signals.limit` (nullable number, required): The reference deliverability limit for this rate, as a fraction (for example `0.005` for a 0.5% bounce-rate limit). Null for metrics that have no limit, such as delivery rate and open rate. The verdict is classified using `thresholds`, which can differ from this reference limit.
- `signals.status` (string, required)

  This metric's individual verdict, ordered best to worst: `strong`, `healthy`, `watching`, `throttled`. `strong` applies only to `open_rate`, for an open rate well above typical. For the other rates, `healthy`, `watching`, and `throttled` indicate how close the rate is to a level that risks deliverability. The verdict follows the `thresholds` boundaries rather than the displayed reference `limit`. A signal whose `value` is null, because its denominator was zero in the window, is reported as `healthy`.

  Possible values: `strong`, `healthy`, `watching`, `throttled`
- `signals.thresholds` (object)
- `signals.thresholds.direction` (string, required)

  Which side of the boundaries is at risk. `above` for higher-is-worse rates (bounce, complaint), `below` for lower-is-worse rates (delivery).

  Possible values: `above`, `below`
- `signals.thresholds.watching` (number, required): Crossing this boundary in the risk direction moves the signal to `watching`, as a fraction.
- `signals.thresholds.throttled` (number, required): Crossing this boundary in the risk direction moves the signal to `throttled`, as a fraction.

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