Getting Started with WebSocket
Connect to FortiBlox WebSocket streaming and make your first subscription
Getting Started
This guide will walk you through connecting to FortiBlox WebSocket streaming and receiving real-time X1 Blockchain transactions.
Current Beta - Broadcast Mode
WebSocket currently broadcasts all network transactions to connected clients automatically. No subscription messages needed - just connect and start receiving! Subscription filtering is coming in a future update.
Prerequisites
- An active FortiBlox API key (get one at nexus.fortiblox.com)
- Basic knowledge of WebSocket or async programming
- Node.js 16+ (for Node.js examples) or Python 3.8+ (for Python examples)
Step 1: Get Your API Key
- Log in to FortiBlox Nexus Dashboard
- Navigate to API Keys
- Click Create New API Key
- Your key will start with
fbx_(production) orfbx_test_(testing) - Copy and store it securely
Never expose your API key in client-side code or commit it to version control!
Step 2: Choose Your Platform
Browser WebSocket
Browsers don't support custom headers in WebSocket, so pass your API key as a query parameter:
const apiKey = 'fbx_YOUR_KEY_HERE';
const ws = new WebSocket(
`wss://nexus.fortiblox.com/geyser/ws?api-key=${apiKey}`
);
ws.onopen = () => {
console.log('✅ Connected to FortiBlox WebSocket');
// Transactions will stream automatically - no subscription needed!
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'connected') {
console.log('WebSocket ready:', data.message);
} else if (data.type === 'transaction') {
console.log('New transaction:', data.data.signature);
}
};
ws.onerror = (error) => {
console.error('❌ WebSocket error:', error);
};
ws.onclose = (event) => {
console.log('Connection closed:', event.code, event.reason);
};For production browser apps, use the RPC Proxy to keep your API key server-side.
Node.js WebSocket
Install the ws library:
npm install wsUse headers for authentication (more secure):
const WebSocket = require('ws');
const ws = new WebSocket('wss://nexus.fortiblox.com/geyser/ws', {
headers: {
'X-API-Key': 'fbx_YOUR_KEY_HERE'
}
});
ws.on('open', () => {
console.log('✅ Connected to FortiBlox WebSocket');
// Transactions will stream automatically - no subscription needed!
});
ws.on('message', (data) => {
const message = JSON.parse(data);
if (message.type === 'connected') {
console.log('WebSocket ready:', message.message);
} else if (message.type === 'transaction') {
console.log('New transaction:', message.data.signature);
}
});
ws.on('error', (error) => {
console.error('❌ WebSocket error:', error);
});
ws.on('close', (code, reason) => {
console.log(`Connection closed: ${code} - ${reason}`);
});Alternatively, use query parameter (same as browser):
const ws = new WebSocket(
'wss://nexus.fortiblox.com/geyser/ws?api-key=fbx_YOUR_KEY_HERE'
);Python WebSocket
Install the websockets library:
pip install websocketsimport asyncio
import websockets
import json
async def connect_websocket():
uri = 'wss://nexus.fortiblox.com/geyser/ws?api-key=fbx_YOUR_KEY_HERE'
async with websockets.connect(uri) as ws:
print('✅ Connected to FortiBlox WebSocket')
# Transactions will stream automatically - no subscription needed!
# Keep connection alive and listen for messages
async for message in ws:
data = json.loads(message)
if data['type'] == 'connected':
print(f"WebSocket ready: {data['message']}")
elif data['type'] == 'transaction':
print(f"New transaction: {data['data']['signature']}")
# Run the WebSocket client
try:
asyncio.run(connect_websocket())
except KeyboardInterrupt:
print('Disconnected')For header-based authentication:
import websockets
async def connect_with_headers():
uri = 'wss://nexus.fortiblox.com/geyser/ws'
headers = {
'X-API-Key': 'fbx_YOUR_KEY_HERE'
}
async with websockets.connect(uri, extra_headers=headers) as ws:
print('✅ Connected')
# Transactions will stream automatically
async for message in ws:
data = json.loads(message)
if data['type'] == 'connected':
print(f"WebSocket ready: {data['message']}")
elif data['type'] == 'transaction':
print(f"New transaction: {data['data']['signature']}")
asyncio.run(connect_with_headers())Step 3: Receive Transactions
Once connected, transactions stream automatically! No subscription message needed.
Connection Confirmation
When you first connect, you'll receive a confirmation message:
{
"type": "connected",
"message": "WebSocket connected successfully"
}Transaction Messages
Then you'll immediately start receiving transaction events:
{
"type": "transaction",
"timestamp": "2025-11-24T12:34:56.789Z",
"data": {
"signature": "5j7s8K9mN3pQ2rT4uV6wX7yZ8A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U",
"slot": 245678900,
"blockTime": 1700000000,
"err": null,
"fee": 5000,
"accounts": ["account1...", "account2..."],
"logs": ["Program log: ..."]
}
}High Volume Stream
The WebSocket broadcasts all network transactions, which can be high volume. Be prepared to handle hundreds of messages per second during peak times.
Step 4: Complete Example with Client-Side Filtering
Here's a complete working example that receives all transactions and filters for specific accounts client-side:
const WebSocket = require('ws');
class FortiBloxStream {
constructor(apiKey, watchedAccounts = []) {
this.apiKey = apiKey;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.watchedAccounts = new Set(watchedAccounts);
this.transactionCount = 0;
this.matchedCount = 0;
}
connect() {
this.ws = new WebSocket('wss://nexus.fortiblox.com/geyser/ws', {
headers: { 'X-API-Key': this.apiKey }
});
this.ws.on('open', () => {
console.log('✅ Connected to FortiBlox');
console.log('📡 Receiving all network transactions...');
if (this.watchedAccounts.size > 0) {
console.log(`🔍 Filtering for ${this.watchedAccounts.size} accounts`);
}
this.reconnectAttempts = 0;
});
this.ws.on('message', (data) => {
const message = JSON.parse(data);
this.handleMessage(message);
});
this.ws.on('error', (error) => {
console.error('❌ WebSocket error:', error.message);
});
this.ws.on('close', () => {
console.log('⚠️ Connection closed');
this.reconnect();
});
}
handleMessage(message) {
switch (message.type) {
case 'connected':
console.log(`✅ ${message.message}`);
break;
case 'transaction':
this.transactionCount++;
// Client-side filtering
if (this.watchedAccounts.size === 0 || this.matchesWatchedAccounts(message.data)) {
this.matchedCount++;
console.log('💸 Transaction:', message.data.signature);
console.log(' Slot:', message.data.slot);
console.log(' Fee:', message.data.fee);
}
// Log stats every 100 transactions
if (this.transactionCount % 100 === 0) {
console.log(`📊 Stats: ${this.transactionCount} total, ${this.matchedCount} matched`);
}
break;
default:
console.log('Message:', message);
}
}
matchesWatchedAccounts(txData) {
if (!txData.accounts) return false;
return txData.accounts.some(account => this.watchedAccounts.has(account));
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
console.log(`🔄 Reconnecting in ${delay}ms... (attempt ${this.reconnectAttempts})`);
setTimeout(() => this.connect(), delay);
} else {
console.error('❌ Max reconnection attempts reached');
}
}
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// Usage - filter for specific accounts (optional)
const API_KEY = process.env.FORTIBLOX_API_KEY || 'fbx_YOUR_KEY_HERE';
const WATCHED_ACCOUNTS = [
'YourWalletAddress1Here',
'YourWalletAddress2Here'
];
const stream = new FortiBloxStream(API_KEY, WATCHED_ACCOUNTS);
stream.connect();
// Graceful shutdown
process.on('SIGINT', () => {
console.log('Shutting down...');
stream.disconnect();
process.exit(0);
});import asyncio
import websockets
import json
import os
class FortiBloxStream:
def __init__(self, api_key, watched_accounts=None):
self.api_key = api_key
self.uri = f'wss://nexus.fortiblox.com/geyser/ws?api-key={api_key}'
self.reconnect_attempts = 0
self.max_reconnect_attempts = 5
self.watched_accounts = set(watched_accounts or [])
self.transaction_count = 0
self.matched_count = 0
async def connect(self):
try:
async with websockets.connect(self.uri) as ws:
print('✅ Connected to FortiBlox')
print('📡 Receiving all network transactions...')
if self.watched_accounts:
print(f'🔍 Filtering for {len(self.watched_accounts)} accounts')
self.reconnect_attempts = 0
# Listen for messages
async for message in ws:
await self.handle_message(message)
except websockets.exceptions.ConnectionClosed:
print('⚠️ Connection closed')
await self.reconnect()
except Exception as e:
print(f'❌ Error: {e}')
await self.reconnect()
def matches_watched_accounts(self, tx_data):
if 'accounts' not in tx_data:
return False
return any(account in self.watched_accounts for account in tx_data['accounts'])
async def handle_message(self, message):
data = json.loads(message)
if data['type'] == 'connected':
print(f"✅ {data['message']}")
elif data['type'] == 'transaction':
self.transaction_count += 1
# Client-side filtering
if not self.watched_accounts or self.matches_watched_accounts(data['data']):
self.matched_count += 1
tx = data['data']
print(f"💸 Transaction: {tx['signature']}")
print(f" Slot: {tx['slot']}")
print(f" Fee: {tx['fee']}")
# Log stats every 100 transactions
if self.transaction_count % 100 == 0:
print(f"📊 Stats: {self.transaction_count} total, {self.matched_count} matched")
else:
print(f'Message: {data}')
async def reconnect(self):
if self.reconnect_attempts < self.max_reconnect_attempts:
self.reconnect_attempts += 1
delay = min(2 ** self.reconnect_attempts, 30)
print(f'🔄 Reconnecting in {delay}s... (attempt {self.reconnect_attempts})')
await asyncio.sleep(delay)
await self.connect()
else:
print('❌ Max reconnection attempts reached')
# Usage - filter for specific accounts (optional)
API_KEY = os.getenv('FORTIBLOX_API_KEY', 'fbx_YOUR_KEY_HERE')
WATCHED_ACCOUNTS = [
'YourWalletAddress1Here',
'YourWalletAddress2Here'
]
stream = FortiBloxStream(API_KEY, WATCHED_ACCOUNTS)
try:
asyncio.run(stream.connect())
except KeyboardInterrupt:
print('Shutting down...')Common Connection Issues
Issue: "401 Unauthorized"
Cause: Invalid or missing API key
Solutions:
- Verify your API key is correct (starts with
fbx_) - Check the key is active in your dashboard
- Ensure no extra spaces or newlines in the key
- Verify you're using the correct authentication method
Issue: "403 Forbidden"
Cause: API key missing required scope
Solutions:
- Ensure your API key has
geyser:streamscope - Regenerate your API key if needed
- Free tier includes this scope by default
Issue: "429 Too Many Requests"
Cause: Connection limit exceeded
Solutions:
- Close unused WebSocket connections
- Check your current tier limits:
- Free/Developer: 5 concurrent connections
- Business/Professional: 250 concurrent connections
- Consider upgrading your plan
Issue: Connection Keeps Dropping
Cause: Network instability or server maintenance
Solutions:
- Implement reconnection logic with exponential backoff
- Monitor the status page
- Add heartbeat/ping mechanism
- Check your network stability