FortiBlox LogoFortiBlox Docs
NexusWebSocket Streaming

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

  1. Log in to FortiBlox Nexus Dashboard
  2. Navigate to API Keys
  3. Click Create New API Key
  4. Your key will start with fbx_ (production) or fbx_test_ (testing)
  5. 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 ws

Use 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 websockets
import 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:stream scope
  • 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

Next Steps