Find holders who held through a run (holder cohorts)
Find wallets that bought a Robinhood Chain token early and held through the run, with hold time, entry market cap, sells and PnL per wallet. Real example.
Call GET /v1/robinhood/token/{address}/holders/cohort?from=…&to=… (Starter plan, 10 credits). Set a size threshold (min_usd, min_balance or min_pct), continuous=true to require holding for the whole window and reset_on to choose what breaks it. Each wallet comes back with hold_time_s, entry_mcap_usd, sells_in_window, resets and pnl_usd.
A holder cohort is the set of wallets that held a token through a window, for example from the moment the market cap crossed $50k until now. Crius replays every balance change in the window and, for each wallet, tells you when it started holding, how long it held, the market cap when it got in, whether it sold, and its PnL. It’s how you find “diamond hands” (wallets that bought early and didn’t sell into the pump), and the opposite: who took profit and when.
Get the cohort in one call
Who held CHARGE from $50k market cap to now
Charge AI (0x7b8892cea949bc60d494681d1bc3ea96071ab0b8) first crossed $50k market cap two hours after launch. Which wallets held from that moment until now?
curl "https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/holders/cohort?from=mcap:50000&to=now&limit=5" \ -H "x-api-key: $CRIUS_API_KEY"const res = await fetch("https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/holders/cohort?from=mcap:50000&to=now&limit=5", { headers: { "x-api-key": process.env.CRIUS_API_KEY },});const { data, meta } = await res.json();import os, requests
r = requests.get( "https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/holders/cohort?from=mcap:50000&to=now&limit=5", headers={"x-api-key": os.environ["CRIUS_API_KEY"]},)body = r.json()print(body["data"])Real response 200 Captured Oct 28, 2026 · 125 lines (arrays trimmed)
{ "data": { "criteria": { "continuous": false, "exclude": [], "min_balance": null, "min_hold_s": null, "min_pct": null, "min_usd": null, "reset_on": "sell" }, "holders": [ { "address": "0xfee5fee50000062bd986e86502c5acdf989067a0", "balance_at_from": 5758.56, "balance_now": 47534.9, "entry_mcap_usd": 51583.8, "hold_time_s": 1234652, "min_balance_in_window": 5758.56, "pnl_usd": null, "qualified_since_block": 88461359, "qualified_since_ts": 1791990426900, "reset_history": [], "resets": 0, "sells_in_window": 0, "tags": [ "contract" ], "value_usd_now": 0.183244 }, { "address": "0xb823bd7738b61547221f54bb3410b94326aae1eb", "balance_at_from": 497321, "balance_now": 497321, "entry_mcap_usd": 51583.8, "hold_time_s": 1234652, "min_balance_in_window": 497321, "pnl_usd": -22.1233, "qualified_since_block": 88461359, "qualified_since_ts": 1791990426900, "reset_history": [], "resets": 0, "sells_in_window": 0, "tags": [], "value_usd_now": 1.91714 }, { "address": "0x38c16e94e79383a5906449fc84477c1a7b19b613", "balance_at_from": 8344519, "balance_now": 0.00000000214748, "entry_mcap_usd": 51583.8, "hold_time_s": 1234652, "min_balance_in_window": 0.00000000214748, "pnl_usd": -298.108, "qualified_since_block": 88461359, "qualified_since_ts": 1791990426900, "reset_history": [], "resets": 0, "sells_in_window": 0, "tags": [], "value_usd_now": 0.00000000000000827841 }, { "address": "0x8e56f6476d1361cdc6e75798c83354e70d033f23", "balance_at_from": 57.0322, "balance_now": 57.0322, "entry_mcap_usd": 51583.8, "hold_time_s": 1234652, "min_balance_in_window": 57.0322, "pnl_usd": -0.00219735, "qualified_since_block": 88461359, "qualified_since_ts": 1791990426900, "reset_history": [], "resets": 0, "sells_in_window": 0, "tags": [ "smart_account" ], "value_usd_now": 0.000219856 }, { "address": "0xfd2a475d7ce02ec077867d72177063b5c2ab01b6", "balance_at_from": 429.912, "balance_now": 429.912, "entry_mcap_usd": 51583.8, "hold_time_s": 1234652, "min_balance_in_window": 429.912, "pnl_usd": -0.396922, "qualified_since_block": 88461359, "qualified_since_ts": 1791990426900, "reset_history": [], "resets": 0, "sells_in_window": 0, "tags": [], "value_usd_now": 0.00165728 } ], "never_reached_threshold": 0, "qualified": 224, "reset_out": 942, "started_qualified": 229, "window": { "from": { "at": "mcap:50000", "block": 88461359, "ts": 1791990426900 }, "to": { "at": "now", "block": 100406815, "ts": 1793225079000 } } }, "meta": { "block": 100406816, "block_age_ms": 1088, "block_ts": 1793225079000, "chain": "robinhood", "finalized_block": 100397649, "lag_ms": 86, "next_cursor": "5", "safe_block": 100401329 }}What this tells you
window.fromresolvedmcap:50000to block 88461359 atts: 1791990426900(2026-10-14 15:07 UTC);window.toisnow, block 100406815.started_qualified: 229wallets held at the $50k crossing,qualified: 224qualify at the end of the window, andreset_out: 942held at some point in the window but no longer qualify.- The five listed wallets all have
qualified_since_block: 88461359,resets: 0andsells_in_window: 0: they held from the crossing to the end,hold_time_s: 1234652(about 14.3 days).entry_mcap_usd: 51583.8is the market cap at that block’s price. 0xb823bd7738b61547221f54bb3410b94326aae1ebnever touched its position (balance_at_fromandbalance_nowboth 497321) and sits atpnl_usd: -22.1233: holding through the run doesn’t mean profit when the token later fell.0x38c16e94e79383a5906449fc84477c1a7b19b613went from 8,344,519 tokens tobalance_now: 0.00000000214748withsells_in_window: 0. It moved its tokens out by transfer, which the defaultreset_on: "sell"doesn’t count, and with no threshold (criteria.min_usd: null) a dust balance still qualifies. A real query should setmin_usd.0xfee5fee50000062bd986e86502c5acdf989067a0is taggedcontractand haspnl_usd: null: it never bought or sold the token on a DEX, so there is no cost to measure against.
How a wallet qualifies
A wallet qualifies while its balance is above your threshold. Choose one:
min_usd: the balance’s value at each block’s price, for examplemin_usd=50.min_balance: a token amount.min_pct: a share of supply.
Without any of them, any non-zero balance qualifies, as in the example.
A reset breaks a wallet’s holding streak. reset_on decides what counts:
reset_on | A wallet resets when |
|---|---|
sell (default) | it sells on a DEX or launchpad, or drops below the threshold |
any_decrease | its balance goes down for any reason (sell or transfer out), or it drops below the threshold |
below_threshold | only when it drops below the threshold |
none | only when its balance reaches zero |
After a reset, a wallet that still qualifies starts a new streak (its qualified_since_block moves to the reset, and reset_history records it). With continuous=true, any reset, or not qualifying at from, removes it from the result and counts it in reset_out.
Other filters: min_hold_s (a minimum hold time), entered_before (a point: only wallets that first qualified before it), exclude (infra, dev, snipers) and sort (hold_time, the default, longest first; balance; entry_mcap). from and to take the same points as snapshots: block:N, ts:MS, mcap:USD, or the labels launch, graduation, ath, now (see historical holders).
What the fields mean
| Field | Meaning |
|---|---|
qualified | Wallets that qualify at the end of the window (with continuous=true: held the whole window). |
started_qualified | Wallets that qualified at from. |
reset_out | Wallets that qualified at some point in the window but not at the end, or broke continuity. |
never_reached_threshold | Wallets that held in the window but never above the threshold. |
holders[].qualified_since_block, qualified_since_ts | Start of the wallet's current streak. |
holders[].hold_time_s | Seconds from the streak's start to to. |
holders[].entry_mcap_usd | Market cap at the streak's start. |
holders[].balance_at_from, balance_now, min_balance_in_window | Balance at from, at to, and the lowest in between. |
holders[].sells_in_window, resets, reset_history | Sells in the window, the number of resets and when they happened (block, time, reason, balance after). |
holders[].value_usd_now, pnl_usd | Value at to, and sells plus current value minus buys. null when the wallet never traded the token. |
criteria | The definition used, echoed back. |
Use it in your app
-
Define a diamond hand for your users. A typical one: at least $50 (
min_usd=50), held since a point (from=mcap:10000), never sold (continuous=true&reset_on=sell), not the dev or a sniper (exclude=infra,dev,snipers). -
Show the evidence.
entry_mcap_usdandhold_time_sare what readers trust: “in since a $10k market cap, held 14 days”. Addpnl_usdwhen it’s known. -
Use the counters as a conviction score.
qualifiedagainststarted_qualifiedsays how many early holders stayed. In the example, more wallets lost qualification during the window (942) than held at its start (229). -
Get a Starter key. The cohort endpoint needs the Starter plan and costs 10 credits per call; cache results per window, since a closed window never changes.
// Wallets that held at least $50 of CHARGE from a $10k market cap to now, without selling.const token = "0x7b8892cea949bc60d494681d1bc3ea96071ab0b8";const q = new URLSearchParams({ from: "mcap:10000", to: "now", min_usd: "50", continuous: "true", reset_on: "sell", exclude: "infra,dev,snipers", limit: "50",});const res = await fetch(`https://api.crius.sh/v1/robinhood/token/${token}/holders/cohort?${q}`, { headers: { "x-api-key": process.env.CRIUS_API_KEY },});const { data } = await res.json();
console.log(`${data.qualified} of ${data.started_qualified} early holders never sold`);for (const h of data.holders) { const days = (h.hold_time_s / 86400).toFixed(1); console.log(h.address, `in at $${h.entry_mcap_usd} mcap`, `${days} d`, `PnL $${h.pnl_usd ?? "?"}`);}Frequently asked questions
How do I find diamond-hand holders of a token?
Ask for the cohort over the run with a strict definition: /holders/cohort?from=mcap:10000&to=now&min_usd=50&continuous=true&reset_on=sell&exclude=infra. Only wallets that held at least $50 at the start and never sold or dropped below $50 until now come back, sorted by how long they held.
What is the difference between `continuous=true` and the default?
By default a wallet qualifies if it meets the threshold at the end of the window, even if it joined late or sold and bought back (its hold time then starts at its last reset). With continuous=true it must already qualify at from and never reset until to; everyone else is counted in reset_out.
Does a transfer to another wallet count as selling?
Not with the default reset_on=sell: only sells on a DEX or launchpad reset a holder. Use reset_on=any_decrease to also break on transfers out, or a min_usd threshold so a wallet that moved its tokens away drops out (below_threshold).
How do I find early buyers who are still holding?
Use entered_before: from=launch&to=now&entered_before=mcap:20000 keeps only wallets that first qualified before the market cap crossed $20k. Add exclude=snipers,dev to leave out the launch snipers and the creator.
Which plan does the cohort endpoint need?
The Starter plan or higher, 10 credits per call. See Plans & limits.
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