> ## 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 before processed commitment.

<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 scheduled-transaction ([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 product. If you consume [preprocessed transactions over gRPC](/docs/preprocessed-transactions/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 preconfirmation status |
| `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` | `bincode(VersionedTransaction)` | The signed transaction, bincode-serialized.        |

Read the fixed 73-byte prefix in order, then [`bincode`](https://docs.rs/bincode)-deserialize the remaining bytes into a `VersionedTransaction` 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.

## 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]) | bincode(VersionedTransaction)
  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); // bincode-serialized VersionedTransaction

  console.log('Preprocessed transaction:', { slot, signature, bytes: txBytes.length });
  // Deserialize txBytes (bincode) into a VersionedTransaction with your Solana tooling
});

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="Preprocessed Transactions (gRPC)" icon="binary" href="/docs/preprocessed-transactions/grpc">
    The same pre-execution data over gRPC. Will be deprecated in favor of this method.
  </Card>

  <Card title="Preconfirmations" icon="bolt" href="/docs/pre-confirmations/overview">
    Scheduled 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>
