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.
Release files for decoded-shredstream 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| decoded_shredstream-0.1.1.tar.gz | 26.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| decoded_shredstream-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.3 kB
Release files / decoded_shredstream-0.1.1.tar.gz
| Download URL | decoded_shredstream-0.1.1.tar.gz |
|---|---|
| Size | 26.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0ffc866b2dad9db1c3d3197839c0a53b8f87ba207ded86c8c5d8a0992347b98c
|
|
BLAKE2b-256 checksum How to use checksums |
d5c47fb0d71d532aec887feadb2317e61c498f9202cc86e3ccef9fe4c98540de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Release files / decoded_shredstream-0.1.1-py3-none-any.whl
| Download URL | decoded_shredstream-0.1.1-py3-none-any.whl |
|---|---|
| Size | 28.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
da6a2c73f033bc7ae00444971dbe5b220812a9cae87fc4a7af8ebc504f1ce390
|
|
BLAKE2b-256 checksum How to use checksums |
7f805960b4b0edef712cbdae06cdf6194b1685167baa3d93c84aafc6635f5822
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|