Telegram Bot

/holders

`/holders` replies with the live holder count of one token, configured per bot from the dashboard. It answers in a group, in a group-admin chat, and in a…

/holders

/holders replies with the live holder count of one token, configured per bot from the dashboard. It answers in a group, in a group-admin chat, and in a DM with the bot — only the Telegram command menu is scoped by chat type.

Setup

Turn /holders on, in order

  1. Open Settings → Features in the dashboard — the page itself is titled Bot Utilities.
  2. Find the /holders card and turn on its toggle.
  3. With no contract address saved yet, the toggle opens the Configure Holders modal instead of switching the card on directly.
  4. Pick the token's chain from the Blockchain dropdown — 8 chains, listed below.
  5. Enter the token's contract address under Contract Address. The field's placeholder text changes with the chosen chain.
  6. Tap Enable /holders — disabled until the address matches the selected chain's address format.
  7. Send /holders in the chat.

Once an address is saved, a pencil button on the /holders card reopens the same modal, titled Edit Holders Config in this mode, to change the chain or address without turning the feature off first.

Chains and holder sources

/holders counts on 8 of the 9 chains the bot otherwise recognises for contract addresses (ca.CA_SUPPORTED). Each chain races its own list of free, keyless sources at once; the first to answer, in the order below, wins.

The 8 chains, in holders.py's SOURCES order

ChainHolder-count sources, in orderAddress placeholder
EthereumGoPlus → Blockscout → CoinGecko0x… token contract
BNB ChainGoPlus → CoinGecko0x… token contract
BaseGoPlus → Blockscout → CoinGecko0x… token contract
PulseChainBlockscout0x… token contract
Robinhood ChainCoinGecko → GoPlus0x… token contract
SolanaGoPlus → Jupiter → CoinGeckoToken mint address
TONtonapi → CoinGeckoJetton master EQ…
SuiGoPlus → CoinGecko0x…::module::COIN

HyperEVM:

The dashboard's Blockchain dropdown never lists it, and /holders cannot be pointed at a HyperEVM token.

PulseChain has no CoinGecko fallback

Every other chain above falls back to CoinGecko when its free sources don't answer. PulseChain has no coingecko entry in SOURCES — Blockscout is its only source, and that fallback is never attempted.

All of a chain's configured sources are queried at once. The first source in preference order to answer within 2 seconds wins; if none has by then, the reply waits for whichever source answers first, up to 6 seconds total (PREFER_WAIT_S / HARD_WAIT_S, holders.py).

Address validation

The Contract Address field is checked against the selected chain's address family before Enable /holders or Save Changes unlocks, and re-checked on the server when the dashboard saves it.

Address shape per chain family

FamilyChainsShape
EVMEthereum, BNB Chain, Base, PulseChain, Robinhood Chain0x + 40 hex characters
SolanaSolanaa base58 public key (32 bytes decoded)
TONTONEQ or UQ + 46 characters
SuiSuia coin type, 0x<hex>::module::Type — not a plain address

Validation error text

ConditionMessage
No chain picked yetPick the token's chain first
Address fits a different chain's familyThat looks like {an EVM|a Solana|a TON|a Sui} address, not {ChainName}
Address fits no known familyNot a {ChainName} token address

The dashboard's save endpoint re-checks the address on the way in — 256 characters or fewer, matching [a-zA-Z0-9._:-]+ — and rejects any chain outside the 8 above with Unsupported chain. Supported: ethereum, bsc, base, pulsechain, robinhood, solana, ton, sui.

The reply

Every /holders reply is sent as a reply to the triggering message, with notifications suppressed.

Every outcome

CaseReply
Configured; a source answers📊 Holders: 12,483
Configured; every live source fails but a cached count exists📊 Holders: 12,483 (cached)
Enabled, but the chain/address pair fails validation📊 Holders not configured yet.
Enabled; every source fails and nothing is cached📊 Holder data temporarily unavailable. Try again later.
Feature toggle offNo reply — silent

Holders: is sent bold (parse_mode=HTML); a cached answer appends (cached) in italics after the number. Both failure texts above are plain, unformatted strings, and both also delete the /holders command message a short time later — the code does not fix that delay at one number, so none is given here.

A resolved count is cached 15 seconds per chain-and-address pair and single-flighted: overlapping /holders sends inside that window share one fetch instead of re-querying every source.

When /holders is visible

A disabled /holders answers nothing

With the toggle off, sending /holders produces no reply and no error — the message is consumed rather than left for anything else to answer.

/holders is added to Telegram's own command menu only once it is both enabled and pointed at a chain-and-address pair that passes validation. It's added to the group menu and the group-admin menu (all_group_chats, all_chat_administrators); it is never added to the private-chat menu (all_private_chats), including the owner's own DM.

It still works in a DM

The route itself runs at scope any — every chat type. Sending /holders directly in a DM with the bot answers exactly as it does in a group; only the menu suggestion is scoped to groups and admins.

Copyright © 2026