Skip to content

Build a token page

Goal: the page Chord itself shows for a token on Arc — price and change in the header, a candle chart, the trade tape, venue split, pools, holders, and a route card — built from six calls on load and two WebSocket channels after.

Component Call Read
Header (symbol, name, logo, price, 24h change, market cap, flags) GET /arc/token/{address}?hours=24 token — the full token row; series for a 24-hour volume bar
Windowed stats (5m / 1h / 6h / 24h, ATH, 24h range, makers) GET /tokens/stats/{address} windows, ath, range24h, markets
Chart GET /tokens/chart/{address}?tf=1h candles (200, oldest first); page back with before
Trade tape GET /tokens/trades/{address}?limit=50 trades, stats24h
Pools GET /lp/token/{address}/pools?chain=5042 pools ranked by health, with launch facts
Holders GET /arc/token/{address}/holders?limit=20 holders, concentration, top (each tagged wallet / pool / burn)
const A = "https://api.getchord.xyz/v1";
const [tok, stats, chart, tape, pools, holders] = await Promise.all([
`${A}/arc/token/${addr}?hours=24`, `${A}/tokens/stats/${addr}`, `${A}/tokens/chart/${addr}?tf=1h`,
`${A}/tokens/trades/${addr}?limit=50`, `${A}/lp/token/${addr}/pools?chain=5042`, `${A}/arc/token/${addr}/holders?limit=20`,
].map((u) => fetch(u).then((r) => (r.ok ? r.json() : null))));

Treat a null (a 404 or 503) per component: a 503 HOLDERS_PENDING means the ledger just started tracking this token — show “building” and retry in a few minutes; a 404 on the chart means the token has no indexed pool yet.

const ws = new WebSocket("wss://api.getchord.xyz/v1/ws/live");
ws.onopen = () => {
ws.send(JSON.stringify({ op: "sub", channel: "candles", token: addr, tf: "1h" }));
ws.send(JSON.stringify({ op: "sub", channel: "trades", token: addr }));
};
ws.onmessage = (e) => {
const m = JSON.parse(e.data);
if (m.op === "candle") chart.upsert(m.candle); // by t; closed:true seals the bucket
if (m.op === "trades") { tape.prepend(m.items); header.price = m.items[0].priceUsd; }
};

The header price is the latest print; a green dot while frames arrive is honest “live”. Re-fetch /arc/token and /tokens/stats every 30–60 s for the rolled-up figures.

  • token.listed false: show the on-chain symbol with a “not on the list” hint; logoURI is a placeholder until art is found.
  • token.flags includes wash or impostor: badge it. The boards already sort such rows down; the page should say why.
  • token.routable false: hide the trade card, or show “no route yet”.
  • marketCapUsd is fully diluted; label it FDV.

Quote on the user’s amount with GET /quote?chain=5042&inputMint=0x3600…0000&outputMint={address}&amount=…, then follow the Swap recipe. Link the row to https://getchord.xyz/arc/token/{address}?side=buy if you would rather hand off to Chord.

tok.byDex (24-hour share per venue id) makes the venue table; map ids to names with the venue ids table. pools from the liquidity family carries score and reasons — show the reasons on hover, they are the difference between a pool with fifty providers and one where the launcher holds everything.

heldByWalletsPct is supply outside pools and burn sinks. concentration.top10 above ~60 % on a token with thousands of holders is worth a badge. Rows tagged pool are the pools themselves; burn is the dead address.