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

# Beacon Validators

> Ethereum validator details, rewards, withdrawals, and deposits sourced from the Ethereum beacon chain

Query Ethereum beacon chain data for individual validators by index or BLS pubkey. Mainnet only.

<Warning>
  Beacon validator endpoints are an **add-on**, billed at a set monthly fee separate from your API credit pool. Contact sales to enable beacon access for your account.
</Warning>

<Info>
  **Cost:** Set monthly fee under the beacon add-on — no per-call credit deduction.
</Info>

<Note>
  **Rate limit:** 100 requests/min, on a dedicated beacon bucket (does not count against your portfolio rate limit).
</Note>

***

## Path parameters

All endpoints accept one of two path-parameter forms:

| Parameter | Type    | Description                                                                            |
| --------- | ------- | -------------------------------------------------------------------------------------- |
| `:index`  | integer | Non-negative validator index                                                           |
| `:pubkey` | string  | 48-byte BLS pubkey, hex-encoded with `0x` prefix (96 hex characters). Case-insensitive |

> **Note:** BLS pubkeys are **validator identifiers**, not wallet addresses.

***

## Validator Details

Get current state, lifecycle epochs, effective balance, and withdrawal credentials for a single validator.

### Endpoints

```bash theme={null}
GET https://api.octav.fi/v1/beacon/validators/details/index/:index
GET https://api.octav.fi/v1/beacon/validators/details/pubkey/:pubkey
```

### Response

<ResponseField name="validator" type="object">
  Core validator identity and status.

  <Expandable title="Fields">
    <ResponseField name="validator.index" type="number">Validator index</ResponseField>
    <ResponseField name="validator.pubkey" type="string">BLS pubkey (lowercase 0x-prefixed)</ResponseField>
    <ResponseField name="validator.slashed" type="boolean">Whether the validator has been slashed</ResponseField>

    <ResponseField name="validator.status" type="string">
      Validator lifecycle status. One of: `pending` | `active_ongoing` | `active_exiting` | `active_slashed` | `exited_unslashed` | `exited_slashed` | `withdrawal_possible` | `withdrawal_done`
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="lifecycle" type="object">
  Epoch-level lifecycle milestones. Fields are `null` when not yet reached.

  <Expandable title="Fields">
    <ResponseField name="lifecycle.activationEpoch" type="number | null">Epoch when the validator became active</ResponseField>
    <ResponseField name="lifecycle.activationTimestamp" type="number | null">Unix timestamp of activation</ResponseField>
    <ResponseField name="lifecycle.exitEpoch" type="number | null">Epoch when the validator exited (or will exit)</ResponseField>
    <ResponseField name="lifecycle.withdrawableEpoch" type="number | null">Epoch when withdrawal becomes possible</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="balance" type="object">
  Validator balances as wei strings.

  <Expandable title="Fields">
    <ResponseField name="balance.actual" type="string">Current balance in wei</ResponseField>
    <ResponseField name="balance.effective" type="string">Effective balance in wei (used for reward/penalty calculations)</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="withdrawalCredentials" type="object">
  Withdrawal credential info. `address` is only present for 0x01-prefix validators.

  <Expandable title="Fields">
    <ResponseField name="withdrawalCredentials.prefix" type="number">Credential prefix byte (0 = BLS, 1 = EVM address)</ResponseField>
    <ResponseField name="withdrawalCredentials.credential" type="string">Full credential hex string</ResponseField>
    <ResponseField name="withdrawalCredentials.address" type="string | null">EVM withdrawal address (0x01 only, otherwise null)</ResponseField>
  </Expandable>
</ResponseField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.octav.fi/v1/beacon/validators/details/index/123456" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.octav.fi/v1/beacon/validators/details/index/123456',
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  );
  const details = await response.json();
  console.log(`Status: ${details.validator.status}`);
  console.log(`Actual balance: ${details.balance.actual} wei`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://api.octav.fi/v1/beacon/validators/details/index/123456',
      headers={'Authorization': f'Bearer {api_key}'}
  )
  details = response.json()
  print(f"Status: {details['validator']['status']}")
  ```
</CodeGroup>

<Accordion title="Example Response" icon="code">
  ```json theme={null}
  {
    "validator": {
      "index": 123456,
      "pubkey": "0xb03d97937ae39b38a44f90141dcf7f0420c3b9b21caee1a7603046bc9237f5c96b61714474249dafe0a20af583a4fc07",
      "slashed": false,
      "status": "active_ongoing"
    },
    "lifecycle": {
      "activationEpoch": 34567,
      "activationTimestamp": 1672531200,
      "exitEpoch": null,
      "withdrawableEpoch": null
    },
    "balance": {
      "actual": "32045123456789",
      "effective": "32000000000000"
    },
    "withdrawalCredentials": {
      "prefix": 1,
      "credential": "0x0100000000000000000000000a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2",
      "address": "0x0a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2"
    }
  }
  ```
</Accordion>

***

## Validator Rewards

Paginated reward buckets aggregated by `epoch`, `day`, `week`, or `month` for a time range. Typical use case: pull daily rewards for a known validator pubkey for portfolio reporting (Lido, Beaver, etc.).

<Info>
  **Data availability:** Rewards are available from **the Merge** (epoch 146875, September 15, 2022) onward. If you need data going further back, contact us.
</Info>

### Endpoints

```bash theme={null}
GET https://api.octav.fi/v1/beacon/validators/rewards/index/:index
GET https://api.octav.fi/v1/beacon/validators/rewards/pubkey/:pubkey
```

### Query Parameters

<ParamField query="rangeType" type="string" required>
  Determines how `rangeFrom` / `rangeTo` are interpreted.

  * `timestamp` — values are Unix seconds. Max range: 31,536,000 seconds (1 year).
  * `epoch` — values are beacon epoch numbers. Max range: 82,125 epochs (1 year).
</ParamField>

<ParamField query="rangeFrom" type="integer" required>
  Start of the range (inclusive). Must be ≥ 0 and ≤ `rangeTo`. Interpreted as Unix seconds or epoch number per `rangeType`.
</ParamField>

<ParamField query="rangeTo" type="integer" required>
  End of the range (inclusive). Must be ≥ 0. Interpreted as Unix seconds or epoch number per `rangeType`.
</ParamField>

<Note>
  Maximum 1-year range. Cap is **31,536,000 seconds** when `rangeType=timestamp`, **82,125 epochs** when `rangeType=epoch`. For better performance, keep ranges as small as your use case allows.
</Note>

<ParamField query="granularity" type="string" required>
  Bucket size for aggregation. One of: `epoch` | `day` | `week` | `month`
</ParamField>

<ParamField query="offset" type="number" default="0">
  Pagination offset (number of records to skip).
</ParamField>

<ParamField query="limit" type="number" default="10">
  Number of records to return. Max `10`.
</ParamField>

### Response

<ResponseField name="data" type="ValidatorRewardSummary[]">
  Array of reward buckets.

  <Expandable title="ValidatorRewardSummary fields">
    <ResponseField name="epochStart" type="number">First epoch in this bucket</ResponseField>
    <ResponseField name="epochEnd" type="number">Last epoch in this bucket</ResponseField>
    <ResponseField name="timestampStart" type="number">Unix timestamp of bucket start</ResponseField>
    <ResponseField name="timestampEnd" type="number">Unix timestamp of bucket end</ResponseField>
    <ResponseField name="dateStart" type="string">ISO date string of bucket start</ResponseField>
    <ResponseField name="dateEnd" type="string">ISO date string of bucket end</ResponseField>
    <ResponseField name="consensusReward" type="string">Consensus layer reward in wei</ResponseField>
    <ResponseField name="consensusPotentialReward" type="string">Maximum possible consensus reward in wei</ResponseField>
    <ResponseField name="consensusEfficiency" type="number | null">Ratio of actual to potential consensus reward (0–1), or null if not computable</ResponseField>
    <ResponseField name="executionReward" type="string">Execution layer (MEV + tips) reward in wei</ResponseField>
    <ResponseField name="attestationsIncluded" type="number">Number of attestations included on-chain</ResponseField>
    <ResponseField name="attestationsMissed" type="number">Number of attestations missed</ResponseField>
    <ResponseField name="syncParticipations" type="number">Sync committee participations</ResponseField>
    <ResponseField name="syncMissed" type="number">Sync committee slots missed</ResponseField>
    <ResponseField name="proposedBlocks" type="number">Blocks proposed</ResponseField>
    <ResponseField name="missedBlocks" type="number">Block proposals missed</ResponseField>
    <ResponseField name="mevBlocks" type="number">Blocks proposed with MEV</ResponseField>
    <ResponseField name="endEffectiveBalance" type="string">Effective balance at the end of the bucket, in wei</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="offset" type="number">Current pagination offset</ResponseField>
<ResponseField name="limit" type="number">Records per page</ResponseField>
<ResponseField name="totalRows" type="number">Total number of matching buckets</ResponseField>
<ResponseField name="pages" type="number">Total number of pages</ResponseField>

### Example

<CodeGroup>
  ```bash cURL (timestamp mode) theme={null}
  curl "https://api.octav.fi/v1/beacon/validators/rewards/index/123456?rangeType=timestamp&rangeFrom=1704067200&rangeTo=1706745600&granularity=day&limit=10" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```bash cURL (epoch mode) theme={null}
  curl "https://api.octav.fi/v1/beacon/validators/rewards/index/123456?rangeType=epoch&rangeFrom=231200&rangeTo=231424&granularity=epoch&limit=10" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  // timestamp mode
  const params = new URLSearchParams({
    rangeType: 'timestamp',
    rangeFrom: '1704067200',
    rangeTo: '1706745600',
    granularity: 'day',
    limit: '10'
  });

  const response = await fetch(
    `https://api.octav.fi/v1/beacon/validators/rewards/index/123456?${params}`,
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  );
  const rewards = await response.json();
  rewards.data.forEach(bucket => {
    console.log(`${bucket.dateStart}: consensus ${bucket.consensusReward} wei`);
  });
  ```

  ```python Python theme={null}
  import requests

  # epoch mode
  response = requests.get(
      'https://api.octav.fi/v1/beacon/validators/rewards/index/123456',
      params={
          'rangeType': 'epoch',
          'rangeFrom': 231200,
          'rangeTo': 231424,
          'granularity': 'epoch',
          'limit': 10
      },
      headers={'Authorization': f'Bearer {api_key}'}
  )
  data = response.json()
  for bucket in data['data']:
      print(f"{bucket['dateStart']}: {bucket['consensusReward']} wei")
  ```
</CodeGroup>

<Accordion title="Example Response" icon="code">
  ```json theme={null}
  {
    "data": [
      {
        "epochStart": 231200,
        "epochEnd": 231424,
        "timestampStart": 1704067200,
        "timestampEnd": 1704153600,
        "dateStart": "2024-01-01",
        "dateEnd": "2024-01-02",
        "consensusReward": "2450000000000",
        "consensusPotentialReward": "2500000000000",
        "consensusEfficiency": 0.98,
        "executionReward": "125000000000",
        "attestationsIncluded": 224,
        "attestationsMissed": 0,
        "syncParticipations": 0,
        "syncMissed": 0,
        "proposedBlocks": 0,
        "missedBlocks": 0,
        "mevBlocks": 0,
        "endEffectiveBalance": "32000000000000"
      }
    ],
    "offset": 0,
    "limit": 10,
    "totalRows": 31,
    "pages": 4
  }
  ```
</Accordion>

***

## Validator Withdrawals

Paginated list of withdrawals processed for a validator.

### Endpoints

```bash theme={null}
GET https://api.octav.fi/v1/beacon/validators/withdrawals/index/:index
GET https://api.octav.fi/v1/beacon/validators/withdrawals/pubkey/:pubkey
```

### Query Parameters

<ParamField query="offset" type="number" default="0">
  Pagination offset.
</ParamField>

<ParamField query="limit" type="number" default="10">
  Number of records to return. Max `10`.
</ParamField>

### Response

<ResponseField name="data" type="ValidatorWithdrawal[]">
  Array of withdrawal events.

  <Expandable title="ValidatorWithdrawal fields">
    <ResponseField name="slot" type="number">Slot in which the withdrawal was processed</ResponseField>
    <ResponseField name="epoch" type="number">Epoch containing the slot</ResponseField>
    <ResponseField name="timestamp" type="number">Unix timestamp of the withdrawal</ResponseField>
    <ResponseField name="amount" type="string">Withdrawn amount in wei</ResponseField>
    <ResponseField name="address" type="string">EVM address that received the withdrawal</ResponseField>
    <ResponseField name="index" type="number">Validator index</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="offset" type="number">Current pagination offset</ResponseField>
<ResponseField name="limit" type="number">Records per page</ResponseField>
<ResponseField name="totalRows" type="number">Total number of withdrawals</ResponseField>
<ResponseField name="pages" type="number">Total number of pages</ResponseField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.octav.fi/v1/beacon/validators/withdrawals/index/123456?limit=10&offset=0" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.octav.fi/v1/beacon/validators/withdrawals/index/123456?limit=10&offset=0',
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  );
  const withdrawals = await response.json();
  console.log(`Total withdrawals: ${withdrawals.totalRows}`);
  ```
</CodeGroup>

<Accordion title="Example Response" icon="code">
  ```json theme={null}
  {
    "data": [
      {
        "slot": 8901234,
        "epoch": 278163,
        "timestamp": 1706745600,
        "amount": "32000000000000",
        "address": "0x0a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2",
        "index": 123456
      }
    ],
    "offset": 0,
    "limit": 10,
    "totalRows": 3,
    "pages": 1
  }
  ```
</Accordion>

***

## Validator Deposits

Paginated list of deposits made to a validator.

### Endpoints

```bash theme={null}
GET https://api.octav.fi/v1/beacon/validators/deposits/index/:index
GET https://api.octav.fi/v1/beacon/validators/deposits/pubkey/:pubkey
```

### Query Parameters

<ParamField query="offset" type="number" default="0">
  Pagination offset.
</ParamField>

<ParamField query="limit" type="number" default="10">
  Number of records to return. Max `10`.
</ParamField>

### Response

<ResponseField name="data" type="ValidatorDeposit[]">
  Array of deposit events.

  <Expandable title="ValidatorDeposit fields">
    <ResponseField name="slot" type="number">Slot in which the deposit was included</ResponseField>
    <ResponseField name="epoch" type="number">Epoch containing the slot</ResponseField>
    <ResponseField name="timestamp" type="number">Unix timestamp of the deposit</ResponseField>
    <ResponseField name="amount" type="string">Deposited amount in wei</ResponseField>
    <ResponseField name="depositIndex" type="number">Global deposit contract index</ResponseField>
    <ResponseField name="withdrawalCredentials" type="string">Full withdrawal credential hex string</ResponseField>
    <ResponseField name="withdrawalAddress" type="string | null">EVM withdrawal address (0x01 prefix only, otherwise null)</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="offset" type="number">Current pagination offset</ResponseField>
<ResponseField name="limit" type="number">Records per page</ResponseField>
<ResponseField name="totalRows" type="number">Total number of deposits</ResponseField>
<ResponseField name="pages" type="number">Total number of pages</ResponseField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.octav.fi/v1/beacon/validators/deposits/pubkey/0xb03d97937ae39b38a44f90141dcf7f0420c3b9b21caee1a7603046bc9237f5c96b61714474249dafe0a20af583a4fc07?limit=10" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const pubkey = '0xb03d97937ae39b38a44f90141dcf7f0420c3b9b21caee1a7603046bc9237f5c96b61714474249dafe0a20af583a4fc07';

  const response = await fetch(
    `https://api.octav.fi/v1/beacon/validators/deposits/pubkey/${pubkey}?limit=10`,
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  );
  const deposits = await response.json();
  console.log(`Total deposits: ${deposits.totalRows}`);
  ```
</CodeGroup>

<Accordion title="Example Response" icon="code">
  ```json theme={null}
  {
    "data": [
      {
        "slot": 1234567,
        "epoch": 38579,
        "timestamp": 1634567890,
        "amount": "32000000000000000000",
        "depositIndex": 456789,
        "withdrawalCredentials": "0x0100000000000000000000000a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2",
        "withdrawalAddress": "0x0a8c8aaa3f2ddf1b22a3f5b2a2e9b7d8c6f4e1a2"
      }
    ],
    "offset": 0,
    "limit": 10,
    "totalRows": 1,
    "pages": 1
  }
  ```
</Accordion>

***

## Error Responses

<AccordionGroup>
  <Accordion title="400 Bad Request" icon="circle-exclamation">
    Input validation failed (Joi structured error).

    ```json theme={null}
    { "error": "\"index\" must be a non-negative integer" }
    ```

    Common causes: invalid `:index` or `:pubkey` format, missing required query params, `rangeFrom` > `rangeTo`, range exceeding 1-year cap, invalid `rangeType` or `granularity` value.
  </Accordion>

  <Accordion title="401 Unauthorized" icon="lock">
    Missing or invalid Bearer token.

    ```json theme={null}
    { "message": "Unauthorized" }
    ```
  </Accordion>

  <Accordion title="403 Forbidden" icon="ban">
    Valid token but account does not have enterprise beacon access.

    ```json theme={null}
    { "error": "Beacon access requires enterprise subscription. Contact sales." }
    ```
  </Accordion>

  <Accordion title="404 Validator Not Found" icon="magnifying-glass">
    Validator does not exist on mainnet.

    ```json theme={null}
    { "error": "Validator not found for index 99999999" }
    ```
  </Accordion>

  <Accordion title="500 Internal Server Error" icon="triangle-exclamation">
    Upstream beacon data error.

    ```
    Error fetching validator details
    ```
  </Accordion>
</AccordionGroup>

***

## Use Cases

<Tabs>
  <Tab title="Staking Yield in Portfolio" icon="chart-line">
    Aggregate daily rewards across a set of validators and convert wei to ETH for portfolio reporting.

    ```javascript theme={null}
    async function getDailyStakingYield(pubkeys, fromTs, toTs) {
      const buckets = {};

      for (const pubkey of pubkeys) {
        const params = new URLSearchParams({
          rangeType: 'timestamp',
          rangeFrom: String(fromTs),
          rangeTo: String(toTs),
          granularity: 'day',
          limit: '10'
        });

        const response = await fetch(
          `https://api.octav.fi/v1/beacon/validators/rewards/pubkey/${pubkey}?${params}`,
          { headers: { 'Authorization': `Bearer ${apiKey}` } }
        );
        const { data } = await response.json();

        data.forEach(b => {
          const total = BigInt(b.consensusReward) + BigInt(b.executionReward);
          buckets[b.dateStart] = (buckets[b.dateStart] ?? 0n) + total;
        });
      }

      return Object.entries(buckets).map(([date, wei]) => ({
        date,
        eth: Number(wei) / 1e18
      }));
    }
    ```
  </Tab>

  <Tab title="Validator Status" icon="circle-check">
    Read the canonical `validator.status` value to drive lifecycle UI or alerting.

    ```javascript theme={null}
    export const ValidatorStatus = {
      Pending: 'pending',
      ActiveOngoing: 'active_ongoing',
      ActiveExiting: 'active_exiting',
      ActiveSlashed: 'active_slashed',
      ExitedUnslashed: 'exited_unslashed',
      ExitedSlashed: 'exited_slashed',
      WithdrawalPossible: 'withdrawal_possible',
      WithdrawalDone: 'withdrawal_done',
    } as const;

    async function getValidatorStatus(index) {
      const response = await fetch(
        `https://api.octav.fi/v1/beacon/validators/details/index/${index}`,
        { headers: { 'Authorization': `Bearer ${apiKey}` } }
      );
      const { validator } = await response.json();

      if (validator.status === ValidatorStatus.ActiveSlashed ||
          validator.status === ValidatorStatus.ExitedSlashed) {
        alert(`Validator ${validator.index} has been slashed`);
      }

      return validator.status;
    }
    ```
  </Tab>

  <Tab title="Withdrawals by Address" icon="arrow-down-from-bracket">
    Paginate every withdrawal for a validator and bucket totals by recipient address (useful for tax reporting and proof-of-reserves).

    ```javascript theme={null}
    async function getWithdrawalsByAddress(index) {
      const totals = {};
      let offset = 0;

      while (true) {
        const response = await fetch(
          `https://api.octav.fi/v1/beacon/validators/withdrawals/index/${index}?limit=10&offset=${offset}`,
          { headers: { 'Authorization': `Bearer ${apiKey}` } }
        );
        const { data, totalRows } = await response.json();

        data.forEach(w => {
          totals[w.address] = (totals[w.address] ?? 0n) + BigInt(w.amount);
        });

        offset += data.length;
        if (offset >= totalRows || data.length === 0) break;
      }

      return Object.entries(totals).map(([address, wei]) => ({
        address,
        eth: Number(wei) / 1e18
      }));
    }
    ```
  </Tab>
</Tabs>

***

## Best Practices

<AccordionGroup>
  <Accordion title="Cache Validator Details Client-Side" icon="database">
    `validator.status`, `lifecycle.*`, and `withdrawalCredentials.*` change on the order of minutes-to-hours, not seconds. Cache details responses for several minutes to cut latency and load.

    ```javascript theme={null}
    const detailsCache = new Map(); // index -> { value, expiresAt }
    const TTL_MS = 5 * 60 * 1000;

    async function getValidatorDetails(index) {
      const cached = detailsCache.get(index);
      if (cached && cached.expiresAt > Date.now()) return cached.value;

      const response = await fetch(
        `https://api.octav.fi/v1/beacon/validators/details/index/${index}`,
        { headers: { 'Authorization': `Bearer ${apiKey}` } }
      );
      const value = await response.json();

      detailsCache.set(index, { value, expiresAt: Date.now() + TTL_MS });
      return value;
    }
    ```
  </Accordion>

  <Accordion title="Pick Granularity to Match Range" icon="ruler">
    Reward buckets at `granularity=epoch` over a one-year range can yield \~82,000 rows. Match the granularity to the range so a single query returns a meaningful page.

    | Range             | Suggested granularity |
    | ----------------- | --------------------- |
    | \< 1 day          | `epoch`               |
    | 1–30 days         | `day`                 |
    | 1–6 months        | `week`                |
    | 6 months – 1 year | `month`               |

    Going finer than this still works, but you'll need to paginate (`limit` is capped at 10) and stitch results together.
  </Accordion>

  <Accordion title="Convert Wei With BigInt" icon="calculator">
    Reward and balance fields are returned as decimal strings in wei. Summing across long ranges or many validators can exceed `Number.MAX_SAFE_INTEGER` — parse with `BigInt`, divide by `10^18` only at the display step.

    ```javascript theme={null}
    const totalWei = rewards.data.reduce(
      (sum, b) => sum + BigInt(b.consensusReward) + BigInt(b.executionReward),
      0n
    );
    const totalEth = Number(totalWei) / 1e18;
    ```
  </Accordion>

  <Accordion title="Paginate to Fetch All Records" icon="layer-group">
    `limit` is capped at **10** for rewards, withdrawals, and deposits. Loop on `offset` until you've consumed `totalRows`.

    ```javascript theme={null}
    async function fetchAll(url) {
      const all = [];
      let offset = 0;

      while (true) {
        const sep = url.includes('?') ? '&' : '?';
        const response = await fetch(
          `${url}${sep}limit=10&offset=${offset}`,
          { headers: { 'Authorization': `Bearer ${apiKey}` } }
        );
        const { data, totalRows } = await response.json();

        all.push(...data);
        offset += data.length;
        if (offset >= totalRows || data.length === 0) break;
      }

      return all;
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Portfolio" icon="chart-pie" href="/api/endpoints/portfolio">
    Get full portfolio holdings for a wallet address
  </Card>

  <Card title="Transactions" icon="receipt" href="/api/endpoints/transactions">
    View on-chain transaction history
  </Card>
</CardGroup>
