Radion Rust SDK
Official, async-first, fully-typed Rust SDK for Radion. One client, one API key.
use StreamExt;
use ;
use Radion;
async
Features
- Unified client — one
Radion, one API key; product surfaces attach as fields. - Auto-reconnect — exponential backoff with jitter after unexpected drops.
- Subscription restore — subscriptions are replayed automatically on reconnect.
- Heartbeats — periodic pings with stale-connection detection.
- Typed end-to-end — every frame, channel, filter, and payload is statically
typed; events stream as a typed
Payloadenum you canmatchexhaustively. - Async-first — built on
tokio; events are aStream.
Requirements
Rust 1.85 or later.
Install
Cargo features:
| Feature | Default | Description |
|---|---|---|
realtime |
✅ | The WebSocket product surface. |
rustls |
✅ | rustls TLS backend (no system OpenSSL). |
native-tls |
Use the platform native TLS backend instead. | |
tracing |
Emit tracing spans/events. |
To drop the realtime transport (e.g. for a future REST-only build):
Usage
Configuration
Build a client with [Radion::builder]:
| Method | Description |
|---|---|
.api_key(key) |
Radion API key (required), sent as X-API-Key. |
.base_url(url) |
Override the REST base URL. Defaults to https://api.radion.app. |
.ws_url(url) |
Override the realtime endpoint. Defaults to wss://api.radion.app/ws. |
.token(jwt) / .token_provider(p) |
User JWT for the pk_jwt_ flow (see below). |
.auth_in_query(true) |
Send credentials in the WS URL query string. |
let radion = builder.api_key.build?;
# Ok::
Authentication
Two credential schemes, both keyed on the API key (sent as X-API-Key):
- Secret key (
sk_/rk_):.api_key("sk_..."). - Public JWT (
pk_jwt_): pair the public key with a per-user JWT. Use.token_provider(...)so a fresh JWT is fetched on every (re)connect:
use TokenProvider;
let radion = builder
.api_key
.token_provider
.build?;
# Ok::
# async
Use .auth_in_query(true) to move credentials into the WS URL query string —
for a proxy or gateway that strips the X-API-Key / Authorization headers.
Realtime client
Reached as radion.realtime, or constructed standalone with
RealtimeClient::new(RealtimeOptions::new(api_key)).
| Method | Description |
|---|---|
connect() |
Open the connection; resolves once established. |
subscribe(sub) |
Subscribe and return a Stream of that subscription's events. |
unsubscribe(id) |
Drop a subscription by id. |
events() |
Firehose Stream across all subscriptions. |
lifecycle() |
Stream of connection lifecycle events. |
close(code, reason) |
Gracefully close; stops reconnecting. |
connected() |
Whether the socket is currently open. |
Subscriptions & filters
Subscription::new(id, channel) takes a client-defined id (echoed back on every
event) and a channel. Subscriptions default to the confirmed on-chain feed; call
.pending() (or .confirmed(false)) for the speculative pending (mempool) feed:
Subscription::new("pending", Channel::Trading).pending(). Pending events arrive
on the same bare channel name and are told apart by ChannelEvent.confirmed;
their data is a decoded transaction (Payload::Mempool).
Attach server-side filters with .with_filters(...). Some channels require a
filter — wallets needs wallets, markets needs market_ids or token_ids.
Requirements are validated before any frame is sent.
use ;
let sub = new.with_filters;
Channels
Channel enumerates every channel. Nine topic channels each carry a typed
payload — Trading, Fees, Oracle, Resolution, Lifecycle, Positions,
Combos, Transfers, Accounts — plus two cross-cutting filter channels,
Wallets and Markets, that re-emit the matching topic payload. CHANNELS is
the full array. Each channel's event data is the typed Payload enum — match
on it for compile-time exhaustiveness. Unknown channels or event types are
preserved as Payload::Other(serde_json::Value).
Filter high-volume order flow by size with min_usd on Trading (there is no
separate large-trades channel). On the confirmed feed min_usd is the actual
filled USD; on the pending feed it is the intended fill notional
(call.notional_usd). Every topic channel also has a pending (mempool) feed,
selected with .pending() on the subscription.
CLOB channels
The CLOB (central limit order book) family is a first-class, separate set of
subscribable channels. ClobChannel enumerates the six — Book, Prices,
LastTrade, Midpoint, TickSize, BestBidAsk (wire names clob.book,
clob.prices, clob.last_trade, clob.midpoint, clob.tick_size,
clob.best_bid_ask) — and CLOB_CHANNELS is the full array. Unlike topic
channels, each CLOB channel requires a token_ids filter and has no
pending feed. Each carries one fixed payload (Payload::ClobBook,
ClobPrices, ClobLastTrade, ClobMidpoint, ClobTickSize, ClobBestBidAsk)
with no event type discriminator.
use ;
let book = new.with_filters;
Lifecycle events
use StreamExt;
use LifecycleEvent;
# async
Reconnect & subscription restore
By default the client reconnects with exponential backoff (500ms initial, ×2,
30s max, 0.2 jitter) and replays every active subscription on reconnect. Tune via
RealtimeOptions::reconnect(...) or disable with .disable_reconnect().
Heartbeats
A ping is sent every 15s; if no inbound traffic arrives within 10s the connection
is treated as stale, torn down, and reconnected. Tune via
RealtimeOptions::heartbeat(...) or disable with .disable_heartbeat().
Error handling
RadionError covers connection-lifecycle misuse (Connection), server-reported
error frames (Server), and transport failures (Transport). Server and
transport errors during a live connection are surfaced on the lifecycle()
stream as LifecycleEvent::Error.
License
MIT — see LICENSE.