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
| Channel | Description | Use Case |
|---|---|---|
gas_prices | Gas price updates | Transaction timing |
gas_prices_{chain} | Chain-specific gas | gas_prices_ethereum |
blockchain_events | Block and tx events | Real-time monitoring |
alerts | User alert triggers | Notifications |
system_status | System health | Status 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 Type | CU Cost |
|---|---|
| New block header | 40 CUs |
| Gas price update | 40 CUs |
| Pending transaction | 40 CUs |
| Alert trigger | 40 CUs |
Subscription Limits by Tier
| Tier | Max Subscriptions | Events/sec |
|---|---|---|
| Free | 5 | ~12 |
| Starter | 10 | ~75 |
| Professional | 50 | ~250 |
| Business | 100 | ~375 |
| Enterprise | Unlimited | Custom |
See Rate Limits for details.
Error Codes
| Code | Reason | Action |
|---|---|---|
| 1008 | Authentication required | Provide valid JWT token |
| 1008 | Invalid token | Refresh JWT token |
| 1008 | Token expired | Get new JWT token |
| 1011 | Internal error | Reconnect with backoff |
| 1013 | Rate limited | Reduce subscription count |
See Also
- Mempool Streaming - Pending transactions
- Liquidation Streaming - DeFi liquidations
- Authentication - JWT tokens
- Rate Limits - CU consumption