Quickstart
The examples use fetch and the browser’s WebSocket, which Node.js has too.
Over HTTP
Section titled “Over HTTP”-
Ask for tokens, with only the fields you need.
GET https://api.intea.rs/tokens?ids=wrap.near&fields=symbol,price_usd{"height": 219969220,"timestamp_millis": 1792003477277,"columns": ["account_id", "symbol", "price_usd"],"rows": [["wrap.near", "wNEAR", "4.815189744118901"]],"total": 1,"missing": []}Tokens come as a table: the column names once, then a row per token, in the same order.
account_idalways comes first. -
Turn the table into objects, if that’s easier to work with.
/*** Turns a table into `{ <key>: { <column>: value } }`, keyed by its first column*/function tableToObjects({ columns, rows }) {const objects = {};for (const row of rows) {const object = {};columns.slice(1).forEach((column, i) => (object[column] = row[i + 1]));objects[String(row[0])] = object;}return objects;}const params = new URLSearchParams({ids: 'wrap.near,usdt.tether-token.near',fields: 'symbol,price_usd,liquidity_usd,price_change_24h',});const response = await fetch(`https://api.intea.rs/tokens?${params}`);const result = await response.json();if (!response.ok) throw new Error(result.error.message);const tokens = tableToObjects(result);// tokens['wrap.near'] is { symbol: 'wNEAR', price_usd: '4.81', ... }
Every method that answers once works like this over HTTP. See Using HTTP for how params go in a URL.
Over the WebSocket
Section titled “Over the WebSocket”-
Connect. The server’s first message is a
hello, with the latest block and the limits:{ "hello": { "version": 2, "height": 219969289, "timestamp_millis": 1792003517185, "limits": { ... } }, "credits_used": 0 } -
Subscribe. Send a request with an
idyou choose. The reply has the sameid, and every update after it hassubset to thatid:→ { "id": 1, "method": "tokens.subscribe", "params": { "fields": ["symbol", "price_usd"], "ids": ["wrap.near"] } }← { "id": 1, "result": { "height": 219969289, "columns": ["account_id", "symbol", "price_usd"], "rows": [["wrap.near", "wNEAR", "4.81"]], ... } }← { "sub": 1, "height": 219969292, "set": { "wrap.near": { "price_usd": "4.82" } }, ... }Updates only carry what changed: here, only
price_usdofwrap.near. -
Keep a list up to date. This follows the 20 tokens with the most volume in the last 24 hours. Prices only update once they’ve moved 0.1%, and tokens that drop out of the top 20 are in
left:The examples on this site use this small client. It matches replies to requests by
id, and passes each subscription’s updates to its callback:/** A minimal Intear API client: requests resolve with their result, and pushes go to their subscription's callback */export class IntearApi {#socket;#nextId = 1;#pending = new Map();#subscriptions = new Map();ready;constructor(url = 'wss://api.intea.rs/ws') {this.#socket = new WebSocket(url);this.ready = new Promise((resolve, reject) => {this.#socket.addEventListener('error', () => reject(new Error('Connection failed')));this.#socket.addEventListener('message', (event) => {const message = JSON.parse(event.data);if ('hello' in message) {resolve(message.hello);} else if ('sub' in message) {this.#subscriptions.get(message.sub)?.(message);} else if ('id' in message) {const request = this.#pending.get(message.id);this.#pending.delete(message.id);if ('error' in message) {this.#subscriptions.delete(message.id);const error = message.error;request?.reject(new Error(`${error.code}: ${error.message}`));} else {request?.resolve(message.result);}}});});}/*** Sends a request, and resolves with its result*/async request(method, params) {await this.ready;const id = this.#nextId++;const result = new Promise((resolve, reject) => this.#pending.set(id, { resolve, reject }));this.#socket.send(JSON.stringify({ id, method, params }));return result;}/*** Makes a subscription. `onPush` gets each of its pushes, after the result*/async subscribe(method, params, onPush) {await this.ready;const id = this.#nextId++;this.#subscriptions.set(id, onPush);const result = new Promise((resolve, reject) => this.#pending.set(id, { resolve, reject }));this.#socket.send(JSON.stringify({ id, method, params }));return { sub: id, result: await result };}async unsubscribe(sub) {await this.request('unsubscribe', { sub });this.#subscriptions.delete(sub);}close() {this.#socket.close();}}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);
What’s next
Section titled “What’s next”- Connecting: how the protocol works, and what to do after a reconnect.
- Live token lists: thresholds, intervals, and following only the tokens on screen.
- History, then live: show past trades and keep the list live without a gap.