# Build a Telegram token scanner bot for Robinhood Chain

> Build a Telegram bot that scans Robinhood Chain tokens: price, liquidity, holders, taxes and a security score from two Crius API calls, with real examples.

Source: https://docs.crius.sh/guides/build-a-telegram-token-scanner/ · Updated 2026-10-28

Short answer

When a message contains a token address, call `GET /v1/robinhood/token/{address}` (price, market cap, liquidity, holders, launchpad) and `GET /v1/robinhood/token/{address}/security` (score, risk, flags, taxes, honeypot) in parallel and reply with a summary; for a name, call `/search` first and let the user pick. Two credits per scan, and both calls work without a key.

[GET `/token/{address}`](https://docs.crius.sh/reference/tokens/token-profile/) [GET `/token/{address}/security`](https://docs.crius.sh/reference/security/security-checks/) [GET `/search`](https://docs.crius.sh/reference/discovery/search/)

A Telegram token scanner is a bot that answers “is this token OK?” in a chat: someone pastes a Robinhood Chain token address, and the bot replies with its price, market cap, liquidity, holders, launchpad, taxes and a security verdict. With the Crius API that’s two calls per scan, the [token profile](https://docs.crius.sh/reference/tokens/token-profile/) and [security checks](https://docs.crius.sh/reference/security/security-checks/), plus [search](https://docs.crius.sh/reference/discovery/search/) when someone types a name instead of an address. All three are on the anonymous plan, so the bot runs without a key until it gets busy.

## Architecture

1.  **Telegram side.** Long-poll `getUpdates` from the Telegram Bot API (no webhook or server needed) and reply with `sendMessage`. Create the bot with @BotFather to get its token.
    
2.  **Parse the message.** An address (`0x` and 40 hex characters) is scanned directly. A word after `/scan` is a name: search for it and reply with the matches.
    
3.  **Scan.** Call the token profile and security in parallel, then format one message. Cache results for a minute so ten people pasting the same token in a group cost one scan.
    
4.  **Optional feed.** A second process listens to `launches:all` on the stream and posts new tokens that pass the scan to a channel.
    

Endpoints used: [GET`/token/{address}`](https://docs.crius.sh/reference/tokens/token-profile/) [GET`/token/{address}/security`](https://docs.crius.sh/reference/security/security-checks/) [GET`/search`](https://docs.crius.sh/reference/discovery/search/)

## Names are not unique: search, then pick

Example [Anonymous · no key · 1 credit](https://docs.crius.sh/reference/discovery/search/)

### Three tokens called Housecoin

A user types /scan housecoin. Which token do they mean?

GET/v1/robinhood/search?q=housecoin&limit=3

  

**cURL**

```bash
curl "https://api.crius.sh/v1/robinhood/search?q=housecoin&limit=3"
```

**JavaScript**

```js
const res = await fetch("https://api.crius.sh/v1/robinhood/search?q=housecoin&limit=3");
const { data, meta } = await res.json();
```

**Python**

```python
import requests

r = requests.get(
    "https://api.crius.sh/v1/robinhood/search?q=housecoin&limit=3",
)
body = r.json()
print(body["data"])
```

**Real response 200 Captured Oct 28, 2026 · 73 lines (arrays trimmed)**

```json
{
  "data": [
    {
      "address": "0x578dfaf61257ea702b8834bb037740a6368947bd",
      "label": null,
      "launchpad": null,
      "launchpad_name": null,
      "links": {
        "discord": null,
        "farcaster": null,
        "github": null,
        "telegram": null,
        "twitter": null,
        "website": null
      },
      "liquidity_usd": 22978.7,
      "logo": null,
      "name": "Housecoin",
      "price_usd": 0.0000846805,
      "symbol": "HOUSECOIN",
      "type": "token"
    },
    {
      "address": "0x34c21f10ea7ec052ed878d157f40fb4160730b24",
      "label": null,
      "launchpad": "pons",
      "launchpad_name": "Pons",
      "links": {
        "discord": null,
        "farcaster": null,
        "github": null,
        "telegram": null,
        "twitter": "https://x.com/aryonserwin/status/2093942789784637480",
        "website": "https://fs.blog/knowledge-project-podcast/vlad-tenev/?utm_source=chatgpt.com"
      },
      "liquidity_usd": 4978.47,
      "logo": "https://api.crius.sh/v1/robinhood/logo/0x34c21f10ea7ec052ed878d157f40fb4160730b24.png?size=128",
      "name": "Housecoin",
      "price_usd": 0.00000374137,
      "symbol": "housecoin",
      "type": "token"
    },
    {
      "address": "0xc19b982b971642aa6cc5346f5a15cd0ccf4626cc",
      "label": null,
      "launchpad": "pons",
      "launchpad_name": "Pons",
      "links": {
        "discord": null,
        "farcaster": null,
        "github": null,
        "telegram": null,
        "twitter": "https://x.com/coinbase/status/2093710406695125093",
        "website": "https://www.ponsfamily.com/launchpad/0x992f10d2c751ec875d6a2834a70f730d030a25f9"
      },
      "liquidity_usd": 3876.33,
      "logo": "https://api.crius.sh/v1/robinhood/logo/0xc19b982b971642aa6cc5346f5a15cd0ccf4626cc.png?size=128",
      "name": "Housecoin",
      "price_usd": 0.00000218996,
      "symbol": "Housecoin",
      "type": "token"
    }
  ],
  "meta": {
    "block": 100410183,
    "block_age_ms": 555,
    "block_ts": 1793225424000,
    "chain": "robinhood",
    "finalized_block": 100401329,
    "lag_ms": 9,
    "safe_block": 100405028
  }
}
```

What this tells you

-   Three different tokens share the name: `0x578dfaf61257ea702b8834bb037740a6368947bd` (no launchpad, `liquidity_usd` 22,978.7), and two Pons tokens, `0x34c21f10ea7ec052ed878d157f40fb4160730b24` (4,978.47) and `0xc19b982b971642aa6cc5346f5a15cd0ccf4626cc` (3,876.33).
-   Their `symbol`s differ only in case (`HOUSECOIN`, `housecoin`, `Housecoin`), and their links point to unrelated pages. A bot that scans “the first result” would be wrong as often as right.
-   Reply with every match: name, `launchpad_name`, liquidity and the full address, and let the user choose. `type=token` restricts search to tokens; `limit` goes up to 20.

[Search reference](https://docs.crius.sh/reference/discovery/search/) Runs from your browser on the anonymous plan [Data looks wrong? Report it](mailto:support@crius.sh?subject=Wrong%20data%20in%20docs%20example%3A%20Three%20tokens%20called%20Housecoin&body=Page%3A%20https%3A%2F%2Fdocs.crius.sh%2Fguides%2Fbuild-a-telegram-token-scanner%2F%23search-names%0ARequest%3A%20GET%20%2Fv1%2Frobinhood%2Fsearch%3Fq%3Dhousecoin%26limit%3D3%0A%0AWhat's%20wrong%20or%20missing%3F%0A%0A)

## The scan: profile plus security

Example [Anonymous · no key · 1 credit](https://docs.crius.sh/reference/security/security-checks/)

### Housecoin's security: unlocked liquidity in one wallet

The user picks 0x578dfaf61257ea702b8834bb037740a6368947bd. What should the bot warn about?

GET/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd/security

  

**cURL**

```bash
curl "https://api.crius.sh/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd/security"
```

**JavaScript**

```js
const res = await fetch("https://api.crius.sh/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd/security");
const { data, meta } = await res.json();
```

**Python**

```python
import requests

r = requests.get(
    "https://api.crius.sh/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd/security",
)
body = r.json()
print(body["data"])
```

**Real response 200 Captured Oct 28, 2026 · 119 lines (arrays trimmed)**

```json
{
  "data": {
    "contract": {
      "blacklist": false,
      "can_change_limits": false,
      "can_change_tax": false,
      "cooldown": false,
      "implementation": null,
      "is_proxy": false,
      "mintable": false,
      "owner": null,
      "ownership": "none",
      "pausable": false,
      "proxy_kind": null,
      "self_destruct": false,
      "trading_switch": false,
      "upgradeable": false,
      "verified": "none"
    },
    "flags": [
      "lp_unlocked",
      "unverified",
      "lp_single_wallet"
    ],
    "honeypot": {
      "is_honeypot": false,
      "reason": null,
      "selective": false
    },
    "hook": null,
    "known_asset": false,
    "launch": {
      "flags": [
        "lp_single_wallet"
      ],
      "lp_top_wallet": {
        "address": "0x52c6e90a0928df4d6e4a6164ee0c7851144b1454",
        "liquidity_usd": 23239.6,
        "pct": 100
      },
      "sniper_supply_pct": 0.0000145583,
      "sniper_top_wallet_pct": 0.0000145583,
      "sniper_wallets": 1,
      "sniper_window_blocks": 10,
      "uniform_holders": null,
      "wallet_farm": null
    },
    "limits": {
      "max_tx": "1000000000",
      "max_tx_getter": "maxTxAmount()",
      "max_tx_pct": 100,
      "max_wallet": "1000000000",
      "max_wallet_getter": "maxWalletAmount()",
      "max_wallet_pct": 100
    },
    "liquidity": {
      "status": "unlocked",
      "summary": {
        "burned_pct": 0,
        "locked_pct": 0,
        "next_unlock_ts": null,
        "protocol_fee_pct": 0,
        "unlocked_in_wallets_pct": 100,
        "weighting": "by pool liquidity_usd"
      }
    },
    "primary_dex": "uniswap_v4",
    "risk": "medium",
    "score": 50,
    "tax": {
      "buy_pct": 0,
      "confidence": "high",
      "realized": {
        "buy_pct": 0,
        "by_wallet_class": {
          "dev_sell_pct": null,
          "others_sell_pct": 0
        },
        "last_sample_ts": 1793225424000,
        "max_sell_pct_seen": 0,
        "samples": {
          "buys": 53,
          "sells": 48
        },
        "sell_pct": 0,
        "window": "30d"
      },
      "sell_pct": 0,
      "simulated": null,
      "source": "realized",
      "transfer_pct": null
    },
    "tax_history": [
      {
        "from_block": 100408848,
        "pct": 0,
        "side": "buy"
      },
      {
        "from_block": 100408910,
        "pct": 0,
        "side": "sell"
      }
    ],
    "tax_history_total": 2,
    "tax_mode": "static",
    "token": "0x578dfaf61257ea702b8834bb037740a6368947bd"
  },
  "meta": {
    "block": 100410199,
    "block_age_ms": 1166,
    "block_ts": 1793225425000,
    "chain": "robinhood",
    "finalized_block": 100401329,
    "lag_ms": 97,
    "safe_block": 100405028,
    "simulation": "no V2 WETH pair: realized tax only"
  }
}
```

What this tells you

-   `score: 50`, `risk: "medium"`, `flags`: `lp_unlocked`, `unverified`, `lp_single_wallet`.
-   `launch.lp_top_wallet` is `0x52c6e90a0928df4d6e4a6164ee0c7851144b1454` with `pct: 100` of the liquidity (`liquidity_usd` 23,239.6), and `liquidity.status` is `unlocked`: one wallet can pull the whole pool. That’s the line to put first in the reply.
-   No tax: `tax.buy_pct` and `tax.sell_pct` are 0 over 53 buys and 48 sells (`tax.realized.samples`, `confidence: "high"`), and `honeypot.is_honeypot` is false.
-   `meta.simulation` says `"no V2 WETH pair: realized tax only"`: the taxes come from real trades, not a simulation.
-   `contract.ownership: "none"`, with no mint, pause or blacklist: the contract itself isn’t the risk here.

[Security checks reference](https://docs.crius.sh/reference/security/security-checks/) Runs from your browser on the anonymous plan [Data looks wrong? Report it](mailto:support@crius.sh?subject=Wrong%20data%20in%20docs%20example%3A%20Housecoin's%20security%3A%20unlocked%20liquidity%20in%20one%20wallet&body=Page%3A%20https%3A%2F%2Fdocs.crius.sh%2Fguides%2Fbuild-a-telegram-token-scanner%2F%23scan-security%0ARequest%3A%20GET%20%2Fv1%2Frobinhood%2Ftoken%2F0x578dfaf61257ea702b8834bb037740a6368947bd%2Fsecurity%0A%0AWhat's%20wrong%20or%20missing%3F%0A%0A)

## Minutes later: the pool is empty

The same token, scanned again twelve minutes after the security check above. This is what the `lp_single_wallet` warning was about.

Example [Anonymous · no key · 1 credit](https://docs.crius.sh/reference/tokens/token-profile/)

### Housecoin after its liquidity was pulled

Someone pastes 0x578dfaf61257ea702b8834bb037740a6368947bd again. What does the profile show now?

GET/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd

  

**cURL**

```bash
curl "https://api.crius.sh/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd"
```

**JavaScript**

```js
const res = await fetch("https://api.crius.sh/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd");
const { data, meta } = await res.json();
```

**Python**

```python
import requests

r = requests.get(
    "https://api.crius.sh/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd",
)
body = r.json()
print(body["data"])
```

**Real response 200 Captured Oct 28, 2026 · 267 lines (arrays trimmed)**

```json
{
  "data": {
    "address": "0x578dfaf61257ea702b8834bb037740a6368947bd",
    "admin_flags": null,
    "bundles": {
      "see": "/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd/bundles"
    },
    "burned": 771605584,
    "chain_id": 4663,
    "circulating_supply": 228394416,
    "creation": {
      "attribution": "unattributed_factory",
      "block": 100408825,
      "deployed_via": "factory",
      "deployer": "0x52c6e90a0928df4d6e4a6164ee0c7851144b1454",
      "factory": "0x1cbaf24d53fe930fce8eff149fa797d2611da149",
      "source": "trace",
      "tx": "0xba98b7479b2bf5896021262176c4c92f149df0750f93a75b2fcc83869d10d51a"
    },
    "decimals": 18,
    "dexscreener": {
      "status": "checking"
    },
    "first_seen_block": 100408825,
    "history": "complete",
    "holders": {
      "burned_pct": 77.1606,
      "count": 166,
      "dev_holding": null,
      "dev_pct": null,
      "locked_pct": 0,
      "top10_pct": 16.4376
    },
    "launch": null,
    "liquidity": {
      "see": "/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd/liquidity",
      "status": "no_liquidity",
      "summary": {
        "burned_pct": null,
        "locked_pct": null,
        "next_unlock_ts": null,
        "protocol_fee_pct": null,
        "unlocked_in_wallets_pct": null,
        "weighting": "by pool liquidity_usd"
      }
    },
    "locked": 0,
    "market": {
      "fdv_usd": null,
      "liquidity_usd": 0,
      "market_cap_usd": null,
      "pools_count": 1,
      "quote_liquidity_usd": 0
    },
    "metadata": {
      "banner": null,
      "description": null,
      "links": {
        "discord": null,
        "farcaster": null,
        "github": null,
        "telegram": null,
        "twitter": null,
        "website": null
      },
      "logo": null,
      "logo_original": null,
      "socials": {
        "discord": null,
        "farcaster": null,
        "github": null,
        "telegram": null,
        "twitter": null,
        "website": null
      },
      "source": null,
      "sources": {
        "links": null,
        "logo": null
      }
    },
    "name": "Housecoin",
    "price": {
      "ath_market_cap_usd": 21314.9,
      "ath_scope": "all_time",
      "ath_ts": 1793225465000,
      "ath_usd": 0.0000933247,
      "change_pct": {
        "1h": 271.103,
        "1m": 0,
        "1s": 0,
        "24h": 271.103,
        "30s": 0,
        "4h": 271.103,
        "5m": 0,
        "6h": 271.103
      },
      "confidence": null,
      "dex": null,
      "eth": null,
      "liquidity_usd": null,
      "quote": null,
      "quote_liquidity_usd": null,
      "source_pool": null,
      "thin_liquidity": null,
      "usd": null
    },
    "security": {
      "see": "/v1/robinhood/token/0x578dfaf61257ea702b8834bb037740a6368947bd/security"
    },
    "standard": "erc20",
    "stats": {
      "all_time": {
        "buy_volume_usd": 17761,
        "buys": 70,
        "first_trade_ts": 1793225291000,
        "sell_volume_usd": 11036.9,
        "sells": 62,
        "since": "chain_start",
        "trades": 132,
        "volume_usd": 28797.9
      },
      "avg_trade_usd": {
        "1h": 218.166,
        "1m": null,
        "1s": null,
        "24h": 218.166,
        "30s": null,
        "4h": 218.166,
        "5m": null,
        "6h": 218.166
      },
      "buy_volume_usd": {
        "1h": 17761,
        "1m": 0,
        "1s": 0,
        "24h": 17761,
        "30s": 0,
        "4h": 17761,
        "5m": 0,
        "6h": 17761
      },
      "buyers": {
        "1h": 52,
        "1m": 0,
        "1s": 0,
        "24h": 52,
        "30s": 0,
        "4h": 52,
        "5m": 0,
        "6h": 52
      },
      "buys": {
        "1h": 70,
        "1m": 0,
        "1s": 0,
        "24h": 70,
        "30s": 0,
        "4h": 70,
        "5m": 0,
        "6h": 70
      },
      "last_trade_ts": 1793225465000,
      "sell_volume_usd": {
        "1h": 11036.9,
        "1m": 0,
        "1s": 0,
        "24h": 11036.9,
        "30s": 0,
        "4h": 11036.9,
        "5m": 0,
        "6h": 11036.9
      },
      "sellers": {
        "1h": 31,
        "1m": 0,
        "1s": 0,
        "24h": 31,
        "30s": 0,
        "4h": 31,
        "5m": 0,
        "6h": 31
      },
      "sells": {
        "1h": 62,
        "1m": 0,
        "1s": 0,
        "24h": 62,
        "30s": 0,
        "4h": 62,
        "5m": 0,
        "6h": 62
      },
      "traders": {
        "1h": 52,
        "1m": 0,
        "1s": 0,
        "24h": 52,
        "30s": 0,
        "4h": 52,
        "5m": 0,
        "6h": 52
      },
      "trades": {
        "1h": 132,
        "1m": 0,
        "1s": 0,
        "24h": 132,
        "30s": 0,
        "4h": 132,
        "5m": 0,
        "6h": 132
      },
      "txns": {
        "1h": 132,
        "1m": 0,
        "1s": 0,
        "24h": 132,
        "30s": 0,
        "4h": 132,
        "5m": 0,
        "6h": 132
      },
      "volume_eth": {
        "1h": 12.2259,
        "1m": 0,
        "1s": 0,
        "24h": 12.2259,
        "30s": 0,
        "4h": 12.2259,
        "5m": 0,
        "6h": 12.2259
      },
      "volume_usd": {
        "1h": 28797.9,
        "1m": 0,
        "1s": 0,
        "24h": 28797.9,
        "30s": 0,
        "4h": 28797.9,
        "5m": 0,
        "6h": 28797.9
      }
    },
    "symbol": "HOUSECOIN",
    "total_supply": 1000000000,
    "type": "generic",
    "ui_multiplier": null,
    "verification": {
      "compiler": null,
      "contract_name": null,
      "implementation": null,
      "is_proxy": false,
      "sourcify": "none"
    }
  },
  "meta": {
    "block": 100417059,
    "block_age_ms": 1310,
    "block_ts": 1793226138000,
    "chain": "robinhood",
    "finalized_block": 100408671,
    "lag_ms": 100,
    "pending": [],
    "safe_block": 100412483
  }
}
```

What this tells you

-   `market.liquidity_usd` is 0, `price.usd` is `null` and `liquidity.status` is `no_liquidity`: the token’s one pool (`market.pools_count: 1`) has nothing left to trade against.
-   No trade happened after the all-time high: `price.ath_ts` and `stats.last_trade_ts` are both 1793225465000 (22:11:05 UTC), less than a minute after the security check.
-   `creation.deployer` is `0x52c6e90a0928df4d6e4a6164ee0c7851144b1454`, the same wallet the security check reported as holding 100% of the liquidity (`launch.lp_top_wallet`).
-   `holders.count` is still 166: the holders keep their tokens, but there’s no market to sell them in. `creation.attribution: "unattributed_factory"` and `launch: null`: no launchpad, so no launch locker either.

[Token profile reference](https://docs.crius.sh/reference/tokens/token-profile/) Runs from your browser on the anonymous plan [Data looks wrong? Report it](mailto:support@crius.sh?subject=Wrong%20data%20in%20docs%20example%3A%20Housecoin%20after%20its%20liquidity%20was%20pulled&body=Page%3A%20https%3A%2F%2Fdocs.crius.sh%2Fguides%2Fbuild-a-telegram-token-scanner%2F%23after-pull%0ARequest%3A%20GET%20%2Fv1%2Frobinhood%2Ftoken%2F0x578dfaf61257ea702b8834bb037740a6368947bd%0A%0AWhat's%20wrong%20or%20missing%3F%0A%0A)

## The bot

Node.js 22+, no dependencies. Set `TELEGRAM_TOKEN` from @BotFather; `CRIUS_API_KEY` is optional.

```js
// scanner-bot.mjs: TELEGRAM_TOKEN=... node scanner-bot.mjs
const TG = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}`;
const API = "https://api.crius.sh/v1/robinhood";
const HEADERS = process.env.CRIUS_API_KEY ? { "x-api-key": process.env.CRIUS_API_KEY } : {};
const cache = new Map(); // address -> { at, text }

const esc = (s) => String(s ?? "").replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c]); // names are user-made
const usd = (n) => (n == null ? "?" : n >= 1000 ? `$${Math.round(n).toLocaleString("en-US")}` : `$${Number(n).toPrecision(4)}`);
async function get(path, tries = 3) {
  const res = await fetch(`${API}${path}`, { headers: HEADERS });
  if (res.status === 429 && tries > 1) {
    await new Promise((r) => setTimeout(r, Number(res.headers.get("retry-after") ?? 1) * 1000)); // rate limited: wait, retry
    return get(path, tries - 1);
  }
  return (await res.json()).data;
}

async function scan(address) {
  const hit = cache.get(address);
  if (hit && Date.now() - hit.at < 60_000) return hit.text;
  const [t, s] = await Promise.all([get(`/token/${address}`), get(`/token/${address}/security`)]);
  if (!t) return "Token not found on Robinhood Chain.";
  const warn = { critical: "DANGER: honeypot or scam", high: "HIGH RISK", medium: "Medium risk", low: "Low risk" }[s?.risk] ?? "Risk unknown";
  const lines = [
    `<b>${esc(t.name)}</b> (${esc(t.symbol)})`,
    `<code>${address}</code>`,
    `Price ${usd(t.price?.usd)} · 24h ${t.price?.change_pct?.["24h"]?.toFixed(1) ?? "?"}%`,
    `Market cap ${usd(t.market?.market_cap_usd)} · Liquidity ${usd(t.market?.liquidity_usd)}`,
    `Holders ${t.holders?.count ?? "?"} · Top 10 hold ${t.holders?.top10_pct?.toFixed(1) ?? "?"}%`,
    t.launch ? `Launchpad ${esc(t.launch.launchpad_name)} · ${t.launch.status}` : "No launchpad",
    "",
    `${warn} · score ${s?.score ?? "?"}/100`,
    s?.flags?.includes("lp_single_wallet") ? "One wallet holds the unlocked liquidity and can pull it at any time." : null,
    `Tax buy ${s?.tax?.buy_pct ?? "?"}% · sell ${s?.tax?.sell_pct ?? "?"}% · LP ${s?.liquidity?.status ?? "?"}`,
    s?.flags?.length ? `Flags: ${s.flags.join(", ")}` : "No flags",
  ];
  const text = lines.filter((l) => l != null).join("\n");
  cache.set(address, { at: Date.now(), text });
  return text;
}

async function search(q) {
  const hits = (await get(`/search?q=${encodeURIComponent(q)}&type=token&limit=5`)) ?? [];
  if (!hits.length) return "No token with that name.";
  return ["Which one? Send the address:", ...hits.map((h) => `${esc(h.name)} (${esc(h.symbol)}) · ${esc(h.launchpad_name ?? "no launchpad")} · liq ${usd(h.liquidity_usd)}\n<code>${h.address}</code>`)].join("\n\n");
}

async function reply(chatId, text) {
  await fetch(`${TG}/sendMessage`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ chat_id: chatId, text, parse_mode: "HTML", link_preview_options: { is_disabled: true } }),
  });
}

let offset = 0;
for (;;) {
  try {
    const { result = [] } = await (await fetch(`${TG}/getUpdates?timeout=30&offset=${offset}`)).json();
    for (const u of result) {
      offset = u.update_id + 1;
      const text = u.message?.text ?? "";
      const addr = text.match(/0x[0-9a-fA-F]{40}/)?.[0]?.toLowerCase();
      const name = text.match(/^\/scan(?:@\w+)?\s+(.+)/)?.[1];
      if (addr) await reply(u.message.chat.id, await scan(addr));
      else if (name) await reply(u.message.chat.id, await search(name.trim()));
    }
  } catch (e) {
    console.error(e);
    await new Promise((r) => setTimeout(r, 3000));
  }
}
```

## What it costs

| Operation | Calls | Credits |
| --- | --- | --- |
| Scan an address | `/token/{address}` + `/token/{address}/security` | 2 |
| Look up a name | `/search` | 1 |
| Repeat scan within a minute | cached | 0 |
| New-launch feed | 1 stream connection + 2 per posted token | 1 + 2 each |

Without a key, scans are limited to 2 requests per second per IP, which a small group won’t reach. With a Free key, 1,000,000 credits cover about 500,000 scans a month. See [Plans & limits](https://docs.crius.sh/plans-and-limits/).

Escape token names

Token names and symbols are written by whoever deployed the contract. Escape them before sending with `parse_mode: "HTML"`, as `esc()` does, or a name containing `<` breaks the message.

## Frequently asked questions

**Do I need a Crius API key for a Telegram scanner?**

Not to start. Token profile, security and search are on the anonymous plan at 2 requests per second per IP. A busy group needs more: a Free key gives 10 requests per second and 1,000,000 credits a month, about 500,000 scans.

**How do I stop users scanning the wrong token with the same name?**

Treat names as a search, never as an identity. `/search` returns every token with that name or symbol; show each one's address, launchpad and `liquidity_usd`, and scan only after the user sends or taps an address.

**Can the bot post new launches to a channel automatically?**

Yes. Subscribe to `launches:all` on the [WebSocket stream](https://docs.crius.sh/reference/stream/) (Free key with the `stream` scope), wait a minute, run the same scan and post tokens that pass. The [new tokens guide](https://docs.crius.sh/guides/stream-new-tokens/) has the stream client.

**Does this work for Discord too?**

The Crius part is the same: two calls per scan. Only the chat side changes: replace `getUpdates` and `sendMessage` with your Discord library's message events and replies.

## Related

[Use case **Telegram & Discord bots** Build a Robinhood Chain token scanner, buy bot or launch-alert channel for Telegram or Discord with the Crius API, with real replies and credit costs. Read](https://docs.crius.sh/use-cases/telegram-and-discord-bots/) [Guide **Token security score** Score any Robinhood Chain token from 0 to 100 with one API call. Honeypot, buy and sell taxes, owner powers, LP locks, v4 hook risk and insider launches, with real examples. Read](https://docs.crius.sh/guides/token-security-score/) [Guide **Search tokens** Find a Robinhood Chain token's address by name or symbol, or look up any address or Uniswap v4 pool id, with one search call. Real responses included. Read](https://docs.crius.sh/guides/search-tokens/) [Guide **Get token data** Get a Robinhood Chain token's name, supply, price, market cap, holders, launchpad, liquidity lock and links in one API call. Real response, no API key needed. Read](https://docs.crius.sh/guides/get-token-data/) [Guide **Stream new tokens** Get new Robinhood Chain launchpad tokens, graduations and pools the moment they happen, with the Crius WebSocket and a Node.js client that checks each token. Read](https://docs.crius.sh/guides/stream-new-tokens/)
