Decoded ShredStream — Python client
Python client for the Decoded ShredStream of ShredStream.com: pre-execution
Solana transactions, decoded from shreds — the serialized
VersionedTransaction, its signatures and its slot, delivered over gRPC or
UDP push the moment they propagate.
Before execution — transactions carry no status, logs, balance changes or inner instructions, and some will fail on-chain. Use a post-execution source to confirm.
The client is synchronous and blocking: iterating it returns one transaction at a time on the calling thread.
pip install decoded-shredstream
from decoded_shredstream import Client, Filter, GrpcConfig
with Client.grpc(GrpcConfig(endpoint=endpoint, token=token,
filters={"all": Filter()})) as client:
for update in client:
print(update.slot, update.signature)
Requirements — Python 3.10 or later, and a Decoded ShredStream subscription on ShredStream.com. Extras:
solanaforparse(),fastfor a Rust base58.
With the extras:
pip install "decoded-shredstream[solana,fast]"
🔑 Access
Decoded ShredStream is a subscription product, available from ShredStream.com. One subscription covers both transports, and you can move from one to the other whenever you need to.
- gRPC — you receive an endpoint and an access token. Use the endpoint exactly as issued.
- UDP — you register your server's IP and port; datagrams are pushed to it.
Choosing a transport
Both carry the same data; they differ on what the protocol guarantees.
| gRPC | UDP | |
|---|---|---|
| Latency | higher | lowest |
| Delivery | ordered, retransmitted | best-effort, no retransmission |
| Server-side filters | yes | no — you receive the full stream |
⚡ Quickstart — gRPC
from decoded_shredstream import Client, Filter, GrpcConfig
with Client.grpc(
GrpcConfig(
endpoint="your-endpoint.shredstream.com:PORT",
token="YOUR_TOKEN",
filters={"all": Filter()},
)
) as client:
for update in client:
print(update.slot, len(update.data), list(update.filters))
Client.grpc connects and subscribes before returning.
📡 Quickstart — UDP
from decoded_shredstream import Client, UdpConfig
with Client.udp(UdpConfig(port=8002)) as client:
print("listening on", client.local_addr)
for update in client:
print(update.slot, len(update.data), update.signature.hex())
8002 is only an example: bind whichever port you registered in your account.
🔍 Transaction parsing
Every transaction exposes update.data, in the standard Solana wire format,
and its signatures without any decoding:
import base58
update.signature # first signature, raw 64 bytes
update.signatures # every signature
base58.b58encode(update.signature).decode() # to display one
Everything else is available through parse(), which returns a
solders.transaction.VersionedTransaction and requires the solana extra:
for update in client:
message = update.parse().message
keys = message.account_keys
programs = {str(keys[ix.program_id_index]) for ix in message.instructions}
print(update.slot, keys[0], len(message.instructions), sorted(programs))
Without the solana extra, parse() raises ImportError.
🎯 Filters
Filters exist on the gRPC transport only. They are evaluated by the server; the client never filters locally. UDP delivers the full stream.
A subscription carries a map of named filters. Each response is tagged
with the names that matched it, exposed as update.filters.
from decoded_shredstream import Client, Filter, GrpcConfig
client = Client.grpc(
GrpcConfig(
endpoint=endpoint,
token=token,
filters={
"watched": Filter(include=[account]),
"everything": Filter(),
},
)
)
Semantics
A Filter holds three lists of base58 account keys, matched against the
accounts a transaction touches. The three conditions are ANDed, and an empty
list adds no constraint — Filter() matches every transaction.
| List | Matches when the transaction |
|---|---|
include |
touches at least one of the accounts |
exclude |
touches none of the accounts |
required |
touches all of the accounts |
Matching uses the account keys carried in the transaction, signers included. Addresses resolved through an Address Lookup Table cannot be filtered on. A transaction matching several filters is delivered once.
Replacing filters mid-stream
update_filters replaces the whole map atomically. The server applies it
without a reconnect and without a gap in the data, and the new map is the one
any later reconnection re-sends.
client.update_filters({"watched": Filter(include=[account])})
🔄 Errors & reconnection
Recoverable interruptions never reach you: the client reconnects on its own and re-sends the current filter map. Only a refused token, a session closed by the server and a rejected filter map end the stream, raised once by the iteration:
from decoded_shredstream import StreamError
try:
for update in client:
...
except StreamError as e:
print("stream ended:", e)
Every exception, the backoff policy and the telemetry notices are in docs/errors.md.
📖 Documentation
This README is what you need to receive transactions. The rest lives beside it:
| Document | Contents |
|---|---|
| docs/api.md | Every type and method: clients, configuration, filters, updates, UDP codec, performance notes and counters |
| docs/errors.md | Error types, reconnection policy, telemetry notices |
💡 Examples
The examples/ directory contains runnable programs; each reads its
configuration from the environment.
| File | Shows |
|---|---|
udp_quickstart.py |
binding the registered port and printing transactions |
grpc_quickstart.py |
connecting, subscribing to everything, printing transactions |
grpc_filters.py |
named filters and replacing the map mid-stream |
parse_transaction.py |
full parsing with solders |
raw_bytes_pipeline.py |
forwarding update.data without parsing |
low_latency.py |
the shape of a minimal consumption loop |
DECODED_SHREDSTREAM_UDP_PORT=8002 python examples/udp_quickstart.py
DECODED_SHREDSTREAM_ENDPOINT=your-endpoint.shredstream.com:PORT DECODED_SHREDSTREAM_TOKEN=... python examples/grpc_quickstart.py
DECODED_SHREDSTREAM_ENDPOINT=your-endpoint.shredstream.com:PORT DECODED_SHREDSTREAM_TOKEN=... ACCOUNT=<base58> python examples/grpc_filters.py
⚖️ License
Apache-2.0. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file decoded_shredstream-0.1.0.tar.gz.
File metadata
- Download URL: decoded_shredstream-0.1.0.tar.gz
- Upload date:
- Size: 26.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5b980f0135060b48445b58b542e5babeba5f857404fe37ccf509ccf95c55402
|
|
| MD5 |
62cd69bc1a84817c95b2f70e05204105
|
|
| BLAKE2b-256 |
0e14bf2e5acdfb3c875d411580199e607b073c2d01e17d516d864bf1fe341b22
|
File details
Details for the file decoded_shredstream-0.1.0-py3-none-any.whl.
File metadata
- Download URL: decoded_shredstream-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92e8d7cd6d285282ededdb0e78a30a7acd0b14e9765643ff8f030293c191500b
|
|
| MD5 |
138fe4aae23c235454268ac6becf4e07
|
|
| BLAKE2b-256 |
06c0ea397496ca56244d064e939e5f13706ccd091089e3b0a98b81b398c814dc
|