# Pagination > Every list endpoint is cursor-paginated, so paging stays correct and fast even when the list is changing underneath you. Source: https://getlaunchlist.com/help/docs/api/pagination --- Every list endpoint returns the same envelope: ```json { "object": "list", "url": "/v1/contacts", "has_more": true, "data": [/* … */] } ``` Four fields, and that is the whole shape. `url` is the path the page came from, `has_more` says whether another page exists in the direction you are walking, and `data` holds the objects. There is deliberately no total and no page count, because both cost a full scan. > [!WARNING] There is no cursor field in the response > The cursor **is an object id**. To get the next page, take the `id` of the last item in `data` and > send it as `starting_after`. Nothing in the envelope hands you a ready-made cursor. ## Paging forward Pass the last id you saw as `starting_after`: ```bash curl "https://api.getlaunchlist.com/v1/contacts?project=prj_7Qk2mN4pR8&limit=100" \ -H "Authorization: Bearer llk_live_8xKq3Rp9Wm2Nz7Vt6yL1cG5dH0jA4sE8uNqB2fXk" curl "https://api.getlaunchlist.com/v1/contacts?project=prj_7Qk2mN4pR8&limit=100&starting_after=con_4mKp9Wq2Rx" \ -H "Authorization: Bearer llk_live_8xKq3Rp9Wm2Nz7Vt6yL1cG5dH0jA4sE8uNqB2fXk" ``` Here `con_4mKp9Wq2Rx` is the `id` of the last contact in the first page's `data`. Keep going while `has_more` is true. `ending_before` pages the other way, and every page reads newest first either way. | Parameter | Default | Accepts | | ---------------- | ------- | ------------------------------------------ | | `limit` | 25 | 1 to 100 | | `starting_after` | none | an object id, to page toward older records | | `ending_before` | none | an object id, to page toward newer records | These are validated rather than quietly corrected, so a paging bug surfaces as an error instead of as wrong data. A `limit` outside 1 to 100 is a `400`, sending both `starting_after` and `ending_before` is a `400`, and an id that does not resolve to an object in this list is a `400` too. ## Why cursors and not page numbers **Offset pagination breaks while you page.** Ask for page 2 after three new signups have arrived and you will re-read rows you already saw; if rows were deleted, you skip some. On a waitlist that is actively collecting signups, this is not a rare edge. It is the normal case. **Offset gets slower the deeper you go.** `OFFSET 50000` means the database counts fifty thousand rows before returning anything. A cursor is an indexed seek, so page 500 costs the same as page 1. At a million contacts, that difference is the whole design. ## A complete loop ```js const all = []; let cursor = null; let hasMore = true; while (hasMore) { const url = new URL('https://api.getlaunchlist.com/v1/contacts'); url.searchParams.set('project', 'prj_7Qk2mN4pR8'); url.searchParams.set('limit', '100'); if (cursor) { url.searchParams.set('starting_after', cursor); } const page = await fetch(url, { headers: { Authorization: `Bearer ${process.env.LAUNCHLIST_KEY}` }, }).then((response) => response.json()); all.push(...page.data); // The cursor is the last id on this page. The response does not carry one. cursor = page.data.at(-1)?.id ?? null; hasMore = page.has_more && cursor !== null; } ``` > [!TIP] Do not page to count > `GET /v1/contacts/count` reads a maintained counter and answers instantly. Paging through a > million contacts to arrive at a number is expensive for both of us and it is never necessary. ## Next - [List contacts](/help/docs/api/contacts-list) - [Rate limits](/help/docs/api/rate-limits): pace a full sync ## Common questions ### How do I page through all my contacts via the API? Request a page, then pass the last item's id as starting_after to get the next one. Repeat while has_more is true. There are no page numbers and no offset. ### Why does the LaunchList API not use page numbers? Offset pagination skips and repeats rows when the list changes while you are paging, and it gets slower the deeper you go. Cursors are stable under concurrent writes and stay fast at any depth. ### How many results can I get per request? Up to 100, with a default of 25. Ask for what you will use: a larger page is one round trip, but a slower one.