Skip to main content

ChainStream API

ChainStream APIs are a way to stream Solana transaction, slot, and block updates directly from the validator.

Introduction

The ChainStream APIs allow you to stream transaction, slot, and block updates in real-time using an RPC PubSub WebSocket. Why does this matter? Having the latest blocks, slot updates (whether they're processed, rooted or confirmed) and real-time processed commitment-level transactions streamed to your application is critical for some applications. This API intends to solve those needs by streaming this data in real-time.

Another key benefit of the ChainStream API is that it consolidates updates from multiple validators, ensuring you never miss a notification. This is especially important for applications requiring high availability and reliability. It also enables a “fastest wins” strategy: if one of our validators reports a transaction before the others, your subscription benefits from the lower latency.

info

The ChainStream API is available to all customers on the Scale Mode plan. Scale Mode comes with one ChainStream subscription and you can add up to nine (9) additional subscriptions for $0.14/hour each.

Enabling ChainStream API

In order to use the ChainStream API, you need to navigate to the ChainStream API page from the left-side navigation menu. Once there, click Enable ChainStream API.

Click enable to start using the ChainStream API
Click enable to start using the ChainStream API

Once enabled, you can select the number of streams you want to subscribe to. You can add up to 10 streams in total. Please note that one stream is included in the Scale Mode plan, and additional streams are billed at $0.14/hour.

Manage your ChainStream subscriptions
Manage your ChainStream subscriptions

Anatomy of a ChainStream API Subscription Request

ChainStream API uses the JSON RPC 2.0 Spec which you can check out here. Beyond the standard specification, each subscription request requires the network param, and conditionally supports the verified param:

1. network

The network you want to subscribe to. Currently, we support solana-mainnet, solana-devnet will be available soon.

2. verified

Whether or not you want to receive "verified" transactions. If you set this to true, you will only receive transactions that have been verified by ChainStream to have come from multiple validators. This adds a layer of data correctness to the transaction stream, and only applies when the specified commitment level for the stream is set to processed (more information about this here).

Please note: setting this to true can delay the notification by ~400ms in the worst case (until the given block is confirmed). verified is only supported when the specified commitment level is processed.

Setting this param to false will report the notification as quickly as possible without waiting for multiple validators to report the same notification.

Anatomy of a ChainStream API Notification

Every ChainStream API notification will contain the following:

1. subscription

This is the subscription ID that you received when you subscribed to the notification. You will need this to unsubscribe from the notification.

2. result

This is the actual notification. The contents of this will vary depending on the notification type. See below for more details.

Supported Subscription Types

The ChainStream API allows you to subscribe to the following notifications:

  1. Transaction Notifications
  2. Block Notifications
  3. Slot Notifications

Below we will cover how to subscribe to each notification type.

Transaction Notifications

Receive a notification anytime a transaction takes place on your specified network.

The verified parameter plays an important role in the transaction stream:

  • When set to true, ChainStream will wait for multiple validators to report the same transaction at the processed commitment level before sending a notification to you. This can act as an additional data correctness safeguard, allowing your services to guarantee that multiple validators supplying your infrastructure have executed the same transactions at the processed commitment level. While verified transactions are streamed out as soon as multiple validators report them, they can get delayed by up to ~400ms in the worst case. This is the approximate time taken for a block currently being processed to become confirmed.
  • When set to false, transactions are streamed to you as quickly as possible, without waiting for verification.
warning

Setting verified to true is only supported for the processed commitment level.

Filters

With ChainStream transaction notifications, you can optionally filter transactions by the following criteria:

  • excludeVotes - Exclude transactions that are votes from the stream. Default: false
  • accountKeys - Filter transactions by account keys. Default: null. Options include:
    • all - All the specified account keys must be present in the transaction
    • oneOf - At least one of the account keys must be present in the transaction
    • exclude - Exclude transactions that contain any of the specified account keys
  • commitment - The commitment level of the transactions reported ("processed", "confirmed", or "finalized"). More details about what each of the statuses mean is available here.

For accountKeys, if both all and oneOf are supplied, then ChainStream will report transactions that contain all keys in all and at least one key in oneOf. If exclude is supplied, then ChainStream will exclude transactions that contain any of the keys in exclude.

If no filters are supplied, you will receive all transactions at the processed commitment level.

Subscribe Request

Transaction Subscription without filters
{
"jsonrpc": "2.0",
"id": 123,
"method": "chainstream.transactionsSubscribe",
"params": {
"network": "solana-mainnet",
"verified": true,
"filter": {
"excludeVotes": false,
"commitment": "processed" // Can instead be "confirmed" or "finalized"
}
}
}
Subscription with filters applied
{
"jsonrpc": "2.0",
"id": 123,
"method": "chainstream.transactionsSubscribe",
"params": {
"network": "solana-mainnet",
"verified": false, // A commitment level other than "processed" requires this field to be false.
"filter": {
"excludeVotes": false,
"commitment": "confirmed", // Can instead be "processed" or "finalized".
"accountKeys": {
"all": [
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
],
"oneOf": [
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
],
"exclude": [
"Amf4jC8hM8uG4BP4stFHYUShTe7F4jKbiqvAL32D2piJ"
]
}
},
}
}

Subscribe Response

If successful, you should receive a response like below:

tip

Save the subscription ID from the response here, you will need this to unsubscribe in the future.

Transaction Subscription Response
{
"jsonrpc": "2.0",
"result": 446257156701906,
"id": 123
}

Notification

Example of periodic (streaming) chainstream.transactionNotification(s):

Note: The blockTime will be null for transactions whose slotStatus is processed. This is to be expected, as the block has not been confirmed by the cluster yet.

Example Transaction Notification
{
"jsonrpc": "2.0",
"method": "transactionNotification",
"params": {
"subscription": 2624171461871729,
"result": {
"context": {
"slotStatus": "confirmed",
"nodeTime": "2024-11-04T22:08:47.792192110Z",
"isVote": false,
"signature": "5c4YQSj4MaLsJov2kqSfXe9MDVMPhrkysLpbJnznYknWdhbQg1N1952ToSN7aoafERu8CCm2QYgfdED24JSx3qhR",
"index": 1273
},
"value": {
"blockTime": null,
"meta": {
"err": null,
"fee": 5000,
"innerInstructions": [
{
"index": 2,
"instructions": [
{
"programIdIndex": 15,
"accounts": [13, 5, 0],
"data": "3QhNgj3UueyD"
},
{
"programIdIndex": 15,
"accounts": [6, 1, 18],
"data": "3ikDtkPDq88o"
}
]
}
],
"loadedAddresses": {
"writable": [],
"readonly": [
"SysvarRent111111111111111111111111111111111",
"srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"
]
},
"logMessages": [
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]",
"Program log: Instruction: InitializeAccount",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 3443 of 799850 compute units",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success",
"Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 invoke [1]",
"Program log: ray_log: A0MwPFJqAgAAEIkgAAAAAAACAAAAAAAAAC31DloJuAEAwrnxoPOiMwCW0DEUEwEAAK8Y1QwAAAAA",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]",
"Program log: Instruction: Transfer",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4645 of 777525 compute units",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]",
"Program log: Instruction: Transfer",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4736 of 769899 compute units",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success",
"Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 consumed 32322 of 796407 compute units",
"Program 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 success",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]",
"Program log: Instruction: CloseAccount",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2915 of 764085 compute units",
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success"
],
"postBalances": [222282079, 0, 6124800, 23357760, 16258560, 2039280, 1181241562583, 3591360, 101977920, 101977920, 79594560, 2039280, 2039280, 2039280, 1, 934087680, 629815277275, 1141440, 11655234211, 0, 1009200, 1141440],
"postTokenBalances": [
{
"accountIndex": 5,
"mint": "5XoipYhJxKWgApXQiv4jLXeVWMTyzwPGXrbM8k6SQKsu",
"owner": "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "14537046742657541",
"decimals": 9,
"uiAmount": 14537046.74265754,
"uiAmountString": "14537046.742657541"
}
},
{
"accountIndex": 6,
"mint": "So11111111111111111111111111111111111111112",
"owner": "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "1181239523303",
"decimals": 9,
"uiAmount": 1181.239523303,
"uiAmountString": "1181.239523303"
}
},
{
"accountIndex": 11,
"mint": "5XoipYhJxKWgApXQiv4jLXeVWMTyzwPGXrbM8k6SQKsu",
"owner": "HsTxrH63NsVACjgWi7UWJtDiUmVE95vceRw3Gzvj46YD",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "0",
"decimals": 9,
"uiAmount": 0.0,
"uiAmountString": "0"
}
},
{
"accountIndex": 12,
"mint": "So11111111111111111111111111111111111111112",
"owner": "HsTxrH63NsVACjgWi7UWJtDiUmVE95vceRw3Gzvj46YD",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "0",
"decimals": 9,
"uiAmount": 0.0,
"uiAmountString": "0"
}
},
{
"accountIndex": 13,
"mint": "5XoipYhJxKWgApXQiv4jLXeVWMTyzwPGXrbM8k6SQKsu",
"owner": "7oGBxZHSPfB5hZmmPAxGjwUCF6z3mQfEBovYDRNVy8Eu",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "481169612391658",
"decimals": 9,
"uiAmount": 481169.612391658,
"uiAmountString": "481169.612391658"
}
}
],
"preBalances": [6995000, 0, 6124800, 23357760, 16258560, 2039280, 1181456854662, 3591360, 101977920, 101977920, 79594560, 2039280, 2039280, 2039280, 1, 934087680, 629815277275, 1141440, 11655234211, 0, 1009200, 1141440],
"preTokenBalances": [
{
"accountIndex": 5,
"mint": "5XoipYhJxKWgApXQiv4jLXeVWMTyzwPGXrbM8k6SQKsu",
"owner": "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "14534391073192386",
"decimals": 9,
"uiAmount": 14534391.073192386,
"uiAmountString": "14534391.073192386"
}
},
{
"accountIndex": 6,
"mint": "So11111111111111111111111111111111111111112",
"owner": "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "1181454815382",
"decimals": 9,
"uiAmount": 1181.454815382,
"uiAmountString": "1181.454815382"
}
},
{
"accountIndex": 11,
"mint": "5XoipYhJxKWgApXQiv4jLXeVWMTyzwPGXrbM8k6SQKsu",
"owner": "HsTxrH63NsVACjgWi7UWJtDiUmVE95vceRw3Gzvj46YD",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "0",
"decimals": 9,
"uiAmount": 0.0,
"uiAmountString": "0"
}
},
{
"accountIndex": 12,
"mint": "So11111111111111111111111111111111111111112",
"owner": "HsTxrH63NsVACjgWi7UWJtDiUmVE95vceRw3Gzvj46YD",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "0",
"decimals": 9,
"uiAmount": 0.0,
"uiAmountString": "0"
}
},
{
"accountIndex": 13,
"mint": "5XoipYhJxKWgApXQiv4jLXeVWMTyzwPGXrbM8k6SQKsu",
"owner": "7oGBxZHSPfB5hZmmPAxGjwUCF6z3mQfEBovYDRNVy8Eu",
"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"uiTokenAmount": {
"amount": "483825281856813",
"decimals": 9,
"uiAmount": 483825.281856813,
"uiAmountString": "483825.281856813"
}
}
],
"rewards": []
},
"slot": 299532711,
"transaction": {
"message": {
"accountKeys": [
"7oGBxZHSPfB5hZmmPAxGjwUCF6z3mQfEBovYDRNVy8Eu",
"2zdYoDBKdTVqSJHpbQz3cne6Y6ekgd4vkV46Je73huWg",
"78Et9sj482ird1EGohdbLmbjUgdXJvJTKEP3UZmgZiCz",
"BEwvDdPchqu8s1wVMpV3aWdyg6Ssq5ZzkJEoz2DqfDTB",
"4Eaw1Aio4qrrMDNLYMJHMyFvF64w4V77DctSk9qq5VZc",
"CDdLaah6wbU7FTDenytjfwmq5tDhib22h6RmbbBGP99v",
"CeAkcWfX649NUyY9fF1jho7CyT6K4nqgaTLT4jqEWvxJ",
"9rGQpBmF8LvNiTLN12kSLsuzCvkXVhMu3gyNHoFKutZf",
"3z29eeGusomTC51MjYMsxyYGBsLNoiyi5G5cPawKfVBF",
"RLxRE9UAX7fjUNjirqk3Cx3yxjn2sjhKhJByW7YGQNr",
"GrECgLGLuUJRdaktWbDnUtxiwC7jSc8BNTswV86XxUqP",
"EzCAUXJqM3epn3R7XPTf8fJdAP9oF3zQdpKpSZpjMLYS",
"3aetj18Lgu3LkXqpNf79Tywbs796NEKm59wn3Wpwf5Vz",
"9KcbBAHvC1FvJMBwarUDoQPGARiXkc26Ub8Ezv8sHfwM",
"11111111111111111111111111111111",
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"So11111111111111111111111111111111111111112",
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
"5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1",
"HsTxrH63NsVACjgWi7UWJtDiUmVE95vceRw3Gzvj46YD",
"SysvarRent111111111111111111111111111111111",
"srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"
],
"addressTableLookups": [
{
"accountKey": "2immgwYNHBbyVQKVGCEkgWpi53bLwWNRMB5G2nbgYV17",
"writableIndexes": [],
"readonlyIndexes": [
5,
11
]
}
],
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 6,
"numRequiredSignatures": 1
},
"instructions": [
{
"programIdIndex": 14,
"accounts": [0, 1],
"data": "3ipZWnW2QusN5VeXrVgFNDPNLwhpSmoFUzEX1nAEHWGCaxt6FEUyVLBsNwB4vxTbxmDKzoVS4bqwhrt9CcED7W7eABQDKUL3dMiq5cGiGgLrAsrbWX2mZCa6s6n3PRjKacZwsbr2ENSdsmJjntjRyQdrJnaqonC2odi9EAWr4"
},
{
"programIdIndex": 15,
"accounts": [
1,
16,
0,
20
],
"data": "2"
},
{
"programIdIndex": 17,
"accounts": [15, 2, 18, 3, 4, 5, 6, 21, 7, 8, 9, 10, 11, 12, 19, 13, 1, 0],
"data": "63ofBqds761vkGwR1CQuwJX"
},
{
"programIdIndex": 15,
"accounts": [1, 0, 0],
"data": "A"
}
],
"recentBlockhash": "HPUYxrKqzEGokksxcnUqigAj3JyjHgLNTutLBuP61rmU"
},
"messageHash": "AGzikmCB2oH19mkC24LiezsnYBPcpndqh7qPjYtbExcq",
"signatures": [
"5c4YQSj4MaLsJov2kqSfXe9MDVMPhrkysLpbJnznYknWdhbQg1N1952ToSN7aoafERu8CCm2QYgfdED24JSx3qhR"
]
}
}
}
}
}

Unsubscribe

Unsubscribe using subscription ID, which can be found in each notification from the stream:

Transactions Unsubscribe Request
{
"jsonrpc": "2.0",
"id": 123,
"method": "chainstream.transactionsUnsubscribe",
"params": [446257156701906]

}

Block Notifications

Receive a notification every time a new block is processed by a validator.

Subscription Request

Block Subscription Request
{
"jsonrpc": "2.0",
"id": 5,
"method": "chainstream.blocksSubscribe",
"params": {
"network": "solana-mainnet",
"verified": false
}
}

Subscribe Response

If successful, you should receive a response like below:

tip

Save the subscription ID from the response here, you will need this to unsubscribe in the future.

Block Subscription Response
{
"jsonrpc": "2.0",
"result": 32026100100140,
"id": 123
}

notifications

Example of periodic (streaming) block notifications:

Result:

  • context: this object contains contextual data related to the block notification
  • value: a block notification. See an example notification below for a list of fields returned.
Example Block notification
{
"jsonrpc": "2.0",
"method": "blockNotification",
"params": {
"subscription": 677140260093566,
"result": {
"context": {
"nodeTime": "2025-01-16T22:58:33.277897128Z"
},
"value": {
"slot": 314455074,
"blockhash": "FFeM4Fke7GtPSUcmH46XeGnoNovNvMf19etAVapZTE9y",
"rewards": [
{
"pubkey": "Kbh3h3FGh9sGRPpUtJzX1nfgBN2NGnnBBwVMLBtnZ3fQF4yv2VZm2SSo9GHA",
"lamports": 22117888,
"postBalance": 13183925405,
"rewardType": 0,
"commission": null
}
],
"blockTime": "2025-01-16T22:58:32Z",
"blockHeight": 292758780,
"parentSlot": 314455073,
"parentBlockhash": "3UnmvYokKs9q6YSGZ62wowfL3YRW2FrsHTZm4zEYNHvx",
"executedTransactionCount": 1958,
"entryCount": null,
"numPartitions": null
}
}
}
}

Unsubscribe

Unsubscribe using subscription ID, which can be found in each notification from the stream:

Blocks Unsubscribe Request
{
"jsonrpc": "2.0",
"id": 123,
"method": "chainstream.blocksUnsubscribe",
"params": [677140260093566]
}

Slot Notifications

Receive a notification anytime a slot is updated by a validator.

Subscription Request

Slot Subscription Request
{
"jsonrpc": "2.0",
"id": "123",
"method": "chainstream.slotUpdatesSubscribe",
"params": {
"network": "solana-mainnet",
"verified": true
}
}

Subscription Response

If successful, you should receive a response like below:

tip

Save the subscription ID from the response here, you will need this to unsubscribe in the future.

Slot Update Subscription Response
{
"jsonrpc": "2.0",
"result": 6296444419224395,
"id": 123
}

Notification

Receive a notification anytime a slot is updated by the validator.

Slot Update Notification
{
"jsonrpc": "2.0",
"method": "slotUpdateNotification",
"params": {
"subscription": 6296444419224395,
"result": {
"context": {
"nodeTime": "2023-11-28T20:47:23.363515459Z"
},
"value": {
"slot": 232822534,
"parent": 232822533,
"status": "processed"
}
}
}
}

Unsubscribe

Unsubscribe to slot updates using chainstream.slotUpdatesUnsubscribe method:

Slot Updates Unsubscribe Request
{
"jsonrpc": "2.0",
"id": 123,
"method": "chainstream.slotUpdatesUnsubscribe",
"params": [6296444419224395]
}

Using the ChainStream APIs

info

Make sure you have access. To use the ChainStream API you'll need to be subscribed to our Scale Mode plan and enable ChainSteam in the platform.

Establish a WebSocket connection to the ChainStream API endpoint:

This endpoint is different than the Syndica RPC endpoint available on the platform.

wss://api.syndica.io/

Example of sending JSON RPC messages via WebSocket client in Node.JS

const url = 'wss://api.syndica.io/'
const connection = new WebSocket(url)

const slotUpdatesSubscribeReq = {
jsonrpc: '2.0',
id: '123',
method: 'chainstream.slotUpdatesSubscribe',
params: {},
}

connection.onopen = () => {
connection.send(JSON.stringify(slotUpdatesSubscribeReq))
}

connection.onerror = (error) => {
console.log(`WebSocket error: ${error}`)
}

connection.onmessage = (e) => {
console.log(e.data)
// {
// "jsonrpc": "2.0",
// "method": "chainstream.slotUpdateNotification",
// "params": {
// "subscription": "09c63354-70b2-4e4b-8dcb-45c9a981d54f",
// "result": {
// "slot": 150867846,
// "parent": 150867845,
// "status": "processed" // or: "confirmed" & "finalized"
// }
// }
// }
}

Timeouts

There is a five (5) second timeout between creating a WebSocket connection (initial HTTP 101 connection upgrade request) and initiating a subscription (the actual JSON WebSocket RPC request). Clients must send the JSON RPC request within this 5 second window after opening the connection. This is often taken care of for you by most WebSocket client implementations, but tools such as Postman separate out these stages for testing purposes.

Once a subscription has been established there is a sixty (60) second timeout for messages. In other words, if the subscription has no traffic over any 60 second window, the subscription is automatically closed. To avoid this, clients must send PING messages periodically (for example, once every 30 seconds) to keep the subscription alive during periods with no notifications.

WebSocket example with ping
import WebSocket from "ws";

const TOKEN = "Your token here!";

const wsUrl = `wss://api.syndica.io/api-token/${TOKEN}`;
const ws_transactions: WebSocket = new WebSocket(wsUrl);

var n_tx = 0;

ws_transactions.on("open", function open(): void {
console.log("connected to Chainstream API");

// Send the specified request to subscribe.
// The request's filter below is intentionally set to produce no/very low volume of messages,
// keeping this subscription alive requires the client to send PING messages.
ws_transactions.send(
JSON.stringify({
jsonrpc: "2.0",
id: 123,
method: "chainstream.transactionsSubscribe",
params: {
network: "solana-mainnet",
verified: false,
filter: {
accountKeys: {
all: [
"BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY",
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
"SysvarRent111111111111111111111111111111111",
],
},
},
},
})
);

// Send PING messages every 30s.
setInterval(() => {
ws_transactions.ping();
}, 30000);
});

ws_transactions.on("message", function incoming(data: WebSocket.Data): void {
// The first message is always the subscription result.
if (n_tx == 0) {
console.log("transaction subscription result", JSON.parse(data.toString()));
} else {
// Deserialize.
let tx = JSON.parse(data.toString());

// Add a timestamp field and log out the transaction signature.
tx.utc_timestamp = new Date().toUTCString();
let sig = tx.params.result.context.signature;
let ts = tx.utc_timestamp;
console.log(`[${ts}] Transaction ${sig} received`);
}

n_tx++;
});

ws_transactions.on("close", function close(): void {
console.log("Disconnected from the server.");
});

ws_transactions.on("error", function error(err: Error): void {
console.error("WebSocket error:", err);
});