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​