Skip to main content

WebSocket Streaming

Connect to Axol's WebSocket API for real-time blockchain data, gas prices, and system events.

Connection

Endpoint

wss://api.axol.io/api/v1/ws/connect?token=YOUR_JWT_TOKEN

Authentication

All WebSocket connections require JWT authentication via query parameter:

# Get JWT token first
curl -X POST https://api.axol.io/api/v1/auth/token \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"grant_type": "client_credentials"}'

# Connect with token
wscat -c "wss://api.axol.io/api/v1/ws/connect?token=eyJhbG..."

Connection Response

{
"type": "connected",
"data": {
"connection_id": "conn_abc123",
"message": "Connected to Axol API WebSocket",
"available_channels": [
"gas_prices",
"blockchain_events",
"alerts",
"system_status"
]
}
}

Available Channels

ChannelDescriptionUse Case
gas_pricesGas price updatesTransaction timing
gas_prices_{chain}Chain-specific gasgas_prices_ethereum
blockchain_eventsBlock and tx eventsReal-time monitoring
alertsUser alert triggersNotifications
system_statusSystem healthStatus monitoring

Message Types

Subscribe to Channels

{
"type": "subscribe",
"channels": ["gas_prices", "alerts"]
}

Response:

{
"type": "subscription_result",
"data": {
"subscribed_channels": ["gas_prices", "alerts"],
"message": "Subscribed to 2 channels"
}
}

Unsubscribe

{
"type": "unsubscribe",
"channels": ["gas_prices"]
}

Ping/Pong

Keep connection alive:

{"type": "ping", "data": {}}

Response:

{"type": "pong", "data": {}}

Get Connection Info

{"type": "get_info"}

Response:

{
"type": "connection_info",
"data": {
"connection_id": "conn_abc123",
"user_id": "user_xyz",
"channels": ["gas_prices", "alerts"],
"connected_at": "2026-01-15T10:30:00Z",
"last_ping": "2026-01-15T10:35:00Z",
"state": "active"
}
}

Specialized Endpoints

Gas Price Updates

Dedicated endpoint for real-time gas monitoring:

wss://api.axol.io/api/v1/ws/gas-updates/{chain}?token=JWT&interval=10

Parameters:

  • chain: ethereum, polygon, arbitrum, etc.
  • interval: Update interval in seconds (5-300, default: 10)

Message format:

{
"type": "gas_update",
"data": {
"chain": "ethereum",
"gas_data": {
"slow": 20,
"standard": 25,
"fast": 30,
"instant": 40,
"base_fee": 18
}
},
"channel": "gas_prices_ethereum"
}

Mempool Streaming

See Mempool Streaming for pending transaction streaming.

wss://api.axol.io/api/v1/mempool/{chain}/stream?token=JWT

Liquidation Streaming

See Liquidation Streaming for DeFi liquidation opportunities.

wss://api.axol.io/api/v1/defi/liquidations/stream?token=JWT

Code Examples

Python - Basic Connection

import asyncio
import json
import websockets

async def connect_websocket(jwt_token: str):
url = f"wss://api.axol.io/api/v1/ws/connect?token={jwt_token}"

async with websockets.connect(url) as ws:
# Receive welcome message
welcome = await ws.recv()
print(f"Connected: {json.loads(welcome)}")

# Subscribe to channels
await ws.send(json.dumps({
"type": "subscribe",
"channels": ["gas_prices", "alerts"]
}))

result = await ws.recv()
print(f"Subscribed: {json.loads(result)}")

# Listen for messages
while True:
try:
message = await ws.recv()
data = json.loads(message)
print(f"Received: {data['type']}")

# Handle different message types
if data["type"] == "gas_update":
gas = data["data"]["gas_data"]
print(f" Fast gas: {gas['fast']} gwei")
elif data["type"] == "alert_triggered":
print(f" Alert: {data['data']['name']}")

except websockets.ConnectionClosed:
print("Connection closed")
break

asyncio.run(connect_websocket("YOUR_JWT_TOKEN"))

Python - Gas Monitor Class

import asyncio
import json
from typing import Callable, Optional
import websockets

class GasMonitor:
def __init__(self, jwt_token: str, chain: str = "ethereum"):
self.jwt_token = jwt_token
self.chain = chain
self.url = f"wss://api.axol.io/api/v1/ws/gas-updates/{chain}"
self._ws = None
self._callback: Optional[Callable] = None

async def connect(self, interval: int = 10):
"""Connect to gas updates stream."""
url = f"{self.url}?token={self.jwt_token}&interval={interval}"
self._ws = await websockets.connect(url)

def on_update(self, callback: Callable):
"""Register callback for gas updates."""
self._callback = callback

async def stream(self):
"""Stream gas updates."""
while True:
try:
message = await self._ws.recv()
data = json.loads(message)

if data["type"] == "gas_update" and self._callback:
await self._callback(data["data"])

except websockets.ConnectionClosed:
print("Reconnecting...")
await asyncio.sleep(1)
await self.connect()

async def close(self):
if self._ws:
await self._ws.close()

# Usage
async def main():
monitor = GasMonitor("YOUR_JWT_TOKEN", "ethereum")

@monitor.on_update
async def handle_gas(data):
gas = data["gas_data"]
print(f"ETH Gas - Fast: {gas['fast']} | Base: {gas['base_fee']}")

await monitor.connect(interval=10)
await monitor.stream()

asyncio.run(main())

TypeScript - WebSocket Client

interface WsMessage {
type: string;
data: any;
channel?: string;
timestamp?: string;
}

class AxolWebSocket {
private ws: WebSocket | null = null;
private handlers: Map<string, ((data: any) => void)[]> = new Map();
private reconnectAttempts = 0;

constructor(
private jwtToken: string,
private maxReconnects = 5
) {}

async connect(): Promise<void> {
const url = `wss://api.axol.io/api/v1/ws/connect?token=${this.jwtToken}`;

return new Promise((resolve, reject) => {
this.ws = new WebSocket(url);

this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
resolve();
};

this.ws.onmessage = (event) => {
const message: WsMessage = JSON.parse(event.data);
this.dispatch(message);
};

this.ws.onclose = () => {
console.log('WebSocket closed');
this.reconnect();
};

this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
reject(error);
};
});
}

private async reconnect(): Promise<void> {
if (this.reconnectAttempts < this.maxReconnects) {
this.reconnectAttempts++;
const delay = Math.pow(2, this.reconnectAttempts) * 1000;
console.log(`Reconnecting in ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
await this.connect();
}
}

subscribe(channels: string[]): void {
this.send({ type: 'subscribe', channels });
}

unsubscribe(channels: string[]): void {
this.send({ type: 'unsubscribe', channels });
}

ping(): void {
this.send({ type: 'ping', data: {} });
}

on(messageType: string, handler: (data: any) => void): void {
if (!this.handlers.has(messageType)) {
this.handlers.set(messageType, []);
}
this.handlers.get(messageType)!.push(handler);
}

private dispatch(message: WsMessage): void {
const handlers = this.handlers.get(message.type) || [];
handlers.forEach(h => h(message.data));

// Also dispatch to 'all' handlers
const allHandlers = this.handlers.get('all') || [];
allHandlers.forEach(h => h(message));
}

private send(data: object): void {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}

close(): void {
this.ws?.close();
}
}

// Usage
async function main() {
const ws = new AxolWebSocket(process.env.JWT_TOKEN!);

ws.on('connected', (data) => {
console.log('Connected:', data.connection_id);
console.log('Available channels:', data.available_channels);
});

ws.on('gas_update', (data) => {
console.log(`Gas update: ${data.gas_data.fast} gwei`);
});

ws.on('alert_triggered', (data) => {
console.log(`Alert: ${data.name} - ${data.message}`);
});

await ws.connect();
ws.subscribe(['gas_prices', 'alerts']);

// Keep alive with periodic pings
setInterval(() => ws.ping(), 30000);
}

main();

JavaScript (Browser)

class AxolWS {
constructor(jwtToken) {
this.jwtToken = jwtToken;
this.ws = null;
this.handlers = {};
}

connect() {
return new Promise((resolve, reject) => {
const url = `wss://api.axol.io/api/v1/ws/connect?token=${this.jwtToken}`;
this.ws = new WebSocket(url);

this.ws.onopen = () => {
console.log('Connected');
resolve();
};

this.ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (this.handlers[msg.type]) {
this.handlers[msg.type].forEach(h => h(msg.data));
}
};

this.ws.onerror = reject;
});
}

on(type, handler) {
if (!this.handlers[type]) this.handlers[type] = [];
this.handlers[type].push(handler);
}

subscribe(channels) {
this.ws.send(JSON.stringify({ type: 'subscribe', channels }));
}

close() {
this.ws?.close();
}
}

// Usage
const ws = new AxolWS('YOUR_JWT_TOKEN');

ws.on('connected', (data) => {
console.log('Connected:', data.connection_id);
ws.subscribe(['gas_prices']);
});

ws.on('gas_update', (data) => {
document.getElementById('gas-price').textContent =
`${data.gas_data.fast} gwei`;
});

ws.connect();

Connection Management

Heartbeat

Send periodic pings to keep connection alive:

async def heartbeat(ws, interval=30):
while True:
await asyncio.sleep(interval)
await ws.send(json.dumps({"type": "ping"}))

Auto-Reconnection

Implement exponential backoff:

async def connect_with_retry(token, max_retries=5):
for attempt in range(max_retries):
try:
ws = await websockets.connect(f"wss://...?token={token}")
return ws
except Exception as e:
delay = 2 ** attempt
print(f"Retry {attempt + 1} in {delay}s")
await asyncio.sleep(delay)
raise ConnectionError("Max retries exceeded")

Error Handling

try:
message = await ws.recv()
except websockets.ConnectionClosed as e:
print(f"Connection closed: {e.code} - {e.reason}")
except Exception as e:
print(f"Error: {e}")

Rate Limits

WebSocket events consume CUs from your monthly pool:

Event TypeCU Cost
New block header40 CUs
Gas price update40 CUs
Pending transaction40 CUs
Alert trigger40 CUs

Subscription Limits by Tier

TierMax SubscriptionsEvents/sec
Free5~12
Starter10~75
Professional50~250
Business100~375
EnterpriseUnlimitedCustom

See Rate Limits for details.


Error Codes

CodeReasonAction
1008Authentication requiredProvide valid JWT token
1008Invalid tokenRefresh JWT token
1008Token expiredGet new JWT token
1011Internal errorReconnect with backoff
1013Rate limitedReduce subscription count

See Also