Blog

Developer Guide

How to Track Live Stellar Transactions in Real Time

Stellar processes 5-10 million transactions per day. If you are building a payment app, monitoring tool, or analytics dashboard, you need to see these transactions as they happen. This guide covers three approaches: cursor-based polling, Server-Sent Events (SSE), and the LumenQuery live transaction viewer.

Approach 1: Cursor-Based Polling

The simplest method — poll Horizon with a cursor to get only new transactions:

const HORIZON = 'https://horizon.stellar.org';

async function streamTransactions(onTransaction) {
  // Get starting cursor
  const initial = await fetch(`${HORIZON}/transactions?limit=1&order=desc`);
  const data = await initial.json();
  let cursor = data._embedded.records[0].paging_token;

  // Poll every 5 seconds
  setInterval(async () => {
    try {
      const res = await fetch(
        `${HORIZON}/transactions?cursor=${cursor}&order=asc&limit=100`
      );
      const batch = await res.json();
      for (const tx of batch._embedded.records) {
        onTransaction(tx);
        cursor = tx.paging_token;
      }
    } catch (err) {
      console.error('Poll error:', err.message);
    }
  }, 5000);
}

streamTransactions((tx) => {
  console.log(`TX ${tx.hash.slice(0, 8)}... | ${tx.operation_count} ops | fee: ${tx.fee_charged}`);
});

Pros and Cons

  • Pro: Simple, works everywhere, no special dependencies
  • Con: 5-second delay between polls, uses more API calls
  • Approach 2: Horizon SSE Streaming

    Horizon supports Server-Sent Events for real-time streaming:

    const EventSource = require('eventsource');
    
    const es = new EventSource(
      `${HORIZON}/transactions?cursor=now&order=asc`
    );
    
    es.onmessage = (event) => {
      const tx = JSON.parse(event.data);
      console.log(`TX ${tx.hash.slice(0, 8)}... | ledger ${tx.ledger}`);
    };
    
    es.onerror = (err) => {
      console.error('SSE error, reconnecting...');
    };

    Filtering by Account

    Stream only transactions for a specific account:

    const es = new EventSource(
      `${HORIZON}/accounts/${ACCOUNT_ID}/transactions?cursor=now`
    );

    Filtering by Payments

    Stream only payment operations:

    const es = new EventSource(
      `${HORIZON}/payments?cursor=now`
    );

    Approach 3: Decoding Operations

    Raw transactions are not very readable. Decode the operations for human-friendly output:

    async function decodeTransaction(tx) {
      const opsRes = await fetch(`${HORIZON}/transactions/${tx.hash}/operations`);
      const ops = (await opsRes.json())._embedded.records;
    
      return ops.map(op => {
        switch (op.type) {
          case 'payment':
            return `Payment: ${op.amount} ${op.asset_type === 'native' ? 'XLM' : op.asset_code} from ${op.from.slice(0,8)}... to ${op.to.slice(0,8)}...`;
          case 'create_account':
            return `Create account ${op.account.slice(0,8)}... with ${op.starting_balance} XLM`;
          case 'change_trust':
            return `Trust ${op.asset_code}:${op.asset_issuer?.slice(0,8)}...`;
          case 'invoke_host_function':
            return `Soroban contract invocation`;
          default:
            return `${op.type}`;
        }
      });
    }

    LumenQuery Live Transaction Viewer

    If you do not want to build your own, LumenQuery provides a pre-built live transaction viewer at /dashboard/transactions:

  • Real-time streaming with automatic reconnection
  • Decoded XDR operations in plain English (25+ operation types)
  • Expandable transaction details with raw JSON
  • Soroban operation highlighting
  • Pause/resume and clear controls
  • Dark theme for comfortable monitoring
  • The viewer uses SSE under the hood and decodes all operation types including Soroban contract calls.

    Rate Limit Considerations

    When streaming transactions, be aware of rate limits:

  • Public Horizon limits to ~100-200 requests/minute per IP
  • Each poll cycle counts as a request
  • SSE connections count as long-lived requests
  • For production streaming, use a managed API endpoint with higher limits.


    *Stream live Stellar transactions with LumenQuery. Our live viewer decodes operations in real time — or use the API to build your own.*