---
title: "Campaign results"
description: "A campaign's results: delivered, unique opens and clicks, unsubscribes, bounces, complaints and their rates, recipients, and who didn't receive it. `GET /api/v1/email/campaigns/{id}/stats`"
url: https://www.unitpost.com/docs/email/get-email-campaigns-id-stats
section: "Email API reference"
updated: 2026-10-13
---
# Campaign results

`GET /api/v1/email/campaigns/{id}/stats`

A campaign's results: delivered, unique opens and clicks, unsubscribes, bounces, complaints and their rates, recipients, and who didn't receive it. Open and click rates are `null` when that tracking was off; every rate is `null` until something was sent. Old messages are cleaned up after your plan's retention window; their counts are kept (`source`). Requires the `campaigns:read` capability.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |

## Response fields (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | yes | — |
| `campaign_id` | string | yes | — |
| `recipients` | integer | yes | Contacts the campaign was sent to (suppressed addresses never became recipients). |
| `failed_recipients` | integer | yes | Recipients that didn't receive it, including ones that failed before a message existed. |
| `suppressed` | integer \| null | yes | Contacts left out at send because their address is suppressed; null for campaigns sent before this was counted. |
| `counts` | object | yes | — |
| `rates` | object | yes | Percentages with one decimal (12.3 = 12.3%). null when there is nothing to measure yet, or when that tracking was off. |
| `tracking` | object | yes | — |
| `source` | enum | yes | Live rows, the counts frozen when old messages were cleaned up, or both. One of: `live`, `snapshot`, `combined`. |
| `snapshotted_at` | string \| null | yes | — |

## Responses

| Status | Description |
| --- | --- |
| 200 | The campaign's results. |
| 401 | Missing or invalid API key. |
| 403 | Insufficient scope. |
| 404 | Campaign not found (or it's an SMS campaign). |
| 422 | A query key (this operation takes none). |
| 503 | `operation_timed_out`: the read took too long; retry after `Retry-After`. |

## Example request

```bash
curl -X GET 'https://www.unitpost.com/api/v1/email/campaigns/cmp_123/stats' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'User-Agent: my-app/1.0'
```

## Example response (200)

```json
{
  "object": "campaign_stats",
  "campaign_id": "campaign_123",
  "recipients": 0,
  "failed_recipients": 0,
  "suppressed": 0,
  "counts": {
    "sent": 0,
    "delivered": 0,
    "opened": 0,
    "clicked": 0,
    "unsubscribed": 0,
    "bounced": 0,
    "complained": 0,
    "failed": 0
  },
  "rates": {
    "delivery": 0,
    "open": 0,
    "click": 0,
    "unsubscribe": 0,
    "bounce": 0,
    "complaint": 0
  },
  "tracking": {
    "open": false,
    "click": false
  },
  "source": "live",
  "snapshotted_at": "2026-01-01T00:00:00.000Z"
}
```

## Links

- HTML reference (with Node.js, Python, Ruby, PHP, Laravel, Go, Java, Rust, .NET and CLI examples): https://www.unitpost.com/docs/email/get-email-campaigns-id-stats
- OpenAPI 3.1 (full schemas): https://www.unitpost.com/api/v1/openapi.json
