---
title: "List an SMS campaign's recipients"
description: "How each recipient of an SMS campaign fared: the carrier's answer for a text that went out (delivered, sent, failed, …), or why none was sent (skipped, with reason_code: opted_out, no_marketing_consent, …). `GET /api/v1/sms/campaigns/{id}/recipients`"
url: https://www.unitpost.com/docs/sms/get-sms-campaigns-id-recipients
section: "SMS API reference"
updated: 2026-10-13
---
# List an SMS campaign's recipients

`GET /api/v1/sms/campaigns/{id}/recipients`

How each recipient of an SMS campaign fared: the carrier's answer for a text that went out (`delivered`, `sent`, `failed`, …), or why none was sent (`skipped`, with `reason_code`: `opted_out`, `no_marketing_consent`, …). Newest first, cursor-paginated on `id`; `q` matches digits of the number. `skipped_by_reason` counts skipped recipients over the whole campaign, on the first page (null on the others). A bad `limit` or `q`, or any other query key, is a `422`. Requires the `campaigns:read` capability.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |
| `limit` | query | integer | no | Max rows to return (default 20, max 100). |
| `after` | query | string | no | Return rows after this id (forward paging). |
| `before` | query | string | no | Return rows before this id (backward paging). |
| `q` | query | string | no | Digits of the recipient's number. |

## Response fields (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | yes | — |
| `id` | string | yes | This recipient row (`rcpt_…`); pass the last one as `after` for the next page. |
| `contact_id` | string \| null | yes | — |
| `to` | string \| null | yes | The number in E.164. |
| `name` | string \| null | yes | — |
| `status` | enum | yes | The carrier's answer when a text exists, else why none was sent (`skipped`). One of: `delivered`, `sent`, `failed`, `suppressed`, `canceled`, `deferred`, `pending`, `skipped`. |
| `reason` | string \| null | yes | Why it failed or was skipped, in plain words. |
| `reason_code` | string \| null | yes | The SMS skip reason (`opted_out`, `no_marketing_consent`, …) when there is one. |
| `message_id` | string \| null | yes | The text (`sms_…`); read it with `GET /sms/{id}`. |
| `segments` | integer \| null | yes | Segments billed. |
| `created_at` | string | yes | — |

## Responses

| Status | Description |
| --- | --- |
| 200 | A page of recipients. |
| 401 | Missing or invalid API key. |
| 403 | Insufficient scope, or SMS is not on this plan: create, send, resume and reschedule return `limit_reached` with `feature_not_in_plan` on `smsChannel` in `error.details[]`. |
| 404 | SMS campaign not found, or SMS is not enabled. |
| 422 | A bad `limit`, cursor or `q`, a repeated key, or an unknown one. |
| 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/sms/campaigns/cmp_123/recipients' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'User-Agent: my-app/1.0'
```

## Example response (200)

```json
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "sms_campaign_recipient",
      "id": "id_123",
      "contact_id": "contact_123",
      "to": "customer@example.com",
      "name": "Example",
      "status": "delivered",
      "reason": "string",
      "reason_code": "string",
      "message_id": "message_123",
      "segments": 0,
      "created_at": "2026-01-01T00:00:00.000Z"
    }
  ],
  "skipped_by_reason": [
    {
      "reason": "string",
      "count": 1
    }
  ]
}
```

## Links

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