Add safety checks to a Robinhood Chain trading bot
Keep a Robinhood Chain trading bot away from honeypots, high taxes and pullable liquidity: pre-trade checks with Crius, then live tax and LP alerts.
Before each buy, call GET /v1/robinhood/token/{address}/security (skip risk: "critical", honeypots and taxes above your limit; read flags for unlocked or single-wallet liquidity) and GET /v1/robinhood/token/{address}/price (skip thin_liquidity and trades large against quote_liquidity_usd). That's 2 credits per check, anonymous plan. While you hold, subscribe to tax: and lp: for the token on the WebSocket stream.
A trading bot on Robinhood Chain needs to answer four questions before it buys: can it sell again, what will the round trip cost in taxes, can the liquidity disappear while it holds, and is the price real enough for the size it wants to trade. Crius answers the first three in one security call and the fourth in one price call, both on the anonymous plan. While the bot holds a position, the WebSocket stream tells it when the token’s tax changes or liquidity is removed.
Check 1: can the bot sell, and at what tax?
A honeypot with 0% realized tax
Let's End AI (0xc882f10529a5518ee3393af5a80e9ff00d38d44b) shows no tax on past trades. Should the bot buy?
curl "https://api.crius.sh/v1/robinhood/token/0xc882f10529a5518ee3393af5a80e9ff00d38d44b/security"const res = await fetch("https://api.crius.sh/v1/robinhood/token/0xc882f10529a5518ee3393af5a80e9ff00d38d44b/security");const { data, meta } = await res.json();import requests
r = requests.get( "https://api.crius.sh/v1/robinhood/token/0xc882f10529a5518ee3393af5a80e9ff00d38d44b/security",)body = r.json()print(body["data"])Real response 200 Captured Oct 28, 2026 · 140 lines (arrays trimmed)
{ "data": { "contract": { "blacklist": false, "can_change_limits": false, "can_change_tax": false, "cooldown": false, "implementation": null, "is_proxy": false, "mintable": false, "owner": "0x0000000000000000000000000000000000000000", "ownership": "renounced", "pausable": false, "proxy_kind": null, "self_destruct": false, "trading_switch": true, "upgradeable": false, "verified": "none" }, "flags": [ "honeypot", "unverified", "uniform_holders" ], "honeypot": { "is_honeypot": true, "reason": "simulated sell failed", "selective": false }, "hook": null, "known_asset": false, "launch": { "flags": [ "uniform_holders" ], "lp_top_wallet": { "address": "0xabe3a51937eb4c76ab6449e9823dbffd4409776f", "liquidity_usd": 8.58651, "pct": 0.0253312 }, "sniper_supply_pct": 0.000299049, "sniper_top_wallet_pct": 0.000299049, "sniper_wallets": 1, "sniper_window_blocks": 10, "uniform_holders": { "balance_pct": 0.112731, "holders": 51, "of_top": 100 }, "wallet_farm": null }, "limits": { "max_tx": "1000000000", "max_tx_getter": "maxTxAmount()", "max_tx_pct": 100, "max_wallet": "1000000000", "max_wallet_getter": "maxWalletSize()", "max_wallet_pct": 100 }, "liquidity": { "status": "burned", "summary": { "burned_pct": 99.9715, "locked_pct": 0, "next_unlock_ts": null, "protocol_fee_pct": 0, "unlocked_in_wallets_pct": 0.0284848, "weighting": "by pool liquidity_usd" } }, "primary_dex": "uniswap_v2", "risk": "critical", "score": 0, "tax": { "buy_pct": 0, "confidence": "high", "realized": { "buy_pct": 0, "by_wallet_class": { "dev_sell_pct": null, "others_sell_pct": 0 }, "last_sample_ts": 1793224820000, "max_sell_pct_seen": 0, "samples": { "buys": 27197, "sells": 3423 }, "sell_pct": 0, "window": "30d" }, "sell_pct": 0, "simulated": { "amount_eth": 0.042454083286938696, "buy_gas": 159144, "buy_pct": 0, "can_buy": true, "can_sell": false, "can_transfer": true, "from": "eoa", "revert_reason": { "code": 3, "data": "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000245472616e7366657248656c7065723a205452414e534645525f46524f4d5f4641494c454400000000000000000000000000000000000000000000000000000000", "message": "execution reverted: TransferHelper: TRANSFER_FROM_FAILED" }, "router": "0x89e5db8b5aa49aa85ac63f691524311aeb649eba", "sell_gas": 229228, "sell_pct": null, "transfer_pct": 0 }, "source": "realized", "transfer_pct": 0 }, "tax_history": [ { "from_block": 62239227, "pct": 0, "side": "buy" }, { "from_block": 62240181, "pct": 0, "side": "sell" } ], "tax_history_total": 2, "tax_mode": "static", "token": "0xc882f10529a5518ee3393af5a80e9ff00d38d44b" }, "meta": { "block": 100406838, "block_age_ms": 1257, "block_ts": 1793225081000, "chain": "robinhood", "finalized_block": 100397649, "lag_ms": 14, "safe_block": 100401329, "simulation": "eth_simulateV1 via Uniswap V2 router at head" }}What this tells you
risk: "critical",score: 0,honeypot.is_honeypot: truewithreason: "simulated sell failed". The bot must not buy.- The trap is invisible in the tax numbers:
tax.buy_pctandtax.sell_pctare 0 andtax.realized.samplescounts 27,197 buys and 3,423 sells. A bot that only checks realized tax would buy this token. tax.simulatedshows what happened at the head block:can_buy: true,can_transfer: true,can_sell: false, and the revert message"execution reverted: TransferHelper: TRANSFER_FROM_FAILED".meta.simulationnames the method:"eth_simulateV1 via Uniswap V2 router at head".
Check 2: can the liquidity disappear?
A $1 billion market cap backed by $3,795 in one wallet's hands
MJDQ (0x124597e2d8bbe472825c4918a36de246ed0c92f7) ranks near the top by market cap. Is it safe to hold?
curl "https://api.crius.sh/v1/robinhood/token/0x124597e2d8bbe472825c4918a36de246ed0c92f7/security"const res = await fetch("https://api.crius.sh/v1/robinhood/token/0x124597e2d8bbe472825c4918a36de246ed0c92f7/security");const { data, meta } = await res.json();import requests
r = requests.get( "https://api.crius.sh/v1/robinhood/token/0x124597e2d8bbe472825c4918a36de246ed0c92f7/security",)body = r.json()print(body["data"])Real response 200 Captured Oct 28, 2026 · 111 lines (arrays trimmed)
{ "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": "match" }, "flags": [ "lp_unlocked", "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": "0xe591376415858cae27c81c0813bbaef3a0a46d6a", "liquidity_usd": 3795.38, "pct": 100 }, "sniper_supply_pct": 0.000000291623, "sniper_top_wallet_pct": 0.000000291623, "sniper_wallets": 1, "sniper_window_blocks": 10, "uniform_holders": null, "wallet_farm": null }, "limits": {}, "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": 55, "tax": { "buy_pct": 0, "confidence": "medium", "realized": { "buy_pct": 0, "by_wallet_class": { "dev_sell_pct": null, "others_sell_pct": 0 }, "last_sample_ts": 1792087347000, "max_sell_pct_seen": 0, "samples": { "buys": 6, "sells": 4 }, "sell_pct": 0, "window": "30d" }, "sell_pct": 0, "simulated": null, "source": "realized", "transfer_pct": null }, "tax_history": [ { "from_block": 89408576, "pct": 0, "side": "buy" }, { "from_block": 89421448, "pct": 0, "side": "sell" } ], "tax_history_total": 2, "tax_mode": "static", "token": "0x124597e2d8bbe472825c4918a36de246ed0c92f7" }, "meta": { "block": 100409718, "block_age_ms": 765, "block_ts": 1793225377000, "chain": "robinhood", "finalized_block": 100401329, "lag_ms": 48, "safe_block": 100405028, "simulation": "no V2 WETH pair: realized tax only" }}What this tells you
score: 55,risk: "medium",flags:lp_unlockedandlp_single_wallet. No honeypot, no tax, and the source is verified (contract.verified: "match").launch.lp_top_walletis0xe591376415858cae27c81c0813bbaef3a0a46d6awithpct: 100andliquidity_usd: 3795.38: one wallet owns the whole pool and can withdraw it at any time (liquidity.summary.unlocked_in_wallets_pct: 100).- The tax is measured from only 6 buys and 4 sells (
tax.realized.samples), sotax.confidenceismedium, andtax.simulatedisnull(meta.simulation:"no V2 WETH pair: realized tax only"). - The token list shows this token with a market cap of about $1 billion and 4 holders. Size trades by liquidity, never by market cap. The Telegram scanner guide shows a token with the same two flags whose pool was emptied a minute after its check.
The rules
| Check | Field | Suggested rule |
|---|---|---|
| Sellable | honeypot.is_honeypot, risk | Never buy critical or a honeypot; treat honeypot.selective (only some wallets can sell) the same |
| Taxes | tax.buy_pct, tax.sell_pct, tax.confidence | Skip above your round-trip budget; require high confidence or a fresh simulation for size |
| Launch | flags: insider_launch, wallet_farm | Skip: insiders sniped most of the supply, or the holders are a farm of wallets funded together |
| Liquidity | flags: lp_unlocked, lp_single_wallet; liquidity.summary | Skip or cut size when one wallet can pull the pool |
| Hook | flags: hook_critical_risk, hook_high_risk | Skip: the pool's v4 hook can block swaps, has no code, or its code family has a record of rugs or honeypots |
| Price | thin_liquidity, confidence, quote_liquidity_usd | Skip thin or low-confidence prices; keep the trade a small share of the quote side |
The thresholds are yours to choose. The fields are the same for every token, so the rules stay a few lines of code.
Use it in your bot
-
Check before every buy. Call security and price in parallel (2 credits). Cache the result for a minute or two per token: a bot that fires several orders into the same token doesn’t need a new check for each.
-
Re-simulate before large trades. On Starter and above,
?fresh=1&amount_usd=<size>runs a new buy, transfer and sell simulation at your trade size (20 credits,simulatescope). -
Watch what you hold. Subscribe to
tax:<token>andlp:<token>on the stream for each open position. A tax change arrives asside,from,to; a liquidity removal astype: "remove_liquidity"with the pool,providerand amounts. -
Go deeper when it matters. Liquidity & locks (5 credits, Free) shows each LP position and unlock date; snipers & bundles (10 credits, Starter) shows who bought the launch.
// safety.mjs: pre-trade checks and position watch for a Robinhood Chain bot (Node.js 22+)const API = "https://api.crius.sh/v1/robinhood";const KEY = process.env.CRIUS_API_KEY;const HEADERS = KEY ? { "x-api-key": KEY } : {};const MAX_TAX_PCT = 5;const MAX_SHARE_OF_QUOTE = 0.01; // trade at most 1% of the pool's quote sideconst BLOCK_FLAGS = ["insider_launch", "wallet_farm", "hook_critical_risk", "hook_high_risk", "lp_single_wallet"];
const get = async (path) => (await (await fetch(`${API}${path}`, { headers: HEADERS })).json()).data;
export async function preTradeCheck(token, sizeUsd) { const [s, p] = await Promise.all([get(`/token/${token}/security`), get(`/token/${token}/price`)]); const no = []; if (!s || !p) return { ok: false, reasons: ["no data for this token"] }; if (s.risk === "critical" || s.honeypot?.is_honeypot || s.honeypot?.selective) no.push(`can't sell: ${s.honeypot?.reason ?? s.risk}`); if ((s.tax?.buy_pct ?? 0) + (s.tax?.sell_pct ?? 0) > MAX_TAX_PCT) no.push(`round-trip tax ${s.tax.buy_pct}% + ${s.tax.sell_pct}%`); for (const f of s.flags ?? []) if (BLOCK_FLAGS.includes(f)) no.push(`flag ${f}`); if (p.thin_liquidity || p.confidence === "low") no.push(`unreliable price (${p.confidence})`); if (sizeUsd > (p.quote_liquidity_usd ?? 0) * MAX_SHARE_OF_QUOTE) no.push(`$${sizeUsd} is too large for $${p.quote_liquidity_usd} of quote liquidity`); return { ok: no.length === 0, reasons: no, score: s.score, priceUsd: p.usd };}
// Live alerts for open positions: tax changes and liquidity removals.export function watchPositions(tokens, onAlert) { const ws = new WebSocket(`wss://api.crius.sh/v1/robinhood/stream?api_key=${KEY}`); let idle; const touch = () => { clearTimeout(idle); idle = setTimeout(() => ws.close(), 45_000); }; ws.onopen = () => { touch(); ws.send(JSON.stringify({ op: "subscribe", channels: tokens.flatMap((t) => [`tax:${t}`, `lp:${t}`]) })); }; ws.onmessage = (e) => { touch(); const m = JSON.parse(e.data); if (m.data?.removed) return; if (m.ch.startsWith("tax:")) onAlert({ token: m.data.token, kind: "tax", text: `${m.data.side} tax ${m.data.from}% → ${m.data.to}%` }); if (m.ch.startsWith("lp:") && m.data.type === "remove_liquidity") { onAlert({ token: m.ch.slice(3), kind: "lp", text: `liquidity removed from ${m.data.pool} by ${m.data.provider}` }); } }; ws.onclose = () => { clearTimeout(idle); setTimeout(() => watchPositions(tokens, onAlert), 2000 + Math.random() * 1000); }; ws.onerror = () => ws.close();}
// Example: check before buying $200 of a token, then watch it.const token = "0x124597e2d8bbe472825c4918a36de246ed0c92f7";const verdict = await preTradeCheck(token, 200);console.log(verdict.ok ? "OK to buy" : `skip: ${verdict.reasons.join("; ")}`);if (verdict.ok && KEY) watchPositions([token], (a) => console.log("ALERT", a));Run against the MJDQ token above, it prints skip: flag lp_single_wallet; $200 is too large for $1897.69 of quote liquidity (the quote side is half of the $3,795.38 pool). Either rule alone keeps the bot out; against the honeypot it stops at can't sell: simulated sell failed.
Frequently asked questions
Is the realized tax enough to know a token can be sold?
No. Realized tax describes trades that already happened. A contract can stop sells later, and the honeypot sample below shows 0% realized tax on a token whose sell simulation now reverts. Gate on honeypot.is_honeypot and risk as well as on tax.
How fresh is the security check my bot reads?
Realized taxes update with every block; contract checks and the sell simulation are cached per token. meta.block tells you the block the answer reflects. On Starter and above, ?fresh=1 re-runs the simulation on demand (20 credits, key with the simulate scope) and amount_usd sets its size.
What does a pre-trade check cost?
Security and price are 1 credit each, so 2 credits per token checked; without a key both work at 2 requests per second. Optional deeper checks: liquidity and locks 5 credits (Free plan), bundles and snipers 10 (Starter), a fresh simulation 20 (Starter).
How do I know when a token I hold changes its tax?
Subscribe to tax:<token> on the WebSocket stream. When the realized buy or sell tax moves by a whole percent, you get side, from and to (percent) and can exit before the next trade pays it.
Related
Get your free Crius API key
Every example on this page works with a free key: higher rate limits than the anonymous plan, holders, wallets and the WebSocket stream. No card needed.
- Robinhood Chain (4663) only, from our own archive nodes
- REST + WebSocket
- Pay on-chain or by card