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

# Token holders

> Ranked holders of a token, and one holder's balance and rank.

Holders lists every address that holds a token, largest balance first, with the
token's holder count and supply. Use it for a token's "top holders" table, for
distribution analysis, or (with the rank route) to show a user where they stand.

Set these once in your terminal. The sample values are real Testnet data, so
the read examples return real results.

```bash theme={null}
# Your Testnet key from app.lumeus.dev (starts with lum_test_)
export LUMEUS_API_KEY='paste-your-lum_test_-key'

export ACTIVITY_URL='https://api-testnet.lumeus.dev'
export ASSETS_URL='https://assets-testnet.lumeus.dev'
export WEBHOOKS_URL='https://webhooks-testnet.lumeus.dev'
export RPC_URL='https://rpc-testnet.lumeus.dev'

# Sample Testnet values
export ACCOUNT='GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR'
export ASSET='BTC:GAGVZMODPYFTULIVSY3RGFADIC5LF4LLSN37N4MFWGNZ57TYBXL2LQQX'
export CONTRACT='CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC'
export TX_HASH='1ae3b48681d8b27f31ee8f1893281e9a56fd7c45cf9909cf6a0599949079c2ce'
```

```bash curl theme={null}
curl --fail-with-body --get "$ASSETS_URL/assets/$ASSET/holders" \
  -H "Authorization: Bearer $LUMEUS_API_KEY" \
  --data-urlencode 'limit=100'
```

```js JavaScript theme={null}
const url = new URL(`${process.env.ASSETS_URL}/assets/${process.env.ASSET}/holders`);
url.searchParams.set('limit', '100');
const res = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.LUMEUS_API_KEY}` },
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const page = await res.json();
console.log(page.holder_count, 'holders, supply', page.supply);
for (const h of page._embedded.records) console.log(h.holder, h.balance);
```

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

res = requests.get(
    f"{os.environ['ASSETS_URL']}/assets/{os.environ['ASSET']}/holders",
    params={"limit": 100},
    headers={"Authorization": f"Bearer {os.environ['LUMEUS_API_KEY']}"},
    timeout=30,
)
res.raise_for_status()
page = res.json()
print(page["holder_count"], "holders, supply", page["supply"])
for h in page["_embedded"]["records"]:
    print(h["holder"], h["balance"])
```

Trimmed live response (first record of 55):

```json theme={null}
{
  "_embedded": {
    "records": [
      {
        "holder": "CCSXXPCXICXOC2MLU3UINWYKL6M5VNLVOTHA4ZQ5S5BEDZGRWDTP52CG",
        "asset": "BTC:GAGVZMODPYFTULIVSY3RGFADIC5LF4LLSN37N4MFWGNZ57TYBXL2LQQX",
        "kind": "contract", "balance": "1342.8845961", "balance_raw": "13428845961",
        "decimals": 7, "source": "ledger_entry", "verified": true,
        "archived": false, "authorized": true, "last_modified_ledger": 5331443,
        "paging_token": "WyIxMzQyODg0NTk2MSIsIkNDU1hYUENYSUNYT0MyTUxVM1VJTldZS0w2TTVWTkxWT1RIQTRaUTVTNUJFRFpHUldEVFA1MkNHIl0"
      }
    ]
  },
  "asset": "BTC:GAGVZMODPYFTULIVSY3RGFADIC5LF4LLSN37N4MFWGNZ57TYBXL2LQQX",
  "holder_count": 55,
  "supply": "2131.3861296",
  "supply_source": "exact",
  "decimals": 7,
  "limit": 100,
  "read_view": {"network": "testnet", "as_of_ledger": 5331463, "bootstrap_ledger": 5253887}
}
```

Holders are sorted by balance, largest first, then by address. For a classic
asset the list merges trustline (`G...`) holders and contract (`C...`) holders
of its SAC; `kind` says which. The page header carries `holder_count` (holders
with a non-zero balance), `supply` and `supply_source` (see
[supply](/assets-api/registry.md#supply)). With `as_of_ledger`, `supply` is `null`. The asset ID may be URL-encoded
(`BTC%3AG...`) or raw; both work.

## Parameters

| Parameter | Default | Rules |
| - | - | - |
| `min_balance` | none | Leave out holders below this decimal |
| `include` | none | `zero` also lists zero-balance trustlines |
| `as_of_ledger` | current | Holders at a past ledger, for assets with at most 100,000 holders; see [snapshots](/assets-api/snapshots.md) |
| `limit` | 50 | Values above 200 are lowered to 200 |
| `cursor` | none | From `_links.next.href`. Deep pages are reached only through the cursor |

Holders is a server-side route: [publishable keys](/guides/browser-keys.md) get
`403`.

## One holder's rank

Rank answers "how large is this address among all holders?". The sample
`ACCOUNT` does not hold `$ASSET`, so this example defines its largest holder:

```bash curl theme={null}
export HOLDER='CCSXXPCXICXOC2MLU3UINWYKL6M5VNLVOTHA4ZQ5S5BEDZGRWDTP52CG'

curl --fail-with-body "$ASSETS_URL/assets/$ASSET/holders/$HOLDER" \
  -H "Authorization: Bearer $LUMEUS_API_KEY"
```

```js JavaScript theme={null}
const HOLDER = 'CCSXXPCXICXOC2MLU3UINWYKL6M5VNLVOTHA4ZQ5S5BEDZGRWDTP52CG';
const res = await fetch(
  `${process.env.ASSETS_URL}/assets/${process.env.ASSET}/holders/${HOLDER}`,
  { headers: { Authorization: `Bearer ${process.env.LUMEUS_API_KEY}` } },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { rank, rank_exact, holder_count, record } = await res.json();
console.log(record.balance, `rank ${rank} of ${holder_count}`, rank_exact ? 'exact' : 'approximate');
```

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

HOLDER = "CCSXXPCXICXOC2MLU3UINWYKL6M5VNLVOTHA4ZQ5S5BEDZGRWDTP52CG"
res = requests.get(
    f"{os.environ['ASSETS_URL']}/assets/{os.environ['ASSET']}/holders/{HOLDER}",
    headers={"Authorization": f"Bearer {os.environ['LUMEUS_API_KEY']}"},
    timeout=30,
)
res.raise_for_status()
body = res.json()
print(body["record"]["balance"], f"rank {body['rank']} of {body['holder_count']}", "exact" if body["rank_exact"] else "approximate")
```

```json theme={null}
{
  "record": {
    "holder": "CCSXXPCXICXOC2MLU3UINWYKL6M5VNLVOTHA4ZQ5S5BEDZGRWDTP52CG",
    "asset": "BTC:GAGVZMODPYFTULIVSY3RGFADIC5LF4LLSN37N4MFWGNZ57TYBXL2LQQX",
    "kind": "contract", "balance": "1344.2235021", "balance_raw": "13442235021",
    "source": "ledger_entry", "verified": true, "authorized": true
  },
  "rank": 1,
  "rank_exact": true,
  "holder_count": 55,
  "read_view": {"network": "testnet", "as_of_ledger": 5331479}
}
```

For XLM (4.3 million Testnet holders) the rank of `$ACCOUNT` comes from the
periodic refresh:
`{"rank": 2, "rank_exact": false, "rank_as_of": 5331295, "holder_count": 4339661}`.

### Rank rules

| Case | Answer |
| - | - |
| Asset with up to 100,000 holders | `200`, exact `rank` (1 is the largest), `rank_exact: true` |
| Larger asset | `200`, `rank` from a periodic refresh, `rank_exact: false`, and `rank_as_of` (the ledger it was computed at) |
| Larger asset whose rank is still being computed | `200` at once with `rank: null`, a `rank_note` and a `Retry-After` header (15 seconds). Not charged; retry after the header |
| Address with a zero-balance trustline | `200` with `rank: null` and the zero-balance `record` |
| Address with no trustline or balance entry for the asset | `404 not_found` |

## Credits

Testnet requests are free (they count toward the daily Testnet cap of 10,000).
On Mainnet:

| Request | Credits |
| - | - |
| Holders page | 5 per started 100 records (`limit=100` on a full page: 5) |
| Holders with `as_of_ledger` | 5 per started 100 records |
| One holder's rank | 2 (0 when `rank_note` is present) |

## Errors

| Status | `code` | What to do |
| - | - | - |
| `400` | `invalid_request` | Fix the asset ID, address, parameter or cursor |
| `401` | `unauthorized` | Check the key and the host's network |
| `403` | `forbidden` | Use a secret key; publishable keys cannot read holders |
| `404` | `not_found` | Unknown asset, or the address has no entry for it |
| `409`, `501` | `history_unavailable`, `not_implemented` | Only with `as_of_ledger`; see [snapshots](/assets-api/snapshots.md) |
| `429` | `rate_limited` | Wait `Retry-After`. Over the daily Testnet cap it counts down to 00:00 UTC |
| `503` | `busy`, `index_not_ready`, `query_timeout` | Retry after `Retry-After`; for `query_timeout` lower `limit` first, never repeat it unchanged |

See [errors](/guides/errors.md) for the body and retry rules.

## Next

* [Historical snapshots](/assets-api/snapshots.md): holders at a past ledger, or for an airdrop list.
* [Token registry](/assets-api/registry.md): supply, flags and metadata of the token.
* [Rate limits](/guides/rate-limits.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.