Real-time Sui blockchain data over SSE and WebSocket. Powered by jun.
Connect to any stream with curl or EventSource:
curl -N http://localhost:3000/stream/transactionsconst es = new EventSource("http://localhost:3000/stream/transactions");
es.addEventListener("transactions", (e) => {
const tx = JSON.parse(e.data);
console.log(tx.digest, tx.sender);
});| Endpoint | Description |
|---|---|
/stream/checkpoints |
Checkpoint summaries |
/stream/transactions |
Transaction records |
/stream/move_calls |
Move function calls |
/stream/balance_changes |
Per-transaction balance changes |
/stream/object_changes |
Object state changes |
/stream/transaction_dependencies |
Transaction dependency edges |
/stream/transaction_inputs |
Programmable transaction inputs |
/stream/commands |
All PTB commands |
/stream/system_transactions |
Non-programmable (system) transactions |
/stream/unchanged_consensus_objects |
Read-only consensus object refs |
/stream/events |
Raw events |
checkpoints
{
"sequence_number": "262251530",
"epoch": "1090",
"digest": "...",
"timestamp": "2026-04-07T01:27:58.308Z",
"tx_count": 8
}transactions
{
"digest": "...",
"sender": "0x...",
"success": true,
"computation_cost": "634980",
"storage_cost": "23628400",
"storage_rebate": "19268964",
"move_call_count": 2,
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z",
"epoch": "1090"
}move_calls
{
"tx_digest": "...",
"call_index": 0,
"package": "0x...",
"module": "pool",
"function": "swap",
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}balance_changes
{
"tx_digest": "...",
"checkpoint_seq": "262251530",
"address": "0x...",
"coin_type": "0x2::sui::SUI",
"amount": "-1000000",
"timestamp": "2026-04-07T01:27:58.308Z"
}object_changes
{
"tx_digest": "...",
"object_id": "0x...",
"change_type": "MUTATED",
"object_type": "0x2::coin::Coin<0x2::sui::SUI>",
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}transaction_dependencies
{
"tx_digest": "...",
"depends_on_digest": "...",
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}transaction_inputs
{
"tx_digest": "...",
"input_index": 0,
"kind": "SHARED",
"object_id": "0x...",
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}commands
{
"tx_digest": "...",
"command_index": 0,
"kind": "MoveCall",
"package": "0x...",
"module": "pool",
"function": "swap",
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}system_transactions
{
"tx_digest": "...",
"kind": "ConsensusCommitPrologueV1",
"data": {},
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}unchanged_consensus_objects
{
"tx_digest": "...",
"object_id": "0x...",
"kind": "ReadOnly",
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}events
{
"tx_digest": "...",
"event_seq": 0,
"package_id": "0x...",
"module": "pool",
"event_type": "0xdee9::clob_v2::OrderPlaced",
"sender": "0x...",
"contents": "...",
"checkpoint_seq": "262251530",
"timestamp": "2026-04-07T01:27:58.308Z"
}Connect to ws://localhost:3000/ws for bidirectional streaming with filtering and field selection.
Single subject:
{
"subscribe": "transactions",
"filters": { "sender": "0x..." },
"fields": ["digest", "sender", "timestamp"]
}Multiple subjects at once:
{
"subscriptions": [
"events",
{ "subject": "transactions", "filters": { "sender": "0x..." } },
{ "subject": "balance_changes", "filters": { "coin_type": "0x2::sui::SUI" }, "fields": ["address", "amount"] }
]
}{ "unsubscribe": "transactions" }
{ "unsubscribe": ["transactions", "events"] }{ "subject": "transactions", "data": { "digest": "...", "sender": "0x...", "timestamp": "..." } }Re-subscribing to the same subject replaces the previous subscription.
Exact match — { "sender": "0xabc..." }
Multiple values (OR) — { "coin_type": "0x2::sui::SUI,0xdba3...::usdc::USDC" }
Prefix match — { "event_type": "0xdee9*" } (trailing *)
Negation — { "!sender": "0x000...0" } (prefix !) or { "sender!": "0x000...0" } (suffix !)
GET /health → { "status": "ok" }
| Variable | Default | Description |
|---|---|---|
JUNSTREAM_NATS_URL |
nats://localhost:4222 |
NATS server URL |
JUNSTREAM_NATS_PREFIX |
jun |
NATS subject prefix |
PORT |
3000 |
HTTP listen port |
Sui fullnode (gRPC) → jun publisher → NATS → junstream proxy → SSE / WebSocket
- Publisher: Runs
jun index stream-chain, decodes checkpoints via native Rust decoder, broadcasts all record types to NATS subjects - NATS: Message broker connecting publisher to proxy
- Proxy: Subscribes to NATS subjects, serves SSE streams and WebSocket connections
Install the TypeScript client:
bun add @unconfirmed/junstreamimport { junstream } from "@unconfirmed/junstream";
const client = junstream("http://localhost:3000");
// Async iterator
for await (const tx of client.stream("transactions")) {
console.log(tx.digest, tx.sender);
}
// Callback
const unsub = client.subscribe("balance_changes", (bc) => {
console.log(bc.address, bc.amount);
}, { filter: { coin_type: "0x2::sui::SUI" } });
// WebSocket (multi-stream)
const ws = client.ws();
ws.subscribe("transactions", (tx) => console.log(tx.digest));
ws.subscribe("events", (ev) => console.log(ev.event_type));