Read a token's bubble map and holder clusters
Get a Robinhood Chain token's top holders as a graph, with the funding and transfer links that group them. Real examples, with and without clusters.
Call GET /v1/robinhood/token/{address}/bubblemap?top=100 (Starter plan, 10 credits). nodes are the top holders with their share (pct), USD value and tags; edges are funding, shared funders and token transfers between them; clusters group linked holders with their total pct, members and the links that join them. Pools, lockers, hooks and burn addresses are listed under hidden_infra, not drawn.
A bubble map shows a token’s largest holders as bubbles and draws lines between the ones that are connected, so you can see at a glance whether “the top 20 holders” are 20 people or one person with 20 wallets. Crius builds it from on-chain funding and token transfers on Robinhood Chain. It only links wallets on evidence that means something (the wallet that first funded them, transfers between them) and leaves out pools, lockers and exchanges, which touch everyone.
Get the map in one call
Two clusters among Rebot's top holders
Rebot (0x192b126efea71ca5e7767bc6fc53038d869fbbba) was an insider launch. Are any of its top 100 holders today the same person?
curl "https://api.crius.sh/v1/robinhood/token/0x192b126efea71ca5e7767bc6fc53038d869fbbba/bubblemap?top=100" \ -H "x-api-key: $CRIUS_API_KEY"const res = await fetch("https://api.crius.sh/v1/robinhood/token/0x192b126efea71ca5e7767bc6fc53038d869fbbba/bubblemap?top=100", { 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/0x192b126efea71ca5e7767bc6fc53038d869fbbba/bubblemap?top=100", headers={"x-api-key": os.environ["CRIUS_API_KEY"]},)body = r.json()print(body["data"])Real response 200 Captured Oct 28, 2026 · 227 lines (arrays trimmed)
{ "data": { "block": null, "clusters": [ { "id": "c_80b9b545", "includes_dev": false, "links": { "funding_between": 0, "shared_funders": [ { "address": "0x795834684cdec507ea9ee240fdb7e908567dd316", "wallets": 2 } ], "token_transfers": 0 }, "members": [ "0xdebabd206e5a73dd81a9211533a1bd610c006d20", "0xf0fd2e7718ea59a6ddfe884c7e4714ae2dd5610a" ], "pct": 3.60847, "risk": "medium", "root_funder": "0x795834684cdec507ea9ee240fdb7e908567dd316", "wallets": 2 }, { "id": "c_377c5b", "includes_dev": false, "links": { "funding_between": 1, "shared_funders": [], "token_transfers": 0 }, "members": [ "0x4eec10e9c115a7ea67b59eea59c3a3c777fab236", "0xa0686afb77ed32fbb402a39642d05158b9fa4c96" ], "pct": 2.56134, "risk": "low", "root_funder": "0xa0686afb77ed32fbb402a39642d05158b9fa4c96", "wallets": 2 } ], "edges": [ { "block": 78435467, "eth": 1.2, "from": "0x795834684cdec507ea9ee240fdb7e908567dd316", "hub": false, "to": "0xdebabd206e5a73dd81a9211533a1bd610c006d20", "type": "common_funder" }, { "block": 9427558, "eth": 2, "from": "0xa0686afb77ed32fbb402a39642d05158b9fa4c96", "hub": false, "to": "0x4eec10e9c115a7ea67b59eea59c3a3c777fab236", "type": "funding" }, { "block": 94538219, "eth": 2.5, "from": "0x795834684cdec507ea9ee240fdb7e908567dd316", "hub": false, "to": "0xf0fd2e7718ea59a6ddfe884c7e4714ae2dd5610a", "type": "common_funder" } ], "hidden_infra": [ { "address": "0x8366a39cc670b4001a1121b8f6a443a643e40951", "label": "uniswap_v4_pool_manager", "pct": 70.8524, "tags": [ "pool" ] }, { "address": "0x267444d099b10fb5ed7c3cc7b7c767adca574952", "label": "pons_v2_launch_locker", "pct": 8.16327, "tags": [ "lp_locker" ] }, { "address": "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044", "label": "pons_v2_meme_hook", "pct": 0.000056807, "tags": [ "launchpad" ] }, { "address": "0xf69cd0fe7280773bb9b7c93ab68420d07290afa9", "label": null, "pct": 0.00000391777, "tags": [ "contract" ] }, { "address": "0x8cc5b884c33a261fe6aa6499bc7315d5aed57c67", "label": null, "pct": 0.0000000000000000000000002, "tags": [ "contract" ] }, { "address": "0x8f10b468b06c6fd214b65f87778827f7d113f996", "label": null, "pct": 0.0000000000000000000000001, "tags": [ "contract" ] }, { "address": "0xe6eeda550918638529c6cf2ea14f9ea0572d6f88", "label": null, "pct": 0.0000000000000000000000001, "tags": [ "contract" ] } ], "nodes": [ { "cluster": "c_80b9b545", "id": "0xdebabd206e5a73dd81a9211533a1bd610c006d20", "label": null, "pct": 2.19541, "tags": [ "smart_account", "fomo", "whale" ], "value_usd": 88.6343 }, { "cluster": null, "id": "0xb6815093cf0108d146aee2e98e53c17287c42393", "label": null, "pct": 1.86, "tags": [ "whale" ], "value_usd": 75.0931 }, { "cluster": null, "id": "0xc3403905b4e0ffaf973663ad261639087091a5da", "label": null, "pct": 1.68482, "tags": [ "smart_account", "fomo", "whale" ], "value_usd": 68.0207 }, { "cluster": "c_377c5b", "id": "0x4eec10e9c115a7ea67b59eea59c3a3c777fab236", "label": null, "pct": 1.46153, "tags": [ "whale" ], "value_usd": 59.0056 }, { "cluster": "c_80b9b545", "id": "0xf0fd2e7718ea59a6ddfe884c7e4714ae2dd5610a", "label": null, "pct": 1.41306, "tags": [ "smart_account", "whale" ], "value_usd": 57.049 }, { "cluster": "c_377c5b", "id": "0xa0686afb77ed32fbb402a39642d05158b9fa4c96", "label": null, "pct": 1.09981, "tags": [ "whale" ], "value_usd": 44.4023 }, { "cluster": null, "id": "0x1795eacd1a377133002de3bca335336a19dfa2ca", "label": null, "pct": 1.03781, "tags": [ "whale" ], "value_usd": 41.8992 }, { "cluster": null, "id": "0xa2bfbc13abbf678ed8c743e662ba43fbe18159ea", "label": null, "pct": 1, "tags": [ "whale" ], "value_usd": 40.3726 } ], "token": "0x192b126efea71ca5e7767bc6fc53038d869fbbba" }, "meta": { "block": 100415559, "block_age_ms": 766, "block_ts": 1793225982000, "chain": "robinhood", "finalized_block": 100405028, "lag_ms": 31, "safe_block": 100408671 }}What this tells you
clustershas two entries. The larger,c_80b9b545, joins0xdebabd206e5a73dd81a9211533a1bd610c006d20and0xf0fd2e7718ea59a6ddfe884c7e4714ae2dd5610a: togetherpct: 3.60847of supply, sorisk: "medium"(over 3%).- Why they’re linked:
links.shared_fundersnames0x795834684cdec507ea9ee240fdb7e908567dd316, which first funded both (wallets: 2). It is also theroot_funder. The twocommon_funderedges show it sent 1.2 ETH at block 78435467 and 2.5 ETH at block 94538219. - The second cluster,
c_377c5b, is a direct link:links.funding_between: 1. Thefundingedge shows0xa0686afb77ed32fbb402a39642d05158b9fa4c96sent0x4eec10e9c115a7ea67b59eea59c3a3c777fab236its first 2 ETH at block 9427558, so the funder is a member and theroot_funder. Together they holdpct: 2.56134:risk: "low". - All three funding edges are far older than Rebot: its launch transaction was at block 100238938. A link records how a wallet was first funded, which can tie wallets together long before they bought this token.
- Each member node carries its cluster id (
cluster: "c_80b9b545"), andincludes_dev: falseon both clusters: the developer isn’t among them. hidden_infrashows where most of the supply is: the Uniswap v4 PoolManager0x8366a39cc670b4001a1121b8f6a443a643e40951holds 70.85% (the pool’s side) and the Pons launch locker0x267444d099b10fb5ed7c3cc7b7c767adca5749528.16%. The largest real holder has 2.20%.
No clusters at all: sockets
Are the top 50 holders of sockets (0xddccd4690cc99cf19d2e0bdbcb390d17e913958a) connected to each other?
curl "https://api.crius.sh/v1/robinhood/token/0xddccd4690cc99cf19d2e0bdbcb390d17e913958a/bubblemap?top=50" \ -H "x-api-key: $CRIUS_API_KEY"const res = await fetch("https://api.crius.sh/v1/robinhood/token/0xddccd4690cc99cf19d2e0bdbcb390d17e913958a/bubblemap?top=50", { 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/0xddccd4690cc99cf19d2e0bdbcb390d17e913958a/bubblemap?top=50", headers={"x-api-key": os.environ["CRIUS_API_KEY"]},)body = r.json()print(body["data"])Real response 200 Captured Oct 28, 2026 · 138 lines (arrays trimmed)
{ "data": { "block": null, "clusters": [], "edges": [], "hidden_infra": [ { "address": "0x8366a39cc670b4001a1121b8f6a443a643e40951", "label": "uniswap_v4_pool_manager", "pct": 22.2145, "tags": [ "pool" ] }, { "address": "0x267444d099b10fb5ed7c3cc7b7c767adca574952", "label": "pons_v2_launch_locker", "pct": 8.16327, "tags": [ "lp_locker" ] }, { "address": "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044", "label": "pons_v2_meme_hook", "pct": 6.06241, "tags": [ "launchpad" ] }, { "address": "0xbe0b139abc90723af76a89d3051f60ba1b64c8d9", "label": null, "pct": 1, "tags": [ "contract" ] }, { "address": "0xdead000000000000000000000000000000000000", "label": null, "pct": 0.81637, "tags": [ "burn" ] }, { "address": "0x0afeb98a5b40bc38e9cf5a3f202c17843a7ce553", "label": null, "pct": 0.168848, "tags": [ "contract" ] } ], "nodes": [ { "cluster": null, "id": "0xe1a5961d61200006577f2e96114d212889213cd6", "label": null, "pct": 2.82829, "tags": [ "smart_account", "whale" ], "value_usd": 1286.54 }, { "cluster": null, "id": "0xa4109b8d391723d8f6bcfc5d75e6996f61062f76", "label": null, "pct": 2.70423, "tags": [ "smart_account", "fomo", "whale" ], "value_usd": 1230.11 }, { "cluster": null, "id": "0xf2caeb616fb22a8acc00afbaeee83b24f18b354b", "label": null, "pct": 2.23269, "tags": [ "smart_account", "fomo", "whale" ], "value_usd": 1015.61 }, { "cluster": null, "id": "0xb9b642020e216e84887ad414e067928e380bbbd5", "label": null, "pct": 1.77186, "tags": [ "whale" ], "value_usd": 805.992 }, { "cluster": null, "id": "0xc9cafef2258a07f8455da92438360318b34848e2", "label": null, "pct": 1.73578, "tags": [ "smart_account", "fomo", "whale" ], "value_usd": 789.58 }, { "cluster": null, "id": "0xc51c3340b898a67fed7c9f18db75b346dd04f8a0", "label": null, "pct": 1.63331, "tags": [ "smart_account", "fomo", "whale" ], "value_usd": 742.968 } ], "token": "0xddccd4690cc99cf19d2e0bdbcb390d17e913958a" }, "meta": { "block": 100406890, "block_age_ms": 885, "block_ts": 1793225087000, "chain": "robinhood", "finalized_block": 100397649, "lag_ms": 30, "safe_block": 100401329 }}What this tells you
clusters: []andedges: []: none of the top 50 holders funded each other, shared a funder or sent each other tokens. Each bubble stands alone.- The largest holder,
0xe1a5961d61200006577f2e96114d212889213cd6, haspct: 2.82829(about $1,287,value_usd). The holder base is spread out. tagssay who the holders are:whale(1% or more of supply),smart_account, andfomofor smart accounts created by the FOMO trading app, as with0xa4109b8d391723d8f6bcfc5d75e6996f61062f76(2.70%).hidden_infralists the PoolManager (22.21%), the Pons launch locker (8.16%), the PonsV2MemeHook0xe5e702641ea86f4ae6cc3cdaed2b886f976be044(6.06%) and the burn address0xdead000000000000000000000000000000000000(0.82%). Left on the map, the PoolManager would look like the largest holder by far.
How to read the map
Nodes are holders: id, pct of supply, value_usd, label, tags and cluster (null when unlinked). Size them by pct and color them by cluster.
Edges say why two bubbles are connected. Funding edges carry from, to, type, eth, block and hub:
Edge type | Means |
|---|---|
funding | One holder sent the other its first ETH (at least 0.00001 ETH), like 0xa0686afb77ed32fbb402a39642d05158b9fa4c96 → 0x4eec10e9c115a7ea67b59eea59c3a3c777fab236 above. |
common_funder | An outside wallet that first funded two or more of the shown holders, like 0x795834684cdec507ea9ee240fdb7e908567dd316. |
transfers | Holders sent this token to each other. |
Clusters are groups of holders joined by those links:
| Field | Meaning |
|---|---|
id | Cluster id, also set on each member node's cluster. |
wallets, members | How many holders are in it, and their addresses, largest first. |
pct | Share of supply the cluster holds together. |
risk | high above 10% of supply, medium above 3%, else low. |
links | Why they're one cluster: shared_funders (each with the number of members it funded), and counts of funding_between members and token_transfers between them. |
root_funder | The funder shared by the most members, else the member that funded the most others. Never an exchange or bridge. |
includes_dev | The token's developer is in the cluster. |
What doesn’t link wallets matters as much: funding from exchanges, bridges, bots and app relayers (one signer paying the same 100+ wallets again and again), funders that paid more than 500 wallets, dust sends, and lookalike addresses used for address poisoning. Pass include=hubs or include=funders to see those funders, and edges=funding,transfers to choose which edge types come back.
Use it in your app
-
Fetch the map with a Starter key.
top=100is a good default; each call costs 10 credits, so cache it per token for a few minutes. -
Draw it with any force-directed graph library.
nodesandedgesmap directly to nodes and links (from→ source,to→ target). -
Lead with the clusters. Show each cluster’s
pctandrisknext to the map, and say whenincludes_devistrue. -
List
hidden_infraseparately. Users want to know where the rest of the supply is: in the pool, locked or burned.
// Turn a bubble map into nodes and links for a force-directed graph (d3-force, vis-network, …).const token = "0x192b126efea71ca5e7767bc6fc53038d869fbbba"; // Rebotconst res = await fetch(`https://api.crius.sh/v1/robinhood/token/${token}/bubblemap?top=100`, { headers: { "x-api-key": process.env.CRIUS_API_KEY },});const { data } = await res.json();
const nodes = data.nodes.map((n) => ({ id: n.id, radius: 4 + Math.sqrt(n.pct ?? 0) * 6, group: n.cluster ?? n.id }));const ids = new Set(nodes.map((n) => n.id));for (const e of data.edges) for (const a of [e.from, e.to]) if (!ids.has(a)) ids.add(a) && nodes.push({ id: a, radius: 3, group: "funder" });const links = data.edges.map((e) => ({ source: e.from, target: e.to, kind: e.type }));for (const c of data.clusters) console.log(`cluster ${c.id}: ${c.wallets} wallets, ${c.pct}% (${c.risk})`);console.log(`not on the map: ${data.hidden_infra.map((h) => `${h.label ?? h.tags[0]} ${h.pct}%`).join(", ")}`);Frequently asked questions
What is a bubble map?
A picture of a token's top holders where each holder is a bubble sized by how much it holds, and lines connect holders that are linked: one funded the other, they were funded by the same wallet, or they moved the token between them. A big group of connected bubbles is one owner spread over many wallets.
Why isn't the liquidity pool on the bubble map?
Because it isn't a holder. Pools, the Uniswap v4 PoolManager, LP lockers, hooks, burn addresses, routers, other contracts and exchanges are moved to hidden_infra with their share of supply. Everyone trades through them, so drawing them would link unrelated wallets. Add include=infra to draw them; they still never join a cluster.
Why are two wallets funded by the same exchange not in one cluster?
Exchanges, bridges, bots and app relayers fund thousands of unrelated people, so they never link wallets. A funder that paid more than 500 wallets is treated the same way. include=hubs shows those funding edges, marked hub: true, without using them for clusters.
Can I see a bubble map from the past?
Yes: pass block and the map uses holder balances at that block, for example to see who held a token at its peak.
How many holders can the map show?
top sets how many holders become nodes: 100 by default, up to 500. Infrastructure doesn't use up places, so you always get top real holders when the token has that many.
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