---
title: "Page through results"
description: "Iterate over listings, or fetch one page at a time and resume later."
canonical_url: "https://docs-8831a2g1f-poetry.vercel.app/guides/pagination"
---
# Page through results

> Iterate over listings, or fetch one page at a time and resume later.

Most listings come in two forms. `list()` returns an async iterable that fetches pages as the loop needs them. `listPage()` returns one page.

## Iterate

Use `for await` when you want every item, or the first few:

```ts
let seen = 0

for await (const thread of gh.threads.list(repo, { state: 'open' })) {
  console.log(thread.title)
  if (++seen === 20) {
    break
  }
}
```

A `break` stops the requests. `perPage` sets the page size, and `signal` aborts the listing.

## Fetch a page

A page has `items` and an optional `cursor`. Where the forge reports them, it also has `rateLimit` and `warnings`:

```ts
const page = await gh.threads.listPage(repo, { perPage: 50 })

for (const thread of page.items) {
  console.log(thread.title)
}
```

`cursor` is absent when the listing is exhausted. Otherwise, pass it back in the options to read the next page:

```ts
if (page.cursor) {
  const next = await gh.threads.listPage(repo, {
    perPage: 50,
    cursor: page.cursor,
  })
}
```

Persist the whole cursor object and hand it back unchanged. Its fields depend on the forge.

## Poll for changes

GitHub notifications support conditional requests. The first page has an `etag`. To read the first page again, pass it back as the `etag` option. When nothing changed, the page has `notModified: true` and no items:

```ts
let etag: string | undefined

const page = await gh.notifications.listPage({ etag })

if (!page.notModified) {
  handle(page.items)
  etag = page.etag
}
```

For push delivery instead of polling, see [Events](https://docs-8831a2g1f-poetry.vercel.app/concepts/events).


## Sitemap

See the full [sitemap](https://docs-8831a2g1f-poetry.vercel.app/sitemap.md) for all pages.
