# Segments > Count who an unsaved filter matches, save it as a reusable segment, and list every segment with its live count, the same dynamic filters the Signups bar uses. Source: https://getlaunchlist.com/help/docs/api/segments --- Segments are the dynamic filters behind the segment bar on Signups, saved as a `{ match, conditions }` rules tree, evaluated live against the current audience (never a stale cached number). The API speaks the identical grammar the dashboard's segment builder writes. ## Scopes | Operation | Scope | | ------------------------------ | ---------------- | | List, retrieve, preview | `segments:read` | | Create | `segments:write` | ## List segments ``` GET /v1/segments ``` Every segment in a project, both the built-in ones (Everyone, Verified, Waiting for verification, Unsubscribed, Can't email, Subscribed and the rest) and your own, each with its live matching `count`. There is no pagination: a project holds at most a few dozen, so this answers the whole set in one call. Takes `project` (required). ## Retrieve a segment ``` GET /v1/segments/{segment} ``` `{segment}` is a `seg_…` handle. Returns the [segment object](#the-segment-object) with `count` computed fresh, not the stale figure a dashboard cache might show for a moment. ## Count who an unsaved filter matches ``` POST /v1/segments/preview ``` The live count for a `rules` tree that is not saved anywhere. Use it before `POST /segments` to sanity-check a filter, or before a [tag apply call](/help/docs/api/tags) with `limit` to see how big a wave you are about to touch. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ---------------------------------------------------- | | `project` | string | Yes | A `prj_` handle. | | `rules` | object | No | The condition tree, below. Omitted or empty counts everyone. | Returns `{ "object": "segment_preview", "count": }`. ## Create a segment ``` POST /v1/segments ``` Saves a `rules` tree as a named, reusable segment. Requires `name` (up to 120 characters) and `rules`; `description`, `icon`, `color` and `pinned` are optional. Returns **201** with the [segment object](#the-segment-object). Saving a segment of your own is available on Launch and Grow. On a plan without it this is a **422** naming the entitlement in `message`, not a silent no-op, check the response rather than assuming the segment was created. ## The rules tree ```json { "match": "and", "conditions": [ { "field": "is_referrer", "operator": "is_true" }, { "field": "status", "operator": "eq", "value": "subscribed" } ] } ``` `match` is `and` or `or`. Each entry in `conditions` is either a nested group (the same `{ match, conditions }` shape, up to 5 levels deep) or a leaf: `{ field, operator, value }`. Both `POST /segments` and `POST /segments/preview` validate the tree and refuse with a `422` on an unknown field, an operator illegal for its field, or a malformed value, a typo never silently widens the match into something you didn't ask for. | Field(s) | Operators | `value` | | ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | --------------------------------------------- | | `status`, `unsubscribed_by`, `consent`, `trust_tier`, `deliverability`, `stage`, `invite_state`, `identifier_type`, `first_source`, `country`, `device`, `browser`, `os`, `intent_cluster` | `eq`, `neq`, `in`, `not_in` | string, or a string array for `in`/`not_in` | | `referral_count`, `quality_score` | `gt`, `gte`, `lt`, `lte`, `between` | number, or `[min, max]` for `between` | | `created_at`, `verified_at`, `consented_at` | the same comparisons, plus `within_last_days`, `exists`, `not_exists` | an ISO 8601 date; an integer for `within_last_days`; omit for `exists` and `not_exists` | | `submission_count` | `eq`, `gt`, `gte`, `lt`, `lte` | number | | `tag` | `in`, `not_in`, `exists` | an array of `tag_…` handles, from `GET /v1/tags`; omit for `exists` | | `received_broadcast` | `in`, `not_in` | an array of 1-50 `bcd_…` broadcast handles, all from this project | | `is_referrer`, `is_referred`, `awaiting_verification` | `is_true`, `is_false` | omit | | `utm_source`, `utm_medium`, `utm_campaign` | `eq`, `neq`, `in`, `contains`, `exists` | string, string array, or omit for `exists` | | `attribute` | `eq`, `neq`, `contains`, `exists`, `not_exists`, `in` | needs `key` alongside `field`/`operator`/`value` | The `status` values are `pending` (shown as Not subscribed), `subscribed`, `unsubscribed`, `bounced` and `blocked`. People who joined but have not verified their email are `subscribed` with `verified_at` not set. `awaiting_verification` with `is_true` is the people sent a link to verify their email who have not clicked it yet (an expired link included): with `status` `subscribed`, it is the built-in Waiting for verification segment, and the people a broadcast's `recipients: unverified` reaches. `verified_at` with `not_exists` is wider: it also matches people who were never asked to verify. For a few minutes after the update that added `awaiting_verification`, `POST /segments` refuses it with a `422`; `POST /segments/preview` takes it at once. A rule that says `verified`, the old name of `subscribed`, is still accepted for now and is read as `subscribed`. `received_broadcast` also takes an optional `days` (integer, 1 to 3650): "sent at least this many days ago." Combine it with `in` or `not_in` to build waves, for example "received the launch email at least 3 days ago": ```json { "match": "and", "conditions": [ { "field": "received_broadcast", "operator": "in", "value": ["bcd_4kq3mxrz9dnp"], "days": 3 } ] } ``` > [!NOTE] Every id-shaped value in a rules tree is a handle > `tag` and `received_broadcast` are the only two fields whose `value` addresses another record, and > both take that record's public handle (`tag_…`, `bcd_…`), exactly like every other id in this API. > A segment you read back carries handles too; the raw ids the compiler runs against never leave > this endpoint. > [!NOTE] `deliverability` only ever counts verdicts you have paid to reveal > A verdict this workspace has not bought with a credit is withheld from a segment count exactly as > it is withheld everywhere else in the product: `null` until it is revealed with a credit. ## The segment object ```json { "object": "segment", "id": "seg_7qk2mn4pr8vx", "project": "prj_7Qk2mN4pR8", "name": "Engaged referrers", "description": null, "type": "dynamic", "is_system": false, "pinned": false, "icon": null, "color": null, "rules": { "match": "and", "conditions": [] }, "count": 214, "created_at": "2026-09-24T10:02:11+00:00", "url": "/v1/segments/seg_7qk2mn4pr8vx" } ``` `count` is `null` only when it genuinely could not be computed, never a false zero standing in for "no matches." ## Next - [Tags](/help/docs/api/tags), a `segment` handle is what a tag apply call points its `segment` argument at - [Contacts](/help/docs/api/contacts-list), the object a segment's rules filter ## Common questions ### What scope does segments need? segments:read for listing, retrieving and previewing (counting who matches a filter is a read, nothing is saved). segments:write to save a new one. ### Can I filter on a custom attribute my form captured? Yes, with the `attribute` field: `{ "field": "attribute", "key": "company", "operator": "contains", "value": "Acme" }`. The key is your form field's key, whitelisted to letters, digits, underscore and dot. ### Do tag and received_broadcast conditions take a handle, like everything else in the API? Yes. `tag`'s value is an array of `tag_…` handles from GET /v1/tags, and `received_broadcast`'s value is an array of `bcd_…` broadcast handles. A segment you read back carries handles too, never the internal ids the compiler actually runs against. ### Does previewing or saving a segment ever reveal deliverability verdicts I have not paid for? No. A `deliverability` condition only ever counts contacts whose verdict the workspace has already revealed with a credit, the same gate every other surface in the product reads through. ### What counts as "received" a broadcast? A send that was actually handed to the provider and not suppressed, for example by an unsubscribe that landed before the send ran. A broadcast still mid-send counts too, since a second broadcast going out the same afternoon must not also mail that person. People a canceled broadcast never reached do not count.