Algorithmic traders and bots
Act on new swaps, token launches and transfers as they are processed, without waiting for confirmation.
Solana transaction infrastructure
SolJet streams each transaction as soon as it is processed, so your product can react earlier. Built for algorithmic traders, live-data apps and analytics teams.
Act on new swaps, token launches and transfers as they are processed, without waiting for confirmation.
Show balances, swaps and notifications to your users sooner, so the product feels current.
Feed dashboards and alerts with transaction data from the first moment it is processed.
See each transaction when it is processed, before it is confirmed.
Take transactions as processed for the earliest delivery, or in on-chain order, deduplicated and sorted.
Small payloads. Decode only the fields you need.
Legacy, V0 and V1 transactions are supported.
Connect over WebSocket or TCP. Receive parsed JSON, or base64-encoded transactions with a JSON header. Over TCP, raw transaction bytes are also available.
Replay the data you missed while disconnected: up to 9,000 slots (about 1 hour) on Small and Medium, and up to 225,000 slots (about 25 hours) on Big, Everything and Dedicated.
Each API key is tied to a plan and to the services you choose. Request a key for PumpSwap and it delivers PumpSwap transactions over WebSocket or TCP. We highlight the plan that covers their combined daily volume.
Tick the services you want to follow. We size the volume and highlight the plan that covers it.
Straightforward plans
Flat prices based on your daily transaction volume. All prices are listed here. Mainnet streaming at other providers starts at $499/month and is billed by data volume.
Connect over WebSocket for JSON or Base64, or over TCP for raw transaction bytes. Pass each message to your own process function.
// npm install ws @types/ws
import WebSocket from "ws";
const API_KEY = "this is a quick example - store your api key outside of git";
const ENDPOINT = `wss://soljet.one/v1/${API_KEY}`;
type Tx = Record<string, unknown>;
function process(data: Tx | Uint8Array): void {
// your code here
}
const ws = new WebSocket(ENDPOINT);
ws.on("message", (raw) => {
const message = JSON.parse(raw.toString()) as Tx;
if (typeof message.transaction === "string") {
// Base64 feed: decode to raw bytes
process(Buffer.from(message.transaction, "base64"));
} else {
// JSON feed: already a dict
process(message);
}
});
ws.on("close", () => console.log("disconnected"));
ws.on("error", (err) => console.error(err));// Works in the browser and in Node.js 22+ (built-in WebSocket)
const API_KEY = "this is a quick example - store your api key outside of git";
const ENDPOINT = `wss://soljet.one/v1/${API_KEY}`;
function process(data) {
// your code here
}
const ws = new WebSocket(ENDPOINT);
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (typeof message.transaction === "string") {
// Base64 feed: decode to raw bytes
const bytes = Uint8Array.from(atob(message.transaction), (c) => c.charCodeAt(0));
process(bytes);
} else {
// JSON feed: already an object
process(message);
}
};
ws.onclose = () => console.log("disconnected");
ws.onerror = (err) => console.error(err);# pip install websockets
import asyncio
import base64
import json
import websockets
API_KEY = "this is a quick example - store your api key outside of git"
ENDPOINT = f"wss://soljet.one/v1/{API_KEY}"
def process(data):
# data is a dict (JSON feed) or bytes (Base64 feed)
pass
async def main():
async with websockets.connect(ENDPOINT) as ws:
async for raw in ws:
message = json.loads(raw)
if isinstance(message.get("transaction"), str):
# Base64 feed: decode to raw bytes
process(base64.b64decode(message["transaction"]))
else:
# JSON feed: already a dict
process(message)
asyncio.run(main())// Cargo.toml
// tokio = { version = "1", features = ["full"] }
// tokio-tungstenite = { version = "0.24", features = ["native-tls"] }
// futures-util = "0.3"
// serde_json = "1"
// base64 = "0.22"
use base64::{engine::general_purpose::STANDARD, Engine};
use futures_util::StreamExt;
use serde_json::Value;
use tokio_tungstenite::{connect_async, tungstenite::Message};
const API_KEY: &str = "this is a quick example - store your api key outside of git";
enum Data {
Json(Value), // JSON feed
Raw(Vec<u8>), // Base64 feed, decoded
}
fn process(data: Data) {
// your code here
let _ = data;
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let endpoint = format!("wss://soljet.one/v1/{API_KEY}");
let (mut ws, _) = connect_async(endpoint.as_str()).await?;
while let Some(msg) = ws.next().await {
if let Message::Text(text) = msg? {
let message: Value = serde_json::from_str(&text)?;
match message.get("transaction").and_then(Value::as_str) {
Some(b64) => process(Data::Raw(STANDARD.decode(b64)?)),
None => process(Data::Json(message)),
}
}
}
Ok(())
}// Node.js
import net from "node:net";
const API_KEY = "this is a quick example - store your api key outside of git";
const HOST = "soljet.one";
const PORT = 7777;
const TYPE_BLOCKHASH = 1; // recent blockhash: 32 raw bytes (not base58 text)
const TYPE_TX = 2; // raw transaction bytes
function process(type: number, payload: Buffer): void {
// your code here
}
// Reads a varint starting at `offset`.
// Bit 128 means another byte follows. Returns [value, nextOffset].
function readVarint(buf: Buffer, offset: number): [number, number] | null {
let value = 0;
let shift = 0;
for (let i = offset; i < buf.length; i++) {
value += (buf[i] & 0x7f) * 2 ** shift;
if ((buf[i] & 0x80) === 0) return [value, i + 1];
shift += 7;
}
return null; // wait for more bytes
}
let pending: Buffer = Buffer.alloc(0);
const socket = net.connect(PORT, HOST, () => {
// Handshake: send your API key first
socket.write(API_KEY);
});
socket.on("data", (chunk) => {
pending = Buffer.concat([pending, chunk]);
// <type: 1 byte><length: varint><payload>
while (pending.length > 0) {
const type = pending[0];
const header = readVarint(pending, 1);
if (!header) break;
const [length, start] = header;
if (pending.length < start + length) break;
process(type, pending.subarray(start, start + length));
pending = pending.subarray(start + length);
}
});
socket.on("close", () => console.log("disconnected"));
socket.on("error", (err) => console.error(err));// Node.js
import net from "node:net";
const API_KEY = "this is a quick example - store your api key outside of git";
const HOST = "soljet.one";
const PORT = 7777;
const TYPE_BLOCKHASH = 1; // recent blockhash: 32 raw bytes (not base58 text)
const TYPE_TX = 2; // raw transaction bytes
function process(type, payload) {
// your code here
}
// Reads a varint starting at `offset`.
// Bit 128 means another byte follows. Returns [value, nextOffset].
function readVarint(buf, offset) {
let value = 0;
let shift = 0;
for (let i = offset; i < buf.length; i++) {
value += (buf[i] & 0x7f) * 2 ** shift;
if ((buf[i] & 0x80) === 0) return [value, i + 1];
shift += 7;
}
return null; // wait for more bytes
}
let pending = Buffer.alloc(0);
const socket = net.connect(PORT, HOST, () => {
// Handshake: send your API key first
socket.write(API_KEY);
});
socket.on("data", (chunk) => {
pending = Buffer.concat([pending, chunk]);
// <type: 1 byte><length: varint><payload>
while (pending.length > 0) {
const type = pending[0];
const header = readVarint(pending, 1);
if (!header) break;
const [length, start] = header;
if (pending.length < start + length) break;
process(type, pending.subarray(start, start + length));
pending = pending.subarray(start + length);
}
});
socket.on("close", () => console.log("disconnected"));
socket.on("error", (err) => console.error(err));import asyncio
API_KEY = "this is a quick example - store your api key outside of git"
HOST = "soljet.one"
PORT = 7777
TYPE_BLOCKHASH = 1 # recent blockhash: 32 raw bytes (not base58 text)
TYPE_TX = 2 # raw transaction bytes
def process(msg_type, payload):
# payload is bytes
pass
async def read_varint(reader):
# Bit 128 means another byte follows.
value = 0
shift = 0
while True:
byte = (await reader.readexactly(1))[0]
value |= (byte & 0x7F) << shift
if (byte & 0x80) == 0:
return value
shift += 7
async def main():
reader, writer = await asyncio.open_connection(HOST, PORT)
# Handshake: send your API key first
writer.write(API_KEY.encode())
await writer.drain()
# <type: 1 byte><length: varint><payload>
while True:
msg_type = (await reader.readexactly(1))[0]
length = await read_varint(reader)
payload = await reader.readexactly(length)
process(msg_type, payload)
asyncio.run(main())// Cargo.toml
// tokio = { version = "1", features = ["full"] }
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpStream;
const API_KEY: &str = "this is a quick example - store your api key outside of git";
const HOST: &str = "soljet.one";
const PORT: u16 = 7777;
const TYPE_BLOCKHASH: u8 = 1; // recent blockhash: 32 raw bytes (not base58 text)
const TYPE_TX: u8 = 2; // raw transaction bytes
fn process(msg_type: u8, payload: Vec<u8>) {
// your code here
let _ = (msg_type, payload);
}
// Bit 128 means another byte follows.
async fn read_varint<R: AsyncReadExt + Unpin>(reader: &mut R) -> std::io::Result<u64> {
let mut value: u64 = 0;
let mut shift: u32 = 0;
loop {
let byte = reader.read_u8().await?;
value |= u64::from(byte & 0x7f) << shift;
if byte & 0x80 == 0 {
return Ok(value);
}
shift += 7;
}
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut stream = TcpStream::connect((HOST, PORT)).await?;
// Handshake: send your API key first
stream.write_all(API_KEY.as_bytes()).await?;
let mut reader = BufReader::new(stream);
// <type: 1 byte><length: varint><payload>
loop {
let msg_type = reader.read_u8().await?;
let length = read_varint(&mut reader).await? as usize;
let mut payload = vec![0u8; length];
reader.read_exact(&mut payload).await?;
process(msg_type, payload);
}
}A transaction is processed when a validator has executed it and included it in a block. It is confirmed once enough of the network has voted on that block. SolJet delivers transactions at the processed stage. Confirmation used to take several seconds. Solana's new consensus, Alpenglow, went live on mainnet on October 21, 2026 and targets finality of about 150 milliseconds, so the head start is now a fraction of a second.
Rarely, yes. If a block is dropped before it is confirmed, its transactions go with it. Check the confirmation status before you do anything you cannot undo.
Each API key is tied to a plan and to the services you chose. Request a key for PumpSwap and it delivers PumpSwap transactions over WebSocket or TCP, whichever you connect with.
The stream stops until your allowance resets. Paid plans have a daily allowance and the Free plan has a monthly one. The services list shows the estimated daily volume of each service, so you can pick a plan that covers them.
Pay by card or crypto, monthly or yearly. Pay for a year in advance and get 2 months free. The Free plan needs no card.
If your connection drops, reconnect and replay the data you missed. Small and Medium replay up to 9,000 slots (about 1 hour). Big, Everything and Dedicated replay up to 225,000 slots (about 25 hours).
Both deliver the same transactions for your key. Use WebSocket for parsed JSON or Base64. Use TCP for raw transaction bytes.