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

# Pagination in Arbitrage API List Endpoints

> Learn how Arbitrage paginates list responses using limit and offset, how to read the meta object, and how to efficiently iterate through large result sets.

List endpoints in the Arbitrage API return results in pages rather than all at once. You control the page size and position using two query parameters — `limit` and `offset` — and every paginated response includes a `meta` object that tells you exactly where you are and how many total results exist. This makes it straightforward to build efficient polling loops, bulk exports, and infinite-scroll interfaces.

## Query parameters

Add `limit` and `offset` directly to your request URL as query parameters.

| Parameter | Type    | Default | Maximum | Description                                      |
| --------- | ------- | ------- | ------- | ------------------------------------------------ |
| `limit`   | integer | `50`    | `200`   | Number of items to return in this response       |
| `offset`  | integer | `0`     | —       | Number of items to skip before returning results |

Both parameters are optional. Omitting them returns the first 50 results.

## The meta object

Every paginated response wraps results in a `data` array and includes a `meta` object:

```json theme={null}
{
  "data": [...],
  "meta": {
    "count": 50,
    "total": 312,
    "offset": 0,
    "limit": 50
  }
}
```

| Field    | Type    | Description                                      |
| -------- | ------- | ------------------------------------------------ |
| `count`  | integer | Number of items in the current response page     |
| `total`  | integer | Total items matching your query across all pages |
| `offset` | integer | The offset value used for this response          |
| `limit`  | integer | The limit value used for this response           |

Use `total` to calculate how many pages remain. Use `count` to detect the last page — when `count` is less than `limit`, you've reached the end.

<Note>
  The arbitrage endpoints do not always return a `total` count. On those endpoints, rely on `meta.count` to detect the end of results: when `count` is less than `limit`, there are no more pages.
</Note>

## Fetching a page with curl

To fetch the second page of 25 results, set `offset=25` and `limit=25`:

```bash curl theme={null}
curl -H "X-API-Key: pdx_your_key_here" \
  "https://api.predexy.com/api/v1/external/arbitrage/opportunities?limit=25&offset=25"
```

To fetch the maximum page size, use `limit=200`:

```bash curl theme={null}
curl -H "X-API-Key: pdx_your_key_here" \
  "https://api.predexy.com/api/v1/discover?limit=200&offset=0"
```

## Iterating through all pages in Python

The function below fetches all results from a paginated endpoint by incrementing `offset` until all items have been retrieved. It uses `limit=200` to minimize the number of requests:

```python python theme={null}
import requests
from typing import Any

API_KEY = "pdx_your_key_here"
BASE_URL = "https://api.predexy.com"

def fetch_all(path: str, params: dict | None = None) -> list[Any]:
    """Fetch all pages from a Predexy list endpoint."""
    headers = {"X-API-Key": API_KEY}
    page_size = 200
    offset = 0
    all_items = []

    while True:
        query = {**(params or {}), "limit": page_size, "offset": offset}
        response = requests.get(f"{BASE_URL}{path}", headers=headers, params=query)
        response.raise_for_status()

        body = response.json()
        data = body.get("data", [])
        meta = body.get("meta", {})

        all_items.extend(data)
        offset += meta.get("count", 0)

        # Stop when we've received fewer items than requested
        # or when offset has reached total (if total is available)
        total = meta.get("total")
        if total is not None and offset >= total:
            break
        if meta.get("count", 0) < page_size:
            break

    return all_items

# Example: fetch all discovery items
questions = fetch_all("/api/v1/discover")
print(f"Fetched {len(questions)} questions.")
```

<Tip>
  Use `limit=200` for bulk data fetching. It's the maximum allowed and reduces the number of round trips — especially useful for one-time exports or building a local snapshot of all canonical questions.
</Tip>

## The meta.source field on arbitrage endpoints

Arbitrage responses include an additional `meta.source` field that indicates where the data was served from:

| Value            | Meaning                                                                                      |
| ---------------- | -------------------------------------------------------------------------------------------- |
| `database`       | Results were read directly from the primary database — fresh, authoritative data             |
| `cache`          | Results were served from the cache layer — may be up to a few seconds behind the latest scan |
| *(empty string)* | Source is not tracked for this response type                                                 |

You can use `meta.source` to understand data freshness when timing is critical. If you need the most current opportunity data, check that `meta.source` is `database` or confirm that `freshness.is_stale` is `false` on individual opportunities.

<Warning>
  Serving from cache is normal and expected under high load. Stale cache entries are typically only a few seconds old. If freshness is critical to your strategy, also check the `detected_at` and `last_seen_at` fields on individual opportunities.
</Warning>
