---
title: "List a segment's contacts"
method: GET
path: "/segments/{id}/contacts"
tags: ["Segments"]
---

# List a segment's contacts

`GET /segments/{id}/contacts`

## Request

- Base URL: `https://api.privy.com/v1`
- URL: `https://api.privy.com/v1/segments/{id}/contacts`
- Auth: HTTP bearer

Retrieve the segment's members as a paginated list of contacts, in the same shape as the
Contacts API, most recently created first.

Membership is computed periodically, so the list may be up to an hour old (see
`counts.refreshed_at` on the segment). If the segment's membership has **never** been computed,
this endpoint returns `202 Accepted`, starts the computation in the background, and you should
retry shortly.

Every member is returned whatever their consent state, so `pagination.total_count` is the
segment's total membership, as its `total_count` was at the last refresh. It is neither the
segment's `mailable_count` nor its `textable_count` — those are consent-filtered counts taken
at the last refresh.

**Required scope:** `segments_read`

## Path parameters

- `id` string, required

## Query parameters

- `page` integer
- `per_page` integer

## Headers

- `Privy-Account-Id` string

## Response `200`

A paginated list of the segment's member contacts.

- object
  - `data` Contact[]
    - `id` string: Stable, unique identifier for the contact. Use this value in `/v1/contacts/{id}` endpoints.
    - `first_name` string, nullable
    - `last_name` string, nullable
    - `email` string, email, nullable
    - `email_consent` 'subscribed' | 'unsubscribed' | 'never_subscribed' | 'suppressed' | 'compliance_suppressed': The contact's email marketing consent. `subscribed` is the only value that makes a contact mailable; Privy sends no marketing email to any of the other four. - `subscribed` — the contact has opted in. Covers both explicit consent (they ticked an email opt-in box, confirmed a double opt-in, or were subscribed through this API) and implicit consent (they gave you their address while buying, or arrived in an approved "subscribed" import). The two are not distinguished here. - `unsubscribed` — the contact opted out, typically through the preferences link in a Privy email. A later signup that provides consent resubscribes them. - `never_subscribed` — no opt-in and no opt-out on record; shown as "unset" in the Privy dashboard. This is the usual state for someone who bought without opting in, and for an imported contact whose import is still awaiting review. - `suppressed` — you suppressed the contact, from the dashboard or by writing `suppressed` to this field. Reversible: writing any other consent value unsuppresses the contact first, then applies the change you asked for. - `compliance_suppressed` — Privy suppressed the address to protect your sending reputation: a hard bounce, a spam complaint, a rejected delivery, an address that failed Privy's compliance check during an import, or a flow's Suppress Contact action. Read-only through this API — a write to this field returns `422`, and a later signup does not resubscribe them. Other fields on the contact remain editable.
    - `email_consent_at` string, date-time, nullable: When `email_consent` last changed. Null when consent was never set.
    - `phone_number` string, nullable
    - `sms_consent` 'subscribed' | 'unsubscribed' | 'never_subscribed' | 'single_opt_in' | 'pending': The contact's SMS marketing consent. `subscribed` is the only value that makes a contact textable; Privy sends no marketing text to any of the other four. - `subscribed` — the contact's SMS opt-in is confirmed. Reached by replying `Y` to a double opt-in text, by signing up on a display set to Single Opt-In, by replying `START` or `UNSTOP`, by an opt-in recorded through this API, by a CSV import of SMS consent, or by a synced consent status from a connected store provider (e.g. Shopify). A confirmed opt-in does not record which method produced it: Single Opt-In and Double Opt-In displays both land on `subscribed`, so this field cannot tell you whether a contact double-opted in. - `unsubscribed` — the contact opted out, normally by replying `STOP`. Unlike email compliance suppression this is reversible: a new confirmed opt-in subscribes them again. - `never_subscribed` — no opt-in and no opt-out on record. Also the value after a reset: Privy clears SMS consent to this state when the phone number is removed from the contact, when a carrier reports the number as a landline, and when `never_subscribed` is written through this API. A contact who completed the SMS step of a multi-step display and has not yet replied `Y` also reads `never_subscribed` — that flow holds the opt-in off the contact record until it is confirmed — so do not read this value as "Privy never asked". - `single_opt_in` — a transitional pre-confirmation state inside the double opt-in flow, and the only pre-confirmation state a display signup can leave a contact in. The contact submitted a single-step display whose SMS opt-in method is Double Opt-In (Privy's default): the phone number is recorded, consent is not yet confirmed, and Privy is about to send the `Reply Y` confirmation text. In the healthy path the contact leaves this state within seconds — `pending` once the confirmation text is accepted by the carrier, then `subscribed` on the `Y` reply. A contact who stays here is one whose confirmation text was never sent (unsupported region, non-mobile number, SMS sending disabled on the account) or one written here directly through this API, which sends no confirmation text and so has no way forward. Not textable. Despite the name this is not the display's Single Opt-In setting — a display set to Single Opt-In confirms the contact outright, which reads as `subscribed`. - `pending` — Privy sent the double opt-in confirmation text and is waiting for the `Y` reply. Only reachable from `single_opt_in`, and only on the single-step display flow; the multi-step display flow tracks its awaiting-reply opt-ins outside this field. Not textable. Read-only.
    - `sms_consent_at` string, date-time, nullable: When `sms_consent` last changed. Null when consent was never set.
    - `source` string, nullable: How the contact RECORD was created: an import mechanism (`csv`, `shopify`, `mailchimp`, …), a Privy display, or the API. Free-form vocabulary — new integrations add values. This is not signup attribution: a store webhook or sync can create the record before the visitor ever signs up on a display, and a later display signup does not rewrite it. For which display(s) a contact signed up on, read `signups` on `GET /v1/contacts/{id}`.
    - `source_type` string, nullable: The kind of origin behind `source`. Common values: `import` (arrived in a contact import or store sync), `web` (signed up on a display), `hook` (created by a store webhook — e.g. `source: shopify, source_type: hook` is a contact first seen through a Shopify order or customer webhook). Same caveat as `source`: describes record creation, not later display signups.
    - `imported` boolean, nullable: Whether the contact arrived in a contact import.
    - `import_ids` string[]: The email contact imports that touched this contact, oldest first. SMS consent imports are tracked separately and are not referenced here.
    - `tags` string[]: The contact's Privy tags — free-form labels you define, with no fixed vocabulary. Privy lower-cases each name, trims surrounding whitespace, and drops duplicates on save, so tags always read back normalized. A name Privy has not seen for the account before is registered automatically and becomes available for contact filtering, segment conditions, and tag-triggered flows. Tags reach a contact from a CSV import, the contact's profile, a bulk action on the Contacts page, a display's signup tagging, a flow's Tag Contact action, and this API. Tags synced from Shopify are stored separately and are not included here.
    - `custom_fields` object: The contact's custom field values. Values are usually strings, but integration-written fields can carry other JSON scalars (e.g. numeric loyalty point balances).
    - `created_at` string, date-time
    - `updated_at` string, date-time
  - `pagination` PaginationEnvelope
    - `page` integer
    - `per_page` integer
    - `total_count` integer
    - `total_pages` integer

## Other responses

- `202`: The segment's membership has never been computed. Computation has been started — retry in a little while.
- `401`: Missing or invalid bearer token.
- `403`: `insufficient_scope` when the token lacks the required scope for this endpoint.
- `404`: The requested resource does not exist or does not belong to your account.
- `409`: The account's contacts have not been assigned public API ids, so contact reads are unavailable. This happens when contacts were saved before the account had API access. Enable API access for the account, or have Privy staff run the public_id backfill task for it, then retry.
- `429`: Rate limit exceeded. Retry after the specified time.

## Changes

- **2026-11-05** `cf0885e90d5a`: 10 warning
  - added the new `forbidden` enum value to the `error/code` response property for the response status `401`
  - added the new `forbidden` enum value to the `error/code` response property for the response status `403`
  - added the new `forbidden` enum value to the `error/code` response property for the response status `404`
  - added the new `forbidden` enum value to the `error/code` response property for the response status `409`
  - …6 more
- **2026-11-03** `fe500ab59b3b`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/privy/apis/privy-api/changes/segments/:id/contacts/get.md)

---

[API](https://skmtc.dev/privy/apis/privy-api.md) - [All operations](https://skmtc.dev/privy/apis/privy-api/llms.txt) - [OpenAPI document](https://skmtc.dev/privy/apis/privy-api/revisions/bc29b48dcdd2?raw)
