# Pagination https://api-docs.lumar.io/docs/graphql/pagination The Lumar API uses cursor-based pagination for all connection fields. Every connection exposes the `pageInfo` object: ```graphql type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } ``` Where `hasNextPage` and `hasPreviousPage` will tell us whether there is a next or previous page or we hit the last or first one respectively. On the other hand, `startCursor` and `endCursor` will give us _cursor_ values for the start and end of the collection retrieved. These can be used as the `after` or `before` parameter in subsequent queries. ## Pagination parameters | Parameter | Type | Description | | --------- | -------- | --------------------------------------------------------------------- | | `first` | `Int` | Return the first N items from the start of the set. Use with `after`. | | `after` | `String` | Cursor to start after (from `endCursor`). | | `last` | `Int` | Return the last N items from the end of the set. Use with `before`. | | `before` | `String` | Cursor to end before (from `startCursor`). | Use `first` / `after` for forward pagination and `last` / `before` for backward pagination. ## Forward pagination example We run the following query: ```graphql query firstPage { me { accounts(first: 1) { nodes { projects(first: 3) { pageInfo { hasNextPage endCursor } nodes { name } } } } } } ``` **Response:** ```json { "data": { "me": { "accounts": { "nodes": [ { "projects": { "pageInfo": { "hasNextPage": true, "endCursor": "Mw" }, "nodes": [ { "name": "Cool Project" }, { "name": "Deep Crawling" }, { "name": "Pro Ject" } ] } } ] } } } } ``` We can then get the second page with the query: ```graphql query secondPage { me { accounts(first: 1) { nodes { projects(first: 3, after: "Mw") { pageInfo { hasNextPage endCursor } nodes { name } } } } } } ``` **Response:** ```json { "data": { "me": { "accounts": { "nodes": [ { "projects": { "pageInfo": { "hasNextPage": true, "endCursor": "Ng" }, "nodes": [ { "name": "Dev.io: Main" }, { "name": "Dev.io: Blog" }, { "name": "Some Project" } ] } } ] } } } } ``` ## Backward pagination You can also paginate backward from the end of a collection using `last` and `before`: ```graphql query LastPageProjects { me { accounts(first: 1) { nodes { projects(last: 3) { pageInfo { hasPreviousPage startCursor } nodes { name } } } } } } ``` **Response:** ```json { "data": { "me": { "accounts": { "nodes": [ { "projects": { "pageInfo": { "hasPreviousPage": true, "startCursor": "OA" }, "nodes": [ { "name": "Final Project A" }, { "name": "Final Project B" }, { "name": "Final Project C" } ] } } ] } } } } ``` To get the previous page, pass the `startCursor` value as the `before` parameter: ```graphql query PreviousPageProjects { me { accounts(first: 1) { nodes { projects(last: 3, before: "OA") { pageInfo { hasPreviousPage startCursor } nodes { name } } } } } } ``` **Response:** ```json { "data": { "me": { "accounts": { "nodes": [ { "projects": { "pageInfo": { "hasPreviousPage": true, "startCursor": "NQ" }, "nodes": [ { "name": "Project D" }, { "name": "Project E" }, { "name": "Project F" } ] } } ] } } } } ``` ## Pagination loop in TypeScript For programmatic access, loop through all pages until `hasNextPage` is `false`: ```typescript async function fetchAllPages( queryFn: (cursor: string | null) => Promise<{ nodes: T[]; pageInfo: { hasNextPage: boolean; endCursor: string | null }; }>, ): Promise { const allNodes: T[] = []; let cursor: string | null = null; let hasNextPage = true; while (hasNextPage) { const page = await queryFn(cursor); allNodes.push(...page.nodes); hasNextPage = page.pageInfo.hasNextPage; cursor = page.pageInfo.endCursor; } return allNodes; } ``` ## Tips for large datasets - Use a reasonable page size (50--500) depending on the number of fields requested. - For very large exports (tens of thousands of records), consider using [report downloads](generate-report-downloads) instead of paginating through the API. - Include `totalCount` in your first request to estimate the total number of pages. - Avoid combining `first` and `last` in the same request -- use one direction at a time.