# Tags > Create tags, list them with live counts, and apply or remove one from up to a thousand contacts at once, by handle or by a saved segment. Source: https://getlaunchlist.com/help/docs/api/tags --- Tags are the same ad-hoc labels the dashboard's Signups table applies, first-class, renameable, colored, shared with segments (a `tag` condition filters on them) and with the "Add tag" / "Has a tag" automation steps. Nothing about tagging is API-specific. ## Scopes | Operation | Scope | | ----------------------------------- | ---------------- | | List tags | `contacts:read` | | Create, apply, remove | `contacts:write` | ## List tags ``` GET /v1/tags ``` Newest first, cursor-paginated ([pagination](/help/docs/api/pagination)). Each tag carries `contacts_count`, an index-backed count of how many contacts currently hold it, cheap even on a large audience because it never scans the `contacts` table itself. | Parameter | Type | Description | | ----------------- | ------- | -------------------------------- | | `project` | string | **Required.** A `prj_` handle. | | `limit` | integer | 1 to 100, default 25 | | `starting_after` | string | Page forward from this tag id | | `ending_before` | string | Page backward | ## Create a tag ``` POST /v1/tags ``` Idempotent by name within the project: creating a tag that already exists by that name returns the existing one (**200**) instead of a duplicate, so you can call this before every apply without a prior lookup. A brand new tag answers **201**. | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------- | | `project` | string | Yes | A `prj_` handle. | | `name` | string | Yes | Up to 60 characters. | | `color` | string | No | A hex color, up to 32 characters. | ## Apply or remove a tag ``` POST /v1/tags/{tag}/contacts DELETE /v1/tags/{tag}/contacts ``` `{tag}` is the `tag_…` handle from a list or create call. Both verbs take the identical body: an explicit list of contact handles, **or** a saved segment. Send exactly one of `contacts` / `segment`, never both, and never neither: either failure is a `422`. | Parameter | Type | Description | | ---------- | --------------- | ----------------------------------------------------------------------------------------------------------------- | | `contacts` | array of string | `con_…` handles or uuids, 1 to 1000. Mutually exclusive with `segment`. | | `segment` | string | A `seg_…` handle. Every contact it currently matches is affected, unless `limit` narrows it. | | `limit` | integer | With `segment` only, 1 to 1000. Affects the **oldest matching contacts first**, by signup date. | > [!TIP] `segment` + `limit` is how you tag a wave, not the whole list > Build a segment for "the people I want, minus whoever already has this tag" ([segments > docs](/help/docs/api/segments) has the `tag` field's `not_in` operator), preview it to sanity-check > the count, then call this with a `limit`. Run it again later and it naturally picks up wherever the > last run left off, because it always excludes contacts already carrying the tag. Both responses are a `tag_apply_result`: the tag (with its refreshed `contacts_count`), `matched` (how many contacts the request resolved to before applying anything), and `attached` (on the add call) or `detached` (on the remove call), how many rows actually changed. A contact that already carried the tag (or never did, on remove) is left alone and does not count toward that number. Nothing else happens: no webhook, no automation, no email. Contacts held past the workspace's plan limit are silently excluded from both paths, the same as every other bulk action in the product. ## Next - [Segments](/help/docs/api/segments), build the filter a `segment` argument points at - [Pagination](/help/docs/api/pagination) ## Common questions ### What scope does tagging need? contacts:read to list tags, contacts:write to create one or apply/remove it. A tag is audience metadata, not a resource with its own blast radius, so it does not carry a scope of its own. ### Does tagging send email or fire an automation? No. Tagging carries no side effects today, over the dashboard or the API: no webhook, no automation trigger, no email. ### How do I "tag the next 20 people who don't have this tag yet"? Build a segment with a `tag` condition (`operator: not_in`, the tag's id) plus whatever else you want, save or preview it, then call `POST /tags/{tag}/contacts` with `segment` and `limit: 20`. The oldest 20 matches, by signup date, get tagged. ### Can applying a tag touch contacts held past my plan's limit? No. Held contacts are excluded from both the explicit-handle and the segment path, exactly like every other bulk action in the product.