# Metrics

> Sent, delivered, bounces, complaints, opens, clicks and their rates, by day, domain, tag, template, broadcast and mailbox provider. Every definition, the metrics API and the bounce and complaint alerts.

Source: https://docs.pidgely.com/guides/metrics/

Pidgely counts every send and every event as it happens: deliveries, bounces, complaints, delays, opens, clicks and unsubscribes. You read the numbers in the dashboard's **Metrics** page or with [`GET /metrics`](https://docs.pidgely.com/api-reference/metrics/retrieve/), for any range of up to 400 days, by day or broken down by domain, tag, template, broadcast or mailbox provider.

## Counters

| Field | Counts |
|---|---|
| `sent` | Recipients accepted for delivery (suppressed addresses excluded) |
| `delivered` | Deliveries accepted by the receiving server |
| `hard_bounced` | Permanent bounces; the address is [suppressed](https://docs.pidgely.com/guides/suppressions/) |
| `soft_bounced` | Transient and undetermined bounces (mailbox full, message too large, content rejected), and deliveries given up after 72 hours of retries |
| `complained` | Spam complaints (`not-spam` reports are not counted) |
| `delayed` | Deliveries delayed at least once (counted once per recipient) |
| `suppressed` | Recipients skipped because they are on the suppression list |
| `failed` | Deliveries that failed for another reason (account suspended, content expired) |
| `unsubscribed` | One-click and link unsubscribes |
| `opens` | Every recorded open, [machine opens](https://docs.pidgely.com/guides/tracking/#machine-opens-and-bot-clicks) included |
| `machine_opens` | Opens flagged as machine opens (Apple Mail Privacy Protection, image proxy prefetch, scanners) |
| `unique_opens` | Recipients who opened at least once |
| `unique_human_opens` | Recipients with at least one human (not machine) open |
| `clicks` | Every recorded click, bot clicks included |
| `bot_clicks` | Clicks flagged as bot clicks (security scanners, `HEAD` requests, many links at once) |
| `unique_clicks` | Recipients who clicked at least once |
| `unique_human_clicks` | Recipients with at least one human (not bot) click |

Opens and clicks need [open and click tracking](https://docs.pidgely.com/guides/tracking/) on the sending domain or the send.

## Rates

Rates are numbers between 0 and 1. A rate is `null` when its denominator is 0.

| Field | Formula |
|---|---|
| `delivery_rate` | `delivered / sent` |
| `bounce_rate` | `hard_bounced / (delivered + hard_bounced)`, the rate mailbox providers and the [reputation guard](https://docs.pidgely.com/rate-limits/#reputation-guard) judge |
| `soft_bounce_rate` | `soft_bounced / sent` |
| `complaint_rate` | `complained / delivered` |
| `open_rate` | `unique_human_opens / delivered` |
| `raw_open_rate` | `unique_opens / delivered` (machine opens included, comparable with tools that do not filter them) |
| `click_rate` | `unique_human_clicks / delivered` |
| `click_to_open_rate` | `unique_human_clicks / unique_human_opens` |
| `unsubscribe_rate` | `unsubscribed / delivered` |

## Breakdowns

| `group_by` | One point per | Notes |
|---|---|---|
| `day` (default) | UTC day of the range, zeros included | |
| `domain` | Sending domain | `label` is the domain name |
| `tag` | Tag, as `name:value` | Up to 10 tags of an email are counted |
| `template` | [Template](https://docs.pidgely.com/guides/templates/) the email was sent from | `label` is the template name |
| `broadcast` (or `campaign`) | [Broadcast](https://docs.pidgely.com/guides/broadcasts/) | `label` is the broadcast name |
| `provider` | Recipient mailbox provider: `gmail`, `outlook`, `yahoo`, `icloud`, `other` | From the address domain (`gmail.com`, `hotmail.com.br`, `yahoo.co.uk`, `icloud.com`…). Custom domains hosted at Google Workspace, Microsoft 365, Yahoo or iCloud are recognised from the MX host reached by the first delivery attempt. |

Values are listed most sent first, at most 100. A **filter** narrows the result to one value and then always groups by day: pass one of `domain_id`, `tag` (`name:value`), `template_id`, `broadcast_id` or `provider`.

## The API

```bash tab="cURL"
curl -X GET 'https://api.pidgely.com/metrics?days=30&group_by=provider' \
  -H "Authorization: Bearer $PIDGELY_API_KEY"
```

| Parameter | Description |
|---|---|
| `days` | The last N UTC days, today included (default 30, at most 400) |
| `from`, `to` | A range of UTC days (`YYYY-MM-DD`) instead of `days`; at most 400 days |
| `group_by` | `day`, `domain`, `tag`, `template`, `broadcast`, `campaign` or `provider` |
| `domain_id`, `tag`, `template_id`, `broadcast_id`, `provider` | One filter |

```json
{
  "object": "metrics",
  "from": "2026-09-21",
  "to": "2026-10-20",
  "group_by": "provider",
  "filter": null,
  "totals": { "key": "total", "sent": 1200, "delivered": 1188, "hard_bounced": 7, "complained": 0, "unique_human_opens": 455, "unique_human_clicks": 131, "open_rate": 0.383, "click_rate": 0.1103, "bounce_rate": 0.0059, "…": "…" },
  "data": [
    { "key": "gmail", "sent": 640, "delivered": 636, "open_rate": 0.41, "…": "…" },
    { "key": "outlook", "sent": 310, "delivered": 305, "open_rate": 0.33, "…": "…" }
  ],
  "alerts": [],
  "thresholds": { "bounce_rate_warning": 0.04, "complaint_rate_warning": 0.0008, "bounce_rate_limit": 0.04, "complaint_rate_limit": 0.001 }
}
```

Every point has all the counters and all the rates.

### Clicks per link

[`GET /metrics/links`](https://docs.pidgely.com/api-reference/metrics/list-links/) lists the links of a broadcast (`broadcast_id`) or template (`template_id`), most clicked by people first, with `clicks`, `bot_clicks`, `unique_clicks` and `unique_human_clicks`, plus the all-time `totals` of that broadcast or template:

```bash tab="cURL"
curl -X GET 'https://api.pidgely.com/metrics/links?broadcast_id=0199d7f6-4c8d-7e1a-9b3f-2d6a5c0e7f19' \
  -H "Authorization: Bearer $PIDGELY_API_KEY"
```

### One email

[List the events of an email](https://docs.pidgely.com/api-reference/emails/list-events/) returns its whole timeline and, per recipient, `opens`, `human_opens`, `clicks`, `human_clicks`, the first and last open and click, and the `mailbox_provider`.

## Alerts

Mailbox providers start filtering senders well before anything visibly breaks. `alerts` warns when the range's rates cross their thresholds:

| Metric | `warning` above | `critical` above |
|---|---|---|
| `bounce_rate` | 4% | the reputation guard's pause limit (4% by default) |
| `complaint_rate` | 0.08% (Gmail and Yahoo's bulk sender limit is 0.3%; they recommend staying under 0.1%) | the reputation guard's pause limit (0.1% by default) |

```json
{ "metric": "complaint_rate", "level": "warning", "rate": 0.0011, "threshold": 0.0008 }
```

Alerts need some volume to mean anything: bounce alerts start at 50 delivered or bounced messages, complaint alerts at 100 delivered. For comparison, Amazon SES reviews accounts at 5% bounces and 0.1% complaints. The [reputation guard](https://docs.pidgely.com/rate-limits/#reputation-guard) pauses sending on its own 7-day window; the dashboard shows the same warnings on the Overview and Metrics pages.

## How the numbers are kept

- Counters are updated by the same transaction that records each event, in daily rollups per dimension. Reading metrics never scans your emails, so it costs the same for ten emails or ten million.
- Days are **UTC** and an event counts on the day it happened: an email sent on Monday and opened on Tuesday adds to Monday's `sent` and Tuesday's `opens`. Rates over a range are therefore more meaningful than rates of a single day.
- Rollups are kept for 400 days, with the message metadata. They contain no addresses and no personal data.

## In the dashboard

**Metrics** (under Sending) shows the counters and rates of the last 7, 30 or 90 days, the daily sends and engagement charts, breakdowns by domain, mailbox provider, tag, template and broadcast, and the alerts. The **Overview** shows the key rates of the period, and each email's page shows its full timeline: every open and click, bounce and complaint detail.
