Build a Robinhood Chain Token Screener with Node.js
Build a Robinhood Chain token screener in Node.js: rank by volume or momentum, filter on liquidity, holders and flow, then check tax, honeypots and LP.
Crius 7 min read
Short answer: pull ranked candidates from GET /v1/robinhood/tokens (trending, top volume, gainers, new, graduating) with server-side liquidity and volume filters, apply your own rules on the activity fields every row carries, then run GET /token/{address}/security on the shortlist to drop honeypots, taxed tokens and pullable liquidity. In our test this narrowed 200 tokens to 14 tradeable candidates in 13 seconds.
This guide builds that screener step by step in Node.js 20+ with no dependencies, explains the fields it filters on and shows where screeners usually go wrong. Every number below came from a live run in October 2026.
The three stages
/tokens?list=… server-side: ranking, min liquidity, min volume, launchpad, quote │ 200 rows, each with price, liquidity, volume, holders, traders, buys/sells, change ▼your rules client-side: holder count, flow, momentum, age │ 15 shortlisted ▼/token/{address}/security per token, 2 at a time: honeypot, tax, owner powers, LP locks │ ▼14 candidatesRanking and coarse filtering happen on the server, where every token’s statistics are already in memory. Your rules run on the rows you get back. Only the shortlist gets the security check, because that endpoint does real work per token.
Stage 1: candidates
curl "https://api.crius.sh/v1/robinhood/tokens?list=top_volume&window=24h&min_liquidity_usd=25000&limit=100" \ -H "x-api-key: $CRIUS_API_KEY"| Parameter | Values |
|---|---|
list |
trending (default), top_volume, top_gainers, top_losers, new, bonding, graduating, graduated |
window |
1s, 30s, 1m, 5m, 1h, 4h, 6h, 24h (ranking window for trending, volume and movers) |
min_liquidity_usd, min_volume_usd |
server-side floors |
launchpad |
e.g. pons, flap |
quote |
eth, usdg, … |
created_after_ts |
Unix ms, for “new in the last hour” screens |
limit, cursor |
up to 100 per page; meta.next_cursor for the next page |
include_flagged |
include tokens flagged as scams or hidden by moderation (excluded by default) |
trending scores tokens by unique traders weighted by the logarithm of volume, so a token with many small traders outranks one whale churning volume. graduating lists bonding-curve tokens past 50% of their curve, closest to graduation first.
Each row carries what a screener needs, with every activity field broken down by window:
{ "address": "0xc9675fe679c7d4e1b94416f66d61c96127eb964a", "symbol": "GIGATENEV", "price_usd": 0.00119234, "market_cap_usd": 1192338, "liquidity_usd": 146654, "holders": 10219, "volume_usd": { "5m": 3961, "1h": 55243.7, "6h": 334474, "24h": 1240296 }, "change_pct": { "5m": 2.54, "1h": 10.51, "6h": 7.31, "24h": 8.37 }, "traders": { "1h": 384, "24h": 6057 }, "buyers": { "24h": 4508 }, "sellers": { "24h": 4713 }, "buys": { "24h": 10644 }, "sells": { "24h": 10332 }, "launchpad": null, "progress_pct": null, "quote": "ETH", "created_ts": 1791568845600}Stage 2: your rules
const API = "https://api.crius.sh/v1/robinhood";const KEY = process.env.CRIUS_API_KEY;
async function get(path) { for (let i = 0; i < 5; i++) { const r = await fetch(`${API}${path}`, { headers: { "x-api-key": KEY } }); if (r.status === 429 || r.status === 503) { // rate limited or busy: wait as told await new Promise((s) => setTimeout(s, Number(r.headers.get("retry-after") ?? 2) * 1000)); continue; } if (!r.ok) throw new Error(`${path}: ${r.status}`); return r.json(); } throw new Error(`${path}: retries exhausted`);}
// Two pages of candidates.const rows = [];let cursor = "";for (let page = 0; page < 2; page++) { const r = await get(`/tokens?list=top_volume&window=24h&min_liquidity_usd=25000&limit=100${cursor ? `&cursor=${cursor}` : ""}`); rows.push(...r.data); if (!r.meta.next_cursor) break; cursor = r.meta.next_cursor;}
const shortlist = rows .filter((t) => t.holders >= 200) // not a handful of wallets .filter((t) => t.traders["24h"] >= 50) // real participation .filter((t) => t.buyers["24h"] / Math.max(1, t.sellers["24h"]) >= 0.8) // not a one-way exit .filter((t) => (t.change_pct["24h"] ?? 0) > -40) // not collapsing .slice(0, 15);Why these rules:
- Holders and traders separate tokens with an audience from tokens a few wallets trade back and forth. Volume alone is easy to inflate; distinct traders are much harder.
- Buyer-to-seller ratio catches distribution: when far more wallets are selling than buying, the price is usually being held up by a few large buys.
- A momentum floor keeps tokens in free fall out of a “top volume” list, where panic selling ranks them highly.
Stage 3: security on the shortlist
const results = [];for (let i = 0; i < shortlist.length; i += 2) { // two at a time: security is a heavy endpoint const batch = await Promise.all(shortlist.slice(i, i + 2).map((t) => get(`/token/${t.address}/security`))); batch.forEach((s, j) => results.push({ t: shortlist[i + j], s: s.data }));}
const passed = results.filter(({ s }) => !s.honeypot?.is_honeypot && (s.tax?.sell_pct ?? 0) <= 5 && s.risk !== "high");
console.table(passed.slice(0, 8).map(({ t, s }) => ({ symbol: t.symbol, price_usd: t.price_usd, vol_24h: Math.round(t.volume_usd["24h"]), liq: Math.round(t.liquidity_usd), holders: t.holders, chg_24h: t.change_pct["24h"]?.toFixed(1), risk: s.risk, sell_tax: s.tax?.sell_pct, flags: (s.flags ?? []).join(" "),})));The security response is detailed; these are the fields a screener should act on:
| Field | Meaning |
|---|---|
honeypot.is_honeypot |
Sells revert or are blocked. Never list these. |
tax.buy_pct, tax.sell_pct |
Tax measured from real trades (tax.source: "realized") or simulated when there aren’t enough trades yet. |
contract.mintable, pausable, blacklist, can_change_tax |
Powers the owner still holds. |
liquidity.status |
burned, locked or unlocked, with percentages per pool. |
flags |
Short codes: lp_unlocked, hook_can_block_swaps, unverified, ownership_active … |
risk, score |
An overall rating and a 0–100 score. |
Our run, trimmed:
200 candidates → 15 shortlisted → 14 passed securitysymbol price_usd vol_24h liq holders chg_24h risk sell_tax flagsUHI 0.0275856 16301698 508832 8445 -14.5 low 0AI 0.0551436 8869592 2429155 57727 -32.0 low 0 ownership_activeSPY 762.602 6121669 13356096 70614 -0.7 low null mintable pausable upgradeable_proxyCRCL 84.7201 3817375 3625793 12702 -2.5 low null mintable pausable upgradeable_proxyHMCAST 0.00000452166 2714979 160185 1339 1743.3 medium 0 lp_unlocked hook_can_block_swaps unverifiedCONDUCTOR 0.0000017233 2608844 68742 814 1546.3 medium 0 lp_unlocked unverifiedTwo things stand out:
- Tokenized stocks carry “scary” flags legitimately. SPY and CRCL are mintable, pausable and upgradeable because their issuer must be able to mint against custody, freeze for compliance and upgrade. A screener for memecoins should treat these flags as red; a screener that includes stocks should whitelist known issuers (the
known_assetfield marks them) rather than exclude them. hook_can_block_swapsmatters on Uniswap v4. A v4 pool’s hook can be written to block sells. Treat that flag as a reason for manual review, not an automatic pass.
Common screener mistakes
Trusting volume alone. Volume is the easiest number to inflate. Pair every volume filter with distinct traders and holders, as Stage 2 does; a token with $1 million of volume and 30 traders is a different thing from one with $1 million and 3,000.
Filtering on liquidity that can leave. liquidity_usd says how much is in the pools now, not whether it can be pulled. Check liquidity.status in the security response: unlocked liquidity in one wallet can disappear in a single transaction.
Judging a new token on 24-hour windows. A token launched two hours ago has no meaningful 24-hour change. For new tokens, rank on 5m and 1h windows and use created_after_ts to keep the list to recent launches.
Ignoring the quote asset. A token quoted in another volatile token inherits that token’s moves. The quote field tells you what each row trades against; most screeners do best restricting to ETH, WETH and USDG quotes.
Hammering heavy endpoints. Running security checks for every candidate in parallel gets you 503 responses. Shortlist first, check two or three at a time, and cache results.
Running it on a schedule
Run the screener every few minutes and diff the results: tokens entering the list are your alerts. Two practical points:
- Cache security results. A token’s contract powers rarely change; its tax and LP status can. Re-check a token at most every 10–15 minutes, and immediately when the
tax:<token>orlp:<token>stream reports a change. - Use the stream for “new”. For a new-token screener, subscribe to
launches:allandpools:newon the WebSocket API instead of pollinglist=new, then run stages 2 and 3 on each arrival.
Adding depth with batch calls
To show more columns without a request per token, use POST /tokens/batch with up to 100 addresses and a fields list:
curl -X POST "https://api.crius.sh/v1/robinhood/tokens/batch" \ -H "x-api-key: $CRIUS_API_KEY" -H "content-type: application/json" \ -d '{"tokens":["0xc9675fe679c7d4e1b94416f66d61c96127eb964a"],"fields":["symbol","price.usd","holders.count","holders.top10_pct","market.pools_count"]}'The full profile includes holder concentration (holders.top10_pct), creation details, launchpad data and all-time statistics. Security isn’t inlined in batch responses; it points to the security endpoint, which you call for the shortlist only.
Frequently asked questions
How much does a screener run cost?
Two /tokens pages are 2 credits and 15 security checks are 15 credits, so 17 credits per run. Every 5 minutes, that’s about 4,900 credits a day.
Can I screen tokens still on a bonding curve?
Yes: use list=bonding or list=graduating and filter on progress_pct. Bonding-curve tokens have no DEX pool yet, so judge them on holders, traders and the creator’s history.
Why are some tokens missing from the list?
Tokens flagged as scams or hidden by moderation are excluded by default; pass include_flagged=true to see them. Tokens with no trades in the ranking window don’t appear in trending or movers lists.
Does the screener work without an API key?
The /tokens lists and /security work anonymously at 2 requests per second, enough for experiments. A free key raises that to 10 per second with 1,000,000 credits a month.
Next steps
- API reference: discovery and search and security and liquidity.
- Product overview: token security and honeypot checks, token prices, launchpads.
- Related guides: honeypot and tax detection and building a trading terminal.
Questions go to support@crius.sh.
Build with Crius
Robinhood Chain data in one API
Free plan with 1M credits a month. Your key in under a minute.
More from the blog
- TutorialNode.js
How to Connect an LLM to Robinhood Chain Data
Three working ways to give a language model live Robinhood Chain data: an MCP server, a tool-calling loop for any model API, and retrieval over the Crius docs.
- Uniswap v4Real-time
How to Detect New Uniswap v4 Pools on Robinhood Chain
Detect every new Uniswap v4 pool on Robinhood Chain as it is created: the pools:new stream, raw PoolManager Initialize logs, and hook checks before you trade.
- TutorialHistorical data
How to Get All Robinhood Chain Transactions Without Running a Node
Export every trade, transfer and liquidity event for a Robinhood Chain token or wallet with an indexed API. Tested Python and Node.js, no archive node.