WebSockets Quickstart
Subscribe to real-time blockchain events over a persistent WebSocket connection. Receive new blocks, logs, and pending transactions the moment they appear.
Available Subscriptions
| Subscription | Description |
|---|---|
| newHeads | New block headers as they are mined |
| logs | Smart contract event logs with optional topic/address filters |
| newPendingTransactions | Transaction hashes as they enter the mempool |
| syncing | Node sync status changes |
Connect to the WebSocket Endpoint
The WebSocket URL follows the same chain/network pattern as the REST API. Pass your API key as a query parameter.
wss://api.bootnode.dev/v1/ws/{chain}/{network}?apiKey=YOUR_API_KEY
// Examples:
// wss://api.bootnode.dev/v1/ws/ethereum/mainnet?apiKey=bn_live_...
// wss://api.bootnode.dev/v1/ws/base/mainnet?apiKey=bn_live_...
// wss://api.bootnode.dev/v1/ws/polygon/mainnet?apiKey=bn_live_...// Node.js / Browser
const ws = new WebSocket(
"wss://api.bootnode.dev/v1/ws/ethereum/mainnet?apiKey=" +
process.env.BOOTNODE_API_KEY
);
ws.onopen = () => {
console.log("Connected to Bootnode WebSocket");
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
};
ws.onclose = (event) => {
console.log(`WebSocket closed: code=${event.code} reason=${event.reason}`);
};Subscribe to New Block Headers
The newHeads subscription emits a message every time a new block is mined. Each message contains the block header with number, hash, timestamp, gas used, and base fee.
const ws = new WebSocket(
"wss://api.bootnode.dev/v1/ws/ethereum/mainnet?apiKey=" +
process.env.BOOTNODE_API_KEY
);
ws.onopen = () => {
// Subscribe to new block headers
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_subscribe",
params: ["newHeads"],
}));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
// Subscription confirmation
if (message.id === 1) {
console.log("Subscribed! ID:", message.result);
// => "Subscribed! ID: 0x1a2b3c4d"
return;
}
// New block header
if (message.method === "eth_subscription") {
const block = message.params.result;
console.log("New block:", {
number: parseInt(block.number, 16),
hash: block.hash,
timestamp: new Date(parseInt(block.timestamp, 16) * 1000),
gasUsed: parseInt(block.gasUsed, 16),
gasLimit: parseInt(block.gasLimit, 16),
baseFeePerGas: parseInt(block.baseFeePerGas, 16) / 1e9 + " gwei",
transactionCount: block.transactions?.length ?? "N/A",
});
}
};
// Example output:
// New block: {
// number: 20574335,
// hash: "0xabc123...",
// timestamp: 2026-01-15T10:35:22.000Z,
// gasUsed: 15000000,
// gasLimit: 30000000,
// baseFeePerGas: "12.5 gwei",
// transactionCount: 142
// }Subscribe to Logs with Filters
Subscribe to smart contract events by specifying contract addresses and/or event topic signatures. This example monitors USDC Transfer events.
const USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
// keccak256("Transfer(address,address,uint256)")
const TRANSFER_TOPIC =
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
ws.onopen = () => {
// Subscribe to USDC Transfer events
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 2,
method: "eth_subscribe",
params: [
"logs",
{
address: USDC_ADDRESS,
topics: [TRANSFER_TOPIC],
},
],
}));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.id === 2) {
console.log("Subscribed to USDC transfers! ID:", message.result);
return;
}
if (message.method === "eth_subscription") {
const log = message.params.result;
// Decode Transfer event
const from = "0x" + log.topics[1].slice(26);
const to = "0x" + log.topics[2].slice(26);
const amount = BigInt(log.data);
const usdc = Number(amount) / 1e6; // USDC has 6 decimals
console.log(`USDC Transfer: ${from} -> ${to}: $${usdc.toFixed(2)}`);
console.log(` Block: ${parseInt(log.blockNumber, 16)}`);
console.log(` Tx: ${log.transactionHash}`);
}
};
// Example output:
// USDC Transfer: 0xd8dA...6045 -> 0x9876...5432: $1500.00
// Block: 20574335
// Tx: 0xabc123...Subscribe to Pending Transactions
Monitor the mempool for new pending transactions. Useful for MEV detection, frontrunning protection, or real-time transaction feeds.
ws.onopen = () => {
// Subscribe to pending transactions
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 3,
method: "eth_subscribe",
params: ["newPendingTransactions"],
}));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.id === 3) {
console.log("Subscribed to pending txs! ID:", message.result);
return;
}
if (message.method === "eth_subscription") {
const txHash = message.params.result;
console.log("Pending tx:", txHash);
// => "Pending tx: 0xabc123def456..."
// Optionally fetch full transaction details via RPC
// ws.send(JSON.stringify({
// jsonrpc: "2.0",
// id: Date.now(),
// method: "eth_getTransactionByHash",
// params: [txHash],
// }));
}
};Handle Reconnection
WebSocket connections can drop due to network issues or server maintenance. Always implement reconnection logic with exponential backoff.
class BootnodeWebSocket {
private ws: WebSocket | null = null;
private url: string;
private subscriptions: Array<{ id: number; params: any[] }> = [];
private reconnectAttempts = 0;
private maxReconnectAttempts = 10;
private baseDelay = 1000;
private onMessage: (data: any) => void;
constructor(
chain: string,
network: string,
apiKey: string,
onMessage: (data: any) => void
) {
this.url =
`wss://api.bootnode.dev/v1/ws/${chain}/${network}?apiKey=${apiKey}`;
this.onMessage = onMessage;
this.connect();
}
private connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log("WebSocket connected");
this.reconnectAttempts = 0;
// Re-subscribe to all previous subscriptions
for (const sub of this.subscriptions) {
this.ws!.send(JSON.stringify({
jsonrpc: "2.0",
id: sub.id,
method: "eth_subscribe",
params: sub.params,
}));
}
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.onMessage(data);
};
this.ws.onclose = (event) => {
console.log(`WebSocket closed: ${event.code} ${event.reason}`);
this.reconnect();
};
this.ws.onerror = (error) => {
console.error("WebSocket error:", error);
// onclose will fire after onerror, triggering reconnect
};
}
private reconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error("Max reconnection attempts reached. Giving up.");
return;
}
const delay = this.baseDelay * Math.pow(2, this.reconnectAttempts);
const jitter = Math.random() * 1000;
this.reconnectAttempts++;
console.log(
`Reconnecting in ${delay + jitter}ms ` +
`(attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`
);
setTimeout(() => this.connect(), delay + jitter);
}
subscribe(id: number, params: any[]) {
this.subscriptions.push({ id, params });
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
jsonrpc: "2.0",
id,
method: "eth_subscribe",
params,
}));
}
}
unsubscribe(subscriptionId: string) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
jsonrpc: "2.0",
id: Date.now(),
method: "eth_unsubscribe",
params: [subscriptionId],
}));
}
}
close() {
this.maxReconnectAttempts = 0; // Prevent reconnection
this.ws?.close();
}
}
// Usage:
const client = new BootnodeWebSocket(
"ethereum",
"mainnet",
process.env.BOOTNODE_API_KEY!,
(data) => {
if (data.method === "eth_subscription") {
console.log("Event:", data.params.result);
}
}
);
// Subscribe to new blocks
client.subscribe(1, ["newHeads"]);
// Subscribe to USDC transfers
client.subscribe(2, ["logs", {
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
}]);
// Clean shutdown
process.on("SIGINT", () => {
client.close();
process.exit(0);
});Unsubscribing
To stop receiving events for a subscription, send an eth_unsubscribe request with the subscription ID returned from the initial subscribe call.
// Unsubscribe
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 99,
method: "eth_unsubscribe",
params: ["0x1a2b3c4d"], // subscription ID from eth_subscribe response
}));
// Response:
// { "jsonrpc": "2.0", "id": 99, "result": true }Connection Limits
| Plan | Concurrent Connections | Subscriptions per Connection |
|---|---|---|
| Free | 2 | 5 |
| Growth | 10 | 50 |
| Enterprise | Custom | Custom |