> For the complete documentation index, see [llms.txt](https://docs.rpcfast.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rpcfast.com/rpc-fast-saas-solana/data-streaming/txstream.md).

# Aperture TxStream

TxStream is RPC Fast's purpose-built gRPC protocol for receiving decoded Solana transactions directly from the shred pipeline. It is a highly flexible protocol with transaction filtering, real-time transaction simulation, signatures-only responses, and batch transaction delivery.

**We reinvented shred-level transaction streaming:** RPC Fast reconstructs, recovers, deshreds, decodes, filters, and streams transactions through one optimised path, before normal validator replay metadata is available.

{% hint style="success" %}
Use TxStream when transaction arrival time matters more than confirmed post-execution metadata.
{% endhint %}

{% hint style="info" %}
For a limited time, Aperture TxStream is also available on the **Stream plan** – without transaction execution simulation. The full version with real-time simulation is included in the Aperture plan.

Starting **September 1,** the Aperture TxStream protocol will become **exclusive to the Aperture plan**.
{% endhint %}

## Why Use TxStream?

* **Faster transaction delivery.** The protocol is optimised specifically for transaction streaming, with compact payload and batch delivery modes.
* **Lower client overhead.** Transactions arrive decoded, with no raw shred or Solana Entry reconstruction required in your application.
* **Server-side filters.** Filter by vote status, signature, included accounts, excluded accounts, or required accounts before data crosses the network.
* **Compact modes.** Use `signatures_only` for timing and monitoring workloads, or batch transaction delivery to reduce per-message gRPC overhead.
* **Resolved Address Lookup Tables.** RPC Fast was one of the first providers to resolve ALTs for deshredded transactions. Loaded writable and read-only addresses are included when available and participate in account filters.
* **Real-time transaction simulation.** RPC Fast is the first provider to offer real-time simulation of deshredded transactions. Transaction-status simulation achieves approximately 95% accuracy compared with actual execution results.

## Performance Comparisons

RPC Fast compares identical transaction signatures received at the same observer. The comparison uses local receive timestamps.

### TxStream vs ShredStream

This comparison measures when the decoded transaction becomes available to the client. TxStream delivered the decoded transaction first in most matched races.

| TxStream mode            | TxStream arrived first | TxStream median lead when first | TxStream p90 lead when first |
| ------------------------ | ---------------------: | ------------------------------: | ---------------------------: |
| Signatures only          |                  77.6% |                          231 us |                       531 us |
| Full transaction payload |                  75.6% |                          197 us |                       463 us |

### TxStream vs Aperture Yellowstone interface

| TxStream mode            | TxStream arrived first | TxStream median lead when first | TxStream p90 lead when first |
| ------------------------ | ---------------------: | ------------------------------: | ---------------------------: |
| Signatures only          |                  96.3% |                          280 us |                       641 us |
| Full transaction payload |                  93.8% |                          232 us |                       509 us |

### TxStream vs Yellowstone gRPC

| TxStream mode            | TxStream arrived first | TxStream median lead when first | TxStream p90 lead when first |
| ------------------------ | ---------------------: | ------------------------------: | ---------------------------: |
| Signatures only          |                 99.97% |                         7.77 ms |                     22.35 ms |
| Full transaction payload |                 99.97% |                         7.72 ms |                     22.28 ms |

## Protocol Reference

Full protocol reference can be found at <https://github.com/dysnix/aperture-grpc-proto>

TxStream uses the `aperture.Aperture` gRPC service and a single request format for both delivery modes.

### Request Format

Both subscription methods accept `SubscribeTransactionsRequest`:

| Field                | Type             | Default           | Description                                                               |
| -------------------- | ---------------- | ----------------- | ------------------------------------------------------------------------- |
| `vote`               | `VoteFilter`     | `VOTE_FILTER_ALL` | Stream all transactions, votes only, or non-votes only.                   |
| `signature`          | `bytes`          | empty             | Match one 64-byte primary transaction signature.                          |
| `account_include`    | repeated `bytes` | empty             | Match when any listed 32-byte static or loaded account is present.        |
| `account_exclude`    | repeated `bytes` | empty             | Reject when any listed 32-byte static or loaded account is present.       |
| `account_required`   | repeated `bytes` | empty             | Match only when every listed 32-byte static or loaded account is present. |
| `signatures_only`    | `bool`           | `false`           | Return only transaction identity and timing fields.                       |
| `include_simulation` | `bool`           | `false`           | Add a transaction-status simulation result to each response.              |

Different filter fields are combined with logical **AND**. Values inside `account_include` and `account_exclude` use **OR**, while every `account_required` value must match. With no filters, TxStream delivers all transactions.

### Non-Batch and Batch Delivery

<table><thead><tr><th width="236.21875"></th><th width="269.11328125">Non-batch mode</th><th width="246.015625">Batch transaction delivery</th></tr></thead><tbody><tr><td>RPC</td><td><code>/aperture.Aperture/SubscribeTransactions</code></td><td><code>/aperture.Aperture/SubscribeTransactionBatches</code></td></tr><tr><td>Response stream</td><td><code>DecodedTransaction</code></td><td><code>DecodedTransactionBatch</code></td></tr><tr><td>Delivery behavior</td><td>Emits each matching transaction in its own gRPC message.</td><td>Immediately emits available matching transactions, up to 64 per batch, without waiting for the batch to fill.</td></tr><tr><td>Best suited for</td><td>Minimum transaction-by-transaction delivery delay.</td><td>High-throughput consumers that want fewer protobuf and gRPC messages.</td></tr><tr><td>Ordering without simulation</td><td>Transactions are delivered in stream order, with indexes <code>0, 1, 2, …</code>.</td><td>Transactions remain ordered within each batch and across consecutive batches.</td></tr><tr><td>Ordering with simulation</td><td>Transactions are delivered as soon as their simulations complete. Delivery may therefore be out of order—for example, <code>1, 2, 0</code>—while <code>index</code> preserves the original stream order.</td><td>Transaction order is preserved. A batch is delivered only after all simulations in that batch finish, so its latency is determined by the slowest simulation.</td></tr></tbody></table>

The same filters, `signatures_only` mode, and simulation option are available in both delivery modes.

{% hint style="success" icon="circle-info" %}
Use the non-batch stream when the lowest possible simulation latency is the priority. Use the batch stream when preserving delivery order is more important.
{% endhint %}

### Transaction Response Format

`DecodedTransaction` contains:

| Field                       | Type                           | Description                                               |
| --------------------------- | ------------------------------ | --------------------------------------------------------- |
| `slot`                      | `uint64`                       | Solana slot containing the transaction.                   |
| `index`                     | `uint64`                       | Transaction index within the subscription.                |
| `is_vote`                   | `bool`                         | Whether this is a vote transaction.                       |
| `created_at_unix_nanos`     | `uint64`                       | Transaction stream timestamp in Unix nanoseconds.         |
| `signatures`                | repeated `bytes`               | Transaction signatures; each signature is 64 bytes.       |
| `version`                   | `TransactionVersion`           | `TRANSACTION_VERSION_LEGACY` or `TRANSACTION_VERSION_V0`. |
| `header`                    | `MessageHeader`                | Required-signature and read-only-account counts.          |
| `static_account_keys`       | repeated `bytes`               | Static 32-byte account keys from the transaction message. |
| `loaded_writable_addresses` | repeated `bytes`               | Resolved writable 32-byte ALT addresses.                  |
| `loaded_readonly_addresses` | repeated `bytes`               | Resolved read-only 32-byte ALT addresses.                 |
| `recent_blockhash`          | `bytes`                        | The transaction's 32-byte recent blockhash.               |
| `instructions`              | repeated `CompiledInstruction` | Compiled transaction instructions.                        |
| `simulation`                | `TransactionSimulation`        | Present when `include_simulation` is enabled.             |

`MessageHeader` contains `num_required_signatures`, `num_readonly_signed_accounts`, and `num_readonly_unsigned_accounts`. `CompiledInstruction` contains `program_id_index`, the instruction's account indexes, and its raw instruction data.

Instruction account indexes refer to the following concatenated key list:

`static_account_keys + loaded_writable_addresses + loaded_readonly_addresses`

When `signatures_only` is enabled, responses retain `slot`, `index`, `is_vote`, `created_at_unix_nanos`, `signatures`, `version`, and an optional simulation result. Transaction message fields are omitted.

### Batch Response Format

`DecodedTransactionBatch` contains one field:

| Field          | Type                          | Description                                               |
| -------------- | ----------------------------- | --------------------------------------------------------- |
| `transactions` | repeated `DecodedTransaction` | One to 64 transactions matching the subscription request. |

Each transaction has the same format as a non-batch response.

### Simulation Response Format

When requested, `TransactionSimulation` contains:

| Field                     | Type               | Description                                      |
| ------------------------- | ------------------ | ------------------------------------------------ |
| `bank_slot`               | optional `uint64`  | Slot used for the simulation result.             |
| `status`                  | `SimulationStatus` | Simulation outcome.                              |
| `error`                   | optional `string`  | Error details when simulation is not successful. |
| `simulated_at_unix_nanos` | `uint64`           | Simulation timestamp in Unix nanoseconds.        |
| `processing_time_nanos`   | `uint64`           | Simulation processing duration in nanoseconds.   |

`SimulationStatus` values:

<table data-search="false"><thead><tr><th>Status</th><th>Meaning</th></tr></thead><tbody><tr><td><code>SIMULATION_STATUS_SUCCEEDED</code></td><td>The simulation succeeded.</td></tr><tr><td><code>SIMULATION_STATUS_FAILED</code></td><td>The simulation failed.</td></tr><tr><td><code>SIMULATION_STATUS_INVALID_TRANSACTION</code></td><td>The transaction is invalid.</td></tr><tr><td><code>SIMULATION_STATUS_BANK_NOT_AVAILABLE</code></td><td>The required Bank state was unavailable. The transaction may still be valid, but no simulation outcome is available.</td></tr><tr><td><code>SIMULATION_STATUS_SERVER_OVERLOADED</code></td><td>Simulation exceeded its time limit. RPC Fast limits simulation time to preserve TxStream delivery performance.</td></tr><tr><td><code>SIMULATION_STATUS_INTERNAL_ERROR</code></td><td>An internal error occurred during simulation.</td></tr></tbody></table>

## Use the Official Rust Client

As a best practice, use the official [`dysnix/aperture-grpc-client`](https://github.com/dysnix/aperture-grpc-client). It provides byte-safe filters, tuned HTTP/2 flow control, TLS native roots, keepalives, `X-Token` authentication, and automatic reconnection.

Set `APERTURE_X_TOKEN` to your RPC Fast token before running the example.

```rust
use aperture_grpc_client::{
    ApertureClientConfig, ApertureGrpcClient, SubscribeFilters, VoteFilter,
};
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let token = std::env::var("APERTURE_X_TOKEN")?;
    let config = ApertureClientConfig::new("https://aperture-txstream.rpcfast.com:443")
        .with_x_token(token);
    let client = ApertureGrpcClient::new(config);

    let filters = SubscribeFilters::default().vote(VoteFilter::NonVoteOnly);
    let mut stream = Box::pin(client.subscribe_with_reconnect(filters));

    while let Some(message) = stream.next().await {
        let transaction = message?;
        println!(
            "slot={} index={} signatures={}",
            transaction.slot,
            transaction.index,
            transaction.signatures.len()
        );
    }

    Ok(())
}
```

An empty filter streams all transactions. Apply the narrowest server-side filter that serves your workload to reduce bandwidth and client-side work.

## Request Real-Time Simulation

Set the client's `include_simulation` option when you need a predicted result for every matching deshredded transaction.

```rust
use aperture_grpc_client::{
    ApertureClientConfig, ApertureGrpcClient, SimulationStatus, SubscribeFilters,
    VoteFilter,
};
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let token = std::env::var("APERTURE_X_TOKEN")?;
    let config = ApertureClientConfig::new("https://aperture-txstream.rpcfast.com:443")
        .with_x_token(token);
    let client = ApertureGrpcClient::new(config);

    let filters = SubscribeFilters::default()
        .vote(VoteFilter::NonVoteOnly)
        .include_simulation();
    let mut stream = Box::pin(client.subscribe_with_reconnect(filters));

    while let Some(message) = stream.next().await {
        let transaction = message?;
        let Some(simulation) = transaction.simulation else {
            continue;
        };
        let status = SimulationStatus::try_from(simulation.status)
            .unwrap_or(SimulationStatus::Unspecified);

        println!(
            "slot={} status={status:?} bank_slot={:?} error={:?}",
            transaction.slot,
            simulation.bank_slot,
            simulation.error
        );
    }

    Ok(())
}
```

Simulation results can include:

* simulation status and error
* simulation slot
* simulation timestamp and processing time

{% hint style="warning" %}
Enabling simulation adds latency. Simulation results are predictive and may differ from the transaction's eventual execution outcome; transaction-status simulation accuracy is approximately 95% compared with actual execution results.
{% endhint %}

Clients that do not request simulation remain on the immediate pre-execution path. For a compact prediction feed, combine `include_simulation` with `signatures_only`.

### Simulation Delivery Latency

The matched-signature comparison measured simulation-enabled TxStream against the equivalent non-simulation stream:

| TxStream mode            | Median added delivery time | p90 added delivery time |
| ------------------------ | -------------------------: | ----------------------: |
| Signatures only          |                     832 us |                 1.72 ms |
| Full transaction payload |                     791 us |                 1.62 ms |

## Receive Transaction Batches

Use the batch stream when throughput and lower protobuf/gRPC overhead matter more than emitting each transaction in its own message.

```rust
use aperture_grpc_client::{
    ApertureClientConfig, ApertureGrpcClient, SubscribeFilters, VoteFilter,
};
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let token = std::env::var("APERTURE_X_TOKEN")?;
    let config = ApertureClientConfig::new("https://aperture-txstream.rpcfast.com:443")
        .with_x_token(token);
    let client = ApertureGrpcClient::new(config);
    let filters = SubscribeFilters::default()
        .vote(VoteFilter::NonVoteOnly)
        .signatures_only();
    let mut stream = Box::pin(client.subscribe_batches_with_reconnect(filters));

    while let Some(message) = stream.next().await {
        let batch = message?;
        println!("transactions={}", batch.transactions.len());
    }

    Ok(())
}
```

## Important Semantics

TxStream is an early transaction feed, not a final source of execution truth.

* A streamed transaction can fail, never confirm, or land on a fork that does not survive.
* The normal pre-execution stream does not contain confirmed balances, inner instructions, rewards, execution status, or final program logs.
* ALT addresses are emitted when resolved. You must tolerate unresolved addresses and must not treat the stream as authoritative replay state.
* Reconnect after transport failures. The official client does this automatically with exponential backoff.

Use [Yellowstone gRPC](/rpc-fast-saas-solana/data-streaming/yellowstone-grpc.md) when you need confirmed execution metadata, account updates, blocks, or other replay-derived data.

## Test TxStream with grpcurl

Download the official [`txstream.proto`](https://raw.githubusercontent.com/dysnix/aperture-grpc-proto/main/proto/aperture/txstream.proto) file to your current directory, set `APERTURE_X_TOKEN`, and start a non-vote transaction stream:

```bash
grpcurl \
  -proto txstream.proto \
  -H "x-token: ${APERTURE_X_TOKEN}" \
  -d '{
    "vote": "VOTE_FILTER_NON_VOTE_ONLY",
    "signaturesOnly": true
  }' \
  aperture-txstream.rpcfast.com:443 \
  aperture.Aperture/SubscribeTransactions
```

Set `"includeSimulation": true` in the request body to test the simulation-enabled stream.

## When to Use TxStream

The use cases can be split into two categories:

1/ **Decoded, reconstructed transactions –** better developer experience, less engineering work.

Key benefit: less custom infrastructure, faster integration, and no need to decode shreds or reconstruct transactions yourself.

Best for

* **HFT bot developers** looking for the fastest path to production with minimal engineering effort.
* **Trading platforms** that need transaction parsing without maintaining their own decoder.
* **Wallets** tracking pending user transactions.
* **Analytics & monitoring platforms** consuming structured real-time transaction data.
* **Copy trading platforms** monitoring market activity before block confirmation.
* **Solana developers** who need pre-confirmation data without building a shred reconstruction pipeline.

2/ **Real-time execution simulation** – decision-making edge for latency-sensitive applications).

Key benefit: fewer wasted transactions, fewer false positives, lower compute costs, and faster decision-making under tight latency constraints.

Best for

* **MEV searchers** filtering profitable opportunities from transactions that will actually execute.
* **Arbitrage bots** avoiding failed swaps and reducing false-positive signals.
* **Liquidation bots** confirming liquidation candidates before committing capital.
* **High-frequency trading (HFT) systems** making routing decisions with higher confidence.
* **Market makers** reacting only to executable market events.
* **Copy-trading platforms** replicating only transactions likely to succeed.
* **Risk engines** estimating execution outcomes before block inclusion.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.rpcfast.com/rpc-fast-saas-solana/data-streaming/txstream.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
