> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Use preprocessedSubscribe

> Stream pre-execution Solana transactions over WebSocket with the preprocessedSubscribe method — subscribe, filter by account, and decode binary payloads.

<Note>
  **Public Beta.** `preprocessedSubscribe` is available on **all paid plans**
  and is metered at **0.1 credits per message** (one message per delivered
  transaction).
</Note>

## What is `preprocessedSubscribe`?

`preprocessedSubscribe` is a Helius WebSocket method that streams preprocessed transactions — pre-execution Solana transactions delivered **before they reach the `processed` commitment level**. Helius aggregates multiple pre-execution sources — primarily shreds decoded directly as they arrive at the validator, supplemented by [preconfirmation](/docs/pre-confirmations/overview) signals — and delivers them as a single deduplicated stream of compact binary messages, with no deshredding infrastructure on your side.

Transactions sourced from preconfirmation signals arrive later on this feed than on the dedicated [Preconfirmations](/docs/pre-confirmations/overview) product, which remains the earliest access to them.

It is the successor to the earlier preprocessed LaserStream (gRPC) product. If you consume preprocessed transactions over gRPC today, switch to this method — it delivers the same class of data over a plain WebSocket connection at lower latency, and the gRPC delivery will be deprecated.

| Stream                                                                        | Relative timing                                      | Coverage                                           | Data                                                                                                 |
| ----------------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| [Preconfirmations](/docs/pre-confirmations/overview)                               | Earliest                                             | Transactions scheduled by participating validators | Transaction and execution status (Helius preconfirmations only; BAM preconfirmations report unknown) |
| `preprocessedSubscribe`                                                       | Typically after Preconfirmations, before `processed` | Broad Solana transaction coverage                  | Signed transaction before execution                                                                  |
| [`transactionSubscribe`](/docs/rpc/websocket/transaction-subscribe) at `processed` | After execution                                      | Processed transactions                             | Transaction with execution metadata                                                                  |

<Warning>
  `preprocessedSubscribe` is a **best-effort, pre-execution signal**, not a
  commitment level. A streamed transaction can fail, be dropped, or land on a
  different fork. Reconcile against a processed or confirmed stream before
  treating it as final.
</Warning>

## Endpoint

`preprocessedSubscribe` is served from `wss://beta.helius-rpc.com` — the Helius Gatekeeper endpoint — rather than `mainnet.helius-rpc.com`. Authenticate with your API key as a query parameter:

```
wss://beta.helius-rpc.com/?api-key=<API_KEY>
```

Each API key is limited to **10 concurrent connections/subscriptions**.

## Subscribe

Send a JSON-RPC request with the `preprocessedSubscribe` method. `params` carries the account filters and is required — `accountInclude` and `accountRequired` must specify at least one account between them (see [Filtering](#filtering)):

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "preprocessedSubscribe",
  "params": {
    "accountInclude": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
    "accountExclude": [],
    "accountRequired": []
  }
}
```

The server acknowledges the subscription with a JSON text frame containing the subscription ID:

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "result": 1,
  "id": 1
}
```

After this acknowledgement, transaction updates arrive as **binary** WebSocket frames — see [Notification payload](#notification-payload).

## Filtering

Every subscription is scoped by the account filters in `params`. Filtering happens server-side, so you only receive the transactions you care about:

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "preprocessedSubscribe",
  "params": {
    "accountInclude": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
    "accountExclude": ["Vote111111111111111111111111111111111111111"],
    "accountRequired": []
  }
}
```

| Filter            | Match behavior                                                        |
| ----------------- | --------------------------------------------------------------------- |
| `accountInclude`  | Match when the transaction references **any** of the listed accounts. |
| `accountExclude`  | Drop the transaction if it references **any** of the listed accounts. |
| `accountRequired` | Match only when the transaction references **all** listed accounts.   |

Filter rules:

* The three filters are combined with AND logic.
* `accountInclude` and `accountRequired` must specify **at least one account** between them — there is no unfiltered full stream.
* Accounts are base58-encoded pubkeys. Each list accepts up to **5,000** addresses.

### Address lookup table (ALT) resolution

Account filters match more than the transaction's static account keys — Helius resolves [address lookup tables](/docs/glossary#address-lookup-table-alt) server-side, so `accountInclude`, `accountExclude`, and `accountRequired` also match accounts a transaction loads through an ALT. Just pass the account's pubkey; no need to maintain ALT mappings or resolve tables yourself.

## Notification payload

Notifications are delivered as **binary** WebSocket frames (not JSON). Each frame carries a single transaction in a packed byte layout:

| Bytes | Field         | Type                  | Description                                                                                              |
| ----- | ------------- | --------------------- | -------------------------------------------------------------------------------------------------------- |
| 0     | `version`     | `u8`                  | Payload schema version. Currently `1`.                                                                   |
| 1–8   | `slot`        | `u64` (little-endian) | The slot the transaction was observed in.                                                                |
| 9–72  | `signature`   | 64 bytes              | The transaction's first signature, in binary form.                                                       |
| 73+   | `transaction` | `bytes`               | The signed transaction in Solana wire format. See [Decoding the transaction](#decoding-the-transaction). |

Read the fixed 73-byte prefix in order, then decode the remaining bytes to read instructions, accounts, and address-table lookups. The signature is included in the prefix so you can identify and deduplicate a transaction without decoding the full transaction body.

Always read and check the `version` byte first. If Helius needs to update the payload format, the version will increment — branch on it so your decoder keeps working across schema changes.

### Decoding the transaction

The transaction bytes are forwarded exactly as observed on the network, in the standard wire encoding for the transaction's version. Legacy and v0 transactions use the signatures-first layout that `bincode` produces. Transaction v1 ([SIMD-0385](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0385-transaction-v1.md)) uses a message-first layout with signatures at the end, so `bincode` fails on v1 payloads. Use a decoder that handles every version:

* **Rust:** [`agave-transaction-view`](https://docs.rs/agave-transaction-view) parses legacy, v0, and v1 transactions in place, without an intermediate copy. This is the recommended option. [`wincode`](https://docs.rs/wincode), the bincode-compatible serializer used by current Solana SDKs, also decodes v1 into `VersionedTransaction`.
* **JavaScript / TypeScript:** make sure your library version supports transaction v1. Older `VersionedTransaction.deserialize` implementations only handle legacy and v0. Use `@solana/kit` 8.0+ or `@solana/web3.js` v3. See [Transaction v1 support](/docs/rpc/transaction-v1).

```rust theme={"system"}
use agave_transaction_view::transaction_view::TransactionView;

// `frame` is the full binary WebSocket message
let tx_bytes = &frame[73..];
let tx = TransactionView::try_new_unsanitized(tx_bytes)?;

println!("version: {:?}", tx.version()); // Legacy, V0, or V1
for ix in tx.instructions_iter() {
    println!("program index {}: {} bytes", ix.program_id_index, ix.data.len());
}
```

## Example

```javascript theme={"system"}
const WebSocket = require('ws');
const bs58module = require('bs58');
const bs58 = bs58module.default ?? bs58module;

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

ws.on('open', () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'preprocessedSubscribe',
    // Only Jupiter v6 transactions — accountInclude/accountRequired must
    // specify at least one account between them.
    params: {
      accountInclude: ['JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4'],
      accountExclude: [],
      accountRequired: []
    }
  }));

  // Keep the connection alive
  setInterval(() => ws.ping(), 30_000);
});

ws.on('message', (data, isBinary) => {
  // The subscribe acknowledgement arrives as a JSON text frame
  if (!isBinary) {
    const msg = JSON.parse(data.toString());
    if (msg.id === 1) console.log('Subscribed, ID:', msg.result);
    return;
  }

  // Notifications arrive as binary frames:
  // version (u8) | slot (u64 LE) | signature ([u8; 64]) | transaction bytes
  const buf = Buffer.from(data);
  const version = buf.readUInt8(0); // currently 1 — branch on this if it changes
  if (version !== 1) return; // unknown schema version; update your decoder
  const slot = buf.readBigUInt64LE(1);
  const signature = bs58.encode(buf.subarray(9, 73));
  const txBytes = buf.subarray(73); // transaction in Solana wire format (legacy, v0, or v1)

  console.log('Preprocessed transaction:', { slot, signature, bytes: txBytes.length });
  // Decode txBytes with a decoder that supports transaction v1 (see "Decoding the transaction")
});

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

## What data is available?

Each notification carries the signed transaction, its first signature, and its slot. Because delivery happens before execution, the stream does **not** include:

* Execution status or errors
* Pre/post balances or token balance changes
* Log messages or inner instructions
* Compute units consumed

Think of it as receiving the "proposal" without the "result" — you see what the sender tried to do, but not what actually happened. Account and program state updates don't exist yet at this stage either; if you need real-time account state, use [LaserStream gRPC](/docs/laserstream) at `processed` commitment.

## Backpressure

The stream does not buffer indefinitely for slow consumers. If your client reads too slowly and more than **4,000 messages** back up server-side, Helius closes the connection — you receive a clean WebSocket close frame. Drain frames faster than they arrive: keep heavy work such as transaction decoding and strategy logic off the receive loop, and reconnect and resubscribe after a disconnect.

## Delivery guarantees

Delivery is best-effort, not guaranteed, and there is no historical replay. Clients should:

1. Reconnect and resubscribe after a connection closes.
2. Deduplicate by transaction signature.
3. Treat the slot as an observation, not finality.
4. Reconcile against a processed or confirmed stream when execution results matter.

## Pricing

`preprocessedSubscribe` is available on **all paid plans** and metered at **0.1 credits per message** — one message per delivered transaction, billed from your plan. See [Credits](/docs/billing/credits) for details.

## Related

<CardGroup cols={2}>
  <Card title="Preconfirmations" icon="bolt" href="/docs/pre-confirmations/overview">
    Transactions streamed before they become shreds — the earliest transaction signal.
  </Card>

  <Card title="Raw Shreds (UDP)" icon="network-wired" href="/docs/shred-delivery/raw-shreds">
    Unprocessed shred packets over UDP. You implement the deshredding.
  </Card>

  <Card title="transactionSubscribe" icon="tower-broadcast" href="/docs/rpc/websocket/transaction-subscribe">
    Post-execution transactions with rich filtering and execution metadata.
  </Card>
</CardGroup>
