# List contacts > Page through your audience with cursor pagination and filters. Reads the same million-row-safe indexes the dashboard uses, so it stays fast at any size. Source: https://getlaunchlist.com/help/docs/api/contacts-list --- Requires the `contacts:read` scope. Returns your contacts, newest first, with [cursor pagination](/help/docs/api/pagination). ## Query parameters | Parameter | Type | Description | | ---------------- | -------- | ----------------------------------------------------------- | | `project` | string | **Required**, always. A `prj_` handle | | `limit` | integer | 1 to 100, default 25 | | `starting_after` | string | Page forward from this contact id | | `ending_before` | string | Page backward | | `status` | enum | `pending` (Not subscribed), `subscribed`, `unsubscribed`, `bounced`, `blocked`. `verified`, the old name of `subscribed`, is still accepted for now | | `stage` | string | One of the project's configured pipeline stages | | `identifier` | string | Exact match, canonicalized first | | `email` | string | Exact match on the address, canonicalized first | | `external_id` | string | Your own id for the contact | | `created[gte]` | ISO-8601 | Signed up on or after | | `created[lte]` | ISO-8601 | Signed up on or before | Filters combine with AND. > [!WARNING] `project` is required even when your key is pinned to one project > Pinning a key restricts which project it may read; it does not supply a default. A request > without `project` returns `400 parameter_missing`, whether the key is pinned or not. A pinned key > that names a project it does not cover gets `404 resource_missing`, never a `403`. > [!TIP] Searching for one person? Use `identifier` > It is canonicalized before matching, so a Gmail address with dots or a `+tag` finds the same > contact the deduplicator would. Paging the list to look for somebody is never necessary. For a > contact identified by a phone number or a username, filter by `email` instead: their address is > not in `identifier`. ## Paging The envelope is `{ object, url, has_more, data }`. There is no cursor field: you page forward by passing the id of the **last contact in `data`** as `starting_after`, and backward with `ending_before`. Keep going while `has_more` is `true`. ```bash curl "https://api.getlaunchlist.com/v1/contacts?project=prj_7Qk2mN4pR8&limit=25&starting_after=con_4mKp9Wq2Rx" \\ -H "Authorization: Bearer llk_live_8xKq3Rp9Wm2Nz7Vt6yL1cG5dH0jA4sE8uNqB2fXk" ``` Passing both `starting_after` and `ending_before`, or a handle that resolves to nothing, returns `400 parameter_invalid`. So does a `limit` outside 1 to 100; it is refused rather than clamped. ## Notes - Contacts held by the [overflow lock](/help/docs/account/billing) appear with `locked: true` and their identifying fields emptied. They are counted but withheld until the plan is upgraded. - `position` is read live from the leaderboard, not stored, so it is correct at the moment you ask. It is `null` for a contact who is not on the board. - `deliverability` is `null` until that verdict has been revealed with a credit. `trust_tier` is one of `verified`, `valid`, `unknown`, `risky`, `fake`. - Ordering is by creation time, newest first, with the id as a tiebreaker. That is stable under concurrent writes, which is what makes the cursor reliable. ## Next - [Retrieve a contact](/help/docs/api/contacts-retrieve) ยท [Count contacts](/help/docs/api/contacts-count) - [Pagination](/help/docs/api/pagination)