> ## Documentation Index
> Fetch the complete documentation index at: https://docs.engini.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Page through a large catalog

> Walk every application, tool, or connection with offset/top - and know when to stop.

<Info>**Uses:** Python · TypeScript · CLI · REST API</Info>

List endpoints are offset-based. Ask for a page with `offset` and `top`, and read `totalCount` to know when you're done.

<Note>
  cURL samples assume `BASE=https://api.engini.io/v1` and `AUTH="x-api-key: $ENGINI_API_KEY"` - the setup from the [REST walkthrough](/examples/rest-walkthrough). Manual paging is a **REST-only concern**: the SDKs and the CLI page for you, which is why their tabs below are one-liners.
</Note>

```json theme={null}
{ "items": [ ... ], "totalCount": 1374, "offset": 0, "top": 100 }
```

## The loop

<CodeGroup>
  ```python Python theme={null}
  # the SDK already does this - list methods return the complete set
  tools = client.tools.get(applications=["monday"])
  apps  = client.applications.list(available=True)
  ```

  ```typescript TypeScript theme={null}
  const tools = await client.tools.get({ applications: ["monday"] });
  const apps = await client.applications.list({ available: true });
  ```

  ```bash CLI theme={null}
  # the CLI pages for you; --limit caps the result, not the page size
  engini tools list --application monday --limit 500 | jq -r '.[].tool_slug'
  engini applications list --available | jq 'length'
  ```

  ```bash cURL theme={null}
  offset=0
  while :; do
    page=$(curl -s "$BASE/tools?offset=$offset&top=100" -H "$AUTH")
    echo "$page" | jq -r '.items[].toolSlug'
    count=$(echo "$page" | jq '.items | length')
    total=$(echo "$page" | jq '.totalCount')
    offset=$((offset + count))
    { [ "$count" -eq 0 ] || [ "$offset" -ge "$total" ]; } && break
  done
  ```
</CodeGroup>

<Warning>
  Terminate on **both** conditions - an empty page *and* `offset >= totalCount`. Checking only `totalCount` can loop forever if a page comes back short; checking only emptiness costs an extra request every time.
</Warning>

## Defaults worth knowing

| Endpoint                           | `top` default | Max  |
| ---------------------------------- | ------------- | ---- |
| `GET /v1/applications`             | 100           | -    |
| `GET /v1/tools`                    | 100           | -    |
| `GET /v1/toolsets`                 | 100           | 100  |
| `GET /v1/connections`              | **25**        | 100  |
| `GET /v1/connections/{id}/objects` | 100           | 1000 |

Connections defaulting to 25 catches people out - a naive single request looks like the full list when it isn't.

## Filter before you page

Almost always faster than walking everything:

<CodeGroup>
  ```python Python theme={null}
  tools = client.tools.get(applications=["monday"])
  apps  = client.applications.list(available=True, search="crm")
  conns = client.connections.list(application="gmail")
  ```

  ```typescript TypeScript theme={null}
  const tools = await client.tools.get({ applications: ["monday"] });
  const apps = await client.applications.list({ available: true, search: "crm" });
  const conns = await client.connections.list("gmail");
  ```

  ```bash CLI theme={null}
  engini tools list --application monday
  engini applications list --available --search crm
  engini connections list --application gmail
  ```

  ```bash cURL theme={null}
  curl -s "$BASE/tools?applicationSlug=monday&top=100" -H "$AUTH"
  curl -s "$BASE/applications?available=true&search=crm" -H "$AUTH"
  curl -s "$BASE/connections?applicationSlug=gmail" -H "$AUTH"
  ```
</CodeGroup>

<Note>
  `?search=` runs a semantic search and is billed against a **separate, stricter rate-limit budget** than ordinary reads. It's the right tool for "find me something like X", but don't put it inside a pagination loop - filter with `applicationSlug` or `name` there instead.
</Note>

## Rate limits while paging

Two budgets apply per account - a per-second burst and an hourly window. A tight loop over a large catalog can trip the burst, so handle `429` rather than assuming it won't happen:

```bash theme={null}
resp=$(curl -s -w '\n%{http_code}' "$BASE/tools?offset=$offset&top=100" -H "$AUTH")
code=$(echo "$resp" | tail -1)
if [ "$code" = "429" ]; then
  sleep "$(curl -sI "$BASE/tools" -H "$AUTH" | awk -F': ' '/[Rr]etry-[Aa]fter/{print $2+0}')"
  continue
fi
```

`Retry-After` is in seconds: about `1` when the burst window trips, `3600` when the hourly one does. The difference matters - a one-second pause and an hour-long wait deserve very different handling.

In the SDKs the same `429` surfaces as a typed `EnginiRateLimitError` - neither SDK retries for you, by design. See [SDK errors & pagination](/sdk/errors-and-pagination) for the retry pattern.
