Skip to main content

What is transactionSubscribe?

The transactionSubscribe Enhanced WebSocket method enables real-time transaction events. To use it, provide a TransactionSubscribeFilter and optionally include TransactionSubscribeOptions for further customization.
Enhanced WebSockets are now supported on the primary wss://mainnet.helius-rpc.com and wss://devnet.helius-rpc.com endpoints. Switch from the old wss://atlas-mainnet.helius-rpc.com and wss://atlas-devnet.helius-rpc.com endpoints for a unified developer experience.

TransactionSubscribeFilter

  • vote: boolean flag to include/exclude vote-related transactions
  • failed: boolean flag to include/exclude transactions that failed
  • signature: filters updates to a specific transaction based on its signature
  • accountInclude: list of accounts for which you want to receive transaction updates. Only one of the accounts must be included in the transaction updates (e.g., Account 1 OR 2).
  • accountExclude: list of accounts you want to exclude from transaction updates
  • accountRequired: transactions must include all of the specified accounts to be included in updates (e.g., Account 1 AND 2)
You can include up to 50,000 addresses in the accountsInclude, accountExclude and accountRequired arrays.

TransactionSubscribeOptions (Optional)

  • commitment: commitment level for fetching data (processed, confirmed, or finalized)
  • encoding: encoding format of the returned data (base58, base64, or jsonParsed)
  • transactionDetails: level of detail for the returned data (full, signatures, accounts and none)
  • showRewards: boolean flag indicating if reward data should be included in the updates
  • maxSupportedTransactionVersion: specifies the highest version of transactions you want to receive updates from. To get both legacy and v0 transactions, set the value to 0.
maxSupportedTransactionVersion is required to return the accounts and full-level details of a given transaction (i.e., transactionDetails: "accounts" | "full").

Transaction Subscribe Example

In this example, we are subscribing to transactions that contain the Raydium account 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8. When a transaction occurs that contains 675k...1Mp8 account in the accountKeys of the transaction, we will receive a WSS notification. Based on the subscription options, the transaction notification will be sent at the processed commitment level,jsonParsed encoding, full transaction details, and will show rewards.
const WebSocket = require('ws');

// Create a WebSocket connection
const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=<API_KEY>');

// Function to send a request to the WebSocket server
function sendRequest(ws) {
    const request = {
        jsonrpc: "2.0",
        id: 420,
        method: "transactionSubscribe",
        params: [
            {
                accountInclude: ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"]
            },
            {
                commitment: "processed",
                encoding: "jsonParsed",
                transactionDetails: "full",
                showRewards: true,
                maxSupportedTransactionVersion: 0
            }
        ]
    };
    ws.send(JSON.stringify(request));
}

// Function to send a ping to the WebSocket server
function startPing(ws) {
    setInterval(() => {
        if (ws.readyState === WebSocket.OPEN) {
            ws.ping();
            console.log('Ping sent');
        }
    }, 30000); // Ping every 30 seconds
}

// Define WebSocket event handlers

ws.on('open', function open() {
    console.log('WebSocket is open');
    sendRequest(ws);  // Send a request once the WebSocket is open
    startPing(ws);    // Start sending pings
});

ws.on('message', function incoming(data) {
    const messageStr = data.toString('utf8');
    try {
        const messageObj = JSON.parse(messageStr);
        console.log('Received:', messageObj);
    } catch (e) {
        console.error('Failed to parse JSON:', e);
    }
});

ws.on('error', function error(err) {
    console.error('WebSocket error:', err);
});

ws.on('close', function close() {
    console.log('WebSocket is closed');
});

Example Notification

{
    "jsonrpc": "2.0",
    "method": "transactionNotification",
    "params": {
        "subscription": 4743323479349712,
        "result": {
            "transaction": {
                "transaction": [
                    "Ae6zfSExLsJ/E1+q0jI+3ueAtSoW+6HnuDohmuFwagUo2BU4OpkSdUKYNI1dJfMOonWvjaumf4Vv1ghn9f3Avg0BAAEDGycH0OcYRpfnPNuu0DBQxTYPWpmwHdXPjb8y2P200JgK3hGiC2JyC9qjTd2lrug7O4cvSRUVWgwohbbefNgKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0HcpwKokfYDDAJTaF/TWRFWm0Gz5/me17PRnnywHurMBAgIAAQwCAAAAoIYBAAAAAAA=",
                    "base64"
                ],
                "meta": {
                    "err": null,
                    "status": {
                        "Ok": null
                    },
                    "fee": 5000,
                    "preBalances": [
                        28279852264,
                        158122684,
                        1
                    ],
                    "postBalances": [
                        28279747264,
                        158222684,
                        1
                    ],
                    "innerInstructions": [],
                    "logMessages": [
                        "Program 11111111111111111111111111111111 invoke [1]",
                        "Program 11111111111111111111111111111111 success"
                    ],
                    "preTokenBalances": [],
                    "postTokenBalances": [],
                    "rewards": null,
                    "loadedAddresses": {
                        "writable": [],
                        "readonly": []
                    },
                    "computeUnitsConsumed": 0
                }
            },
            "signature": "5moMXe6VW7L7aQZskcAkKGQ1y19qqUT1teQKBNAAmipzdxdqVLAdG47WrsByFYNJSAGa9TByv15oygnqYvP6Hn2p",
            "slot": 224341380,
            "transactionIndex": 42
        }
    }
}

Monitoring new Jupiter DCAs

Jupiter DCA, or Dollar Cost Averaging, is a way to schedule recurring trades on Solana. Because these scheduled buy/sell orders are recorded on-chain, traders can use the transactionSubscribe method and getAsset to listen for new orders.
const WebSocket = require('ws');   
const bs58      = require('bs58').default;

/* ───────────────────── 1.  CONFIG ──────────────────────────── */
const API_KEY   = process.env.HELIUS_API_KEY || (() => { throw new Error('Set HELIUS_API_KEY'); })();
const HELIUS_WS  = `wss://mainnet.helius-rpc.com?api-key=${API_KEY}`;
const HELIUS_RPC = `https://mainnet.helius-rpc.com/?api-key=${API_KEY}`;
const DCA_PROGRAM_ID = 'DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M';

/* ───────────────────── 2.  BINARY DECODER ──────────────────── */
function decodeOpenDcaV2(base58Data) {
  const buf = Buffer.from(bs58.decode(base58Data));
  return {
    appIdx:    buf.readBigUInt64LE(8), // Application Index
    inAmount:  buf.readBigUInt64LE(16), // Input Amount
    perCycle:  buf.readBigUInt64LE(24), // Per Cycle
    interval:  buf.readBigUInt64LE(32) // Interval
  };
}

const TOKEN_META = new Map();   // mint → { symbol, decimals }
/**
 * Fetch symbol & decimals for a mint once then cache.
 * Uses Helius getAsset DAS method: https://www.helius.dev/docs/api-reference/das/getasset
 */
async function getMeta(mint) {
  if (TOKEN_META.has(mint)) return TOKEN_META.get(mint);

  const body = {
    jsonrpc: '2.0',
    id:      'meow',
    method:  'getAsset',
    params:  { id: mint, displayOptions: { showFungible: true } }
  };

  const { result } = await fetch(HELIUS_RPC, {
    method:  'POST',
    headers: { 'Content-Type': 'application/json' },
    body:    JSON.stringify(body)
  }).then(r => r.json());

  const tokenInfo = result.token_info || {};
  const metadata = { symbol: tokenInfo.symbol || '?', decimals: tokenInfo.decimals ?? 0 };
  TOKEN_META.set(mint, metadata);
  return metadata;
}

/* ───────────────────── 4.  PRETTY HELPERS ──────────────────── */
function formatTimestamp(unixSeconds) {
    return new Date(Number(unixSeconds) * 1_000)
             .toISOString()
             .replace('T', ' ')
             .replace('.000Z', ' UTC');
}
function formatInterval(seconds) {
    if (seconds % 86_400 === 0) return `every ${seconds / 86_400}d`;
    if (seconds %  3_600 === 0) return `every ${seconds /  3_600}h`;
    if (seconds %     60 === 0) return `every ${seconds /     60}m`;
    return `every ${seconds}s`;
  }

  function formatAmount(raw, decimals, symbol) {
    const ui = Number(raw) / 10 ** decimals;
    return `${ui} ${symbol}`;
  }
/* ───────────────────── 5.  WEBSOCKET SETUP ─────────────────── */
const ws = new WebSocket(HELIUS_WS);

ws.on('open', () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id:      1,
    method:  'transactionSubscribe',
    params: [
      { failed: false, accountInclude: [DCA_PROGRAM_ID] },
      {
        commitment: 'confirmed',
        encoding:   'jsonParsed',
        transactionDetails: 'full',
        maxSupportedTransactionVersion: 0
      }
    ]
  }));

  setInterval(() => ws.ping(), 10_000);
});

/* ───────────────────── 6.  MAIN MESSAGE HANDLER ────────────── */
ws.on('message', async raw => {
  const payload = JSON.parse(raw);
  const result  = payload.params?.result;
  if (!result) return;

  // Look for the `OpenDcaV2` log message
  const logs = result.transaction.meta.logMessages || [];
  if (!logs.some(l => l.includes('OpenDcaV2'))) return;

  // loop through all instructions in the transaction to find the DCA instruction
  for (const ix of result.transaction.transaction.message.instructions) {
    if (ix.programId !== DCA_PROGRAM_ID) continue;

    try {
      // 1) decode binary payload
      const d = decodeOpenDcaV2(ix.data);

      // 2) fetch token symbols / decimals (cached)
      const [inMeta, outMeta] = await Promise.all([
        getMeta(ix.accounts[3]),   // input mint
        getMeta(ix.accounts[4])    // output mint
      ]);

      // 3) create a nice looking table
      console.table({
        user:        ix.accounts[2],
        pair:        `${inMeta.symbol}${outMeta.symbol}`,
        opened:      formatTimestamp(d.appIdx),
        'total in':  formatAmount(d.inAmount,  inMeta.decimals, inMeta.symbol),
        'per cycle': formatAmount(d.perCycle,  inMeta.decimals, inMeta.symbol),
        interval:    formatInterval(Number(d.interval))
      });
    } catch (e) {}
  }
});

ws.on('error', console.error);

ws.on('close', () => process.exit(1));

Example Notification

Monitoring new pump.fun tokens

const WebSocket = require('ws');

const KEY    = process.env.HELIUS_API_KEY ?? (() => { throw new Error('Set HELIUS_API_KEY'); })();
const WS_URL = `wss://mainnet.helius-rpc.com?api-key=${KEY}`;
const PUMP_FUN_PROG = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P';

/* ────────── 2.  OPEN WEBSOCKET & SUBSCRIBE ──────────────────── */
const ws = new WebSocket(WS_URL);

ws.on('open', () => {
  ws.send(JSON.stringify({
    jsonrpc : '2.0',
    id      : 1,
    method  : 'transactionSubscribe',
    params  : [
      { failed:false, accountInclude:[PUMP_FUN_PROG] },
      { commitment:'confirmed', encoding:'jsonParsed',
        transactionDetails:'full', maxSupportedTransactionVersion:0 }
    ]
  }));
  // ping every 10 s so we don't get dropped
  setInterval(() => ws.ping(), 10_000);
});

/* ────────── 3.  MESSAGE HANDLER ─────────────────────────────── */
ws.on('message', raw => {
  const payload = JSON.parse(raw);
  const result  = payload.params?.result;
  if (!result) return;

  const logs = result.transaction.meta.logMessages || [];
  // filter for the pump.fun "InitializeMint2" log
  if (!logs.some(l => l.includes('Instruction: InitializeMint2'))) return;

  const sig   = result.signature;    // transaction signature
  const keys  = result.transaction.transaction.message.accountKeys
                             .map(k => k.pubkey);
  //   keys[0] → creator wallet
  //   keys[1] → the new token
  console.table({
    tx:      sig,
    creator: keys[0],
    token:   keys[1]
  });
});

ws.on('error', console.error);
ws.on('close', () => process.exit(1));  

Example Notification