Live token lists
tokens.subscribe keeps a list of tokens up to date. The reply has the tokens as they are now, and each push after it only has what changed.
Which tokens
Section titled “Which tokens”Pick tokens in one of these ways:
-
By ID:
"ids": ["wrap.near", "usdt.tether-token.near"]. Usetokens.updateto change the list later. -
By filter:
wherenarrows tokens down by reputation, parent account, creation time, or bounds on numeric fields:"where": { "min_reputation": "NotFake", "min": { "liquidity_usd": 10000 } } -
A top list:
sortandlimitfollow the top tokens by a numeric field as they change places. The sort field has to be one offields:"sort": { "by": "volume_usd_24h", "desc": true }, "limit": 20
With neither ids nor where, it’s every token.
Reading pushes
Section titled “Reading pushes”A push has up to three parts, each left out when empty:
{ "sub": 1, "height": 219969292, "timestamp_millis": 1792003519000, "set": { "wrap.near": { "price_usd": "4.82" }, "new-token.near": { "symbol": "NEW", "price_usd": "0", "volume_usd_24h": 0 } }, "created": ["new-token.near"], "left": ["old-token.near"]}set: the fields that changed, and only those. A token the list didn’t have before (a new token, or one that came to matchwhereor into the toplimit) has every field.created: the tokens insetthat were just created.left: tokens the list no longer has.
So keeping a list is: merge set into what you have, and drop left.
Using the client from the Quickstart:
const tokens = new Map();
const { result } = await api.subscribe( 'tokens.subscribe', { fields: ['symbol', 'icon_hash', 'price_usd', 'volume_usd_24h'], where: { min_reputation: 'NotFake' }, sort: { by: 'volume_usd_24h', desc: true }, limit: 20, min_change: { price_usd: '0.1%', volume_usd_24h: '1%' }, precision: 6, }, (push) => { if (push.end) throw new Error(`Subscription ended: ${push.end.message}`); for (const [token, fields] of Object.entries(push.set ?? {})) { tokens.set(token, { ...tokens.get(token), ...fields }); } for (const token of push.left ?? []) tokens.delete(token); render(tokens); },);
for (const [token, fields] of Object.entries(tableToObjects(result))) tokens.set(token, fields);render(tokens);Fewer updates
Section titled “Fewer updates”Prices and volumes change almost every block. Two params cut the updates down to the ones worth showing:
-
min_changesends a field only once it has moved by a threshold since the value last sent, in either direction. A change from or to0always counts:"min_change": { "price_usd": "0.1%", "liquidity_usd": "1%" }Thresholds are
0.01%,0.1%,0.25%,0.5%,1%,2%,5%or10%. Everyone using the same threshold gets the same values, andwhereandsortgo by them too, so a token near a bound doesn’t flicker in and out of the list. See Token fields for the fields that take one. -
min_interval_mssends at most one push per that much block time, with everything that changed since the one before:200,600,1000,5000,15000or60000.
precision rounds string prices to that many significant digits (1 to 30), but keeps at least 3 decimals, so 12345.6789 at 6 is 12345.679 rather than 12346. Rounded prices also change less often.
Following the tokens on screen
Section titled “Following the tokens on screen”For a long list where only some rows are visible, subscribe with ids and change them as the user scrolls with tokens.update:
{ "id": 2, "method": "tokens.update", "params": { "sub": 1, "add": ["meta-pool.near"], "remove": ["wrap.near"] } }The reply has the added tokens as of its height, and pushes after it carry their changes.
Use icon_hash rather than icon in lists. icon is the whole image inline, in every snapshot. icon_hash is short, and the image is at https://api.intea.rs/icon/<icon_hash>.webp: a WebP of up to 128×128, cached forever since its URL changes whenever the icon does. The browser downloads each icon once, only for the tokens it shows.
Only what changes
Section titled “Only what changes”"snapshot": false leaves rows in the reply empty, for a client that already has the tokens and only wants changes from now on.
After a reconnect
Section titled “After a reconnect”Subscribe with the same params, and resume_from set to the height of the last reply or push you got. The reply then has only what changed since, shaped like a push:
{ "height": 219970000, "timestamp_millis": 1792003900000, "resumed": true, "set": { ... }, "created": [...], "left": [...] }left can name tokens you never had: drop them if you do. When the server can’t work out what changed (it restarted since, or the subscription has a limit), the reply has "resumed": false and every row, like a fresh subscription.
Thresholds are kept for 10 minutes after their last subscriber leaves, so a client back within that only gets what crossed its threshold.