The fast path, documented.
Set up Shred Stream, submit transactions through regional Landing Service endpoints, or request a managed Dedicated Node. Every access flow starts in SlotBolt Discord.
Send straight into the action.
Use one API key across Frankfurt, Amsterdam, New York, and Tokyo. Create it in Discord.
Your free API key starts at 3 TPS in each region. There is no plan payment for this tier; each submitted transaction still needs the required Tip.
Manage your API key and TPS plan
Create your API key in SlotBolt Discord.
Select 40 or 100 TPS. A private order shows the exact USDC amount, wallet, unique payment reference, and quote expiry.
Pay before the quote expires, then submit the transaction ID in Enter TXID. The same key gains the new TPS across all four regions.
Return to the same panel to renew your current tier or upgrade from 40 to 100 TPS. The order quotes unused prepaid credit.
The tip lives inside your transaction.
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: new PublicKey("5kw4XdUjk7k9rPH8cTNFqgtpnwhWekVAXEKB2medmoq"), // example; randomly select from the approved Tip Accounts below
lamports: 100_000_000 // 0.1 SOL
})
Priority Fee and Tip guidance
Configure Priority Fee and Tip for current network conditions. Actual inclusion depends on validators and network conditions.
Approved Tip Accounts
20 approved Tip Accounts
5kw4XdUjk7k9rPH8cTNFqgtpnwhWekVAXEKB2medmoq4tgpD2mKc6ojaGdJhxvjvo4fRDRJCQUimcvt9SX842QtGN1BZUWcoGLStou1M9RD98c9b7JdoXZoTTZR9a6WkdaT3uANArQUJDsaBqFVNCP9C5HZV9YSU7b2upRH3d9pNHtM4A68gTsn6pExwmEAn9CVdJxFvVxLdYertG83SR2BYmtDCinsfRDHjvyfKJjwp8R9TJRCkoP6SGM2eHKJiWyehpP7EKb54KnRaXxQaaDLqDSvGvRL76whuf5CKtum1qSCSMnkAsMLHiUCZoojNF1b6sDRPKrbc6RkeDHPZyGPa5hK5ToYGnPxqoD63TXYrTsXZNsxyt2iZaperxTUz344KUHgBJLJHiKfsAEYBL21cZSmhLjV5VEcgjFH5yzGGXXDQcVTztKS4TTTLWoJNMAHWyFVUDfXasZLfXTt54h4zVpE6wYXa2fHJ8PJG3HQfwxw9yG5w7DzuwnfadAfaikyGyDSLkt2cpGi8qKJzmNeckLTEHnNdmN198quQ1omU2FshgSLYQeB3oww7yQ4me6yFn6LDX9jAZ4Sy3jdg6m15uJM15tB1EBC3Jbz9qc8oYmvSyxaruvnqReY8WXk1ua9r4gRvqKQpEGEVaWcHPbzYkQ8pkC9UyBEWMtLopQGLxUzpzboXRbrqn9Ue9kgGQVb9JkQGzQfkXZAbMS66p7dUs7BKbJRD5soSX8m66MW3jShf4bRzuXPsHtkAAwwvAPCt4H4sEcncKPjo1jZGdWgF2378NNuCQMng3kDU4exgDoZ6LCSfHS2o2pRH2jXYWjE82yeCQBs34DJDfj3ybwrbfUDmpdHQpxMNvoRdnhRmw7E
Regional HTTP endpoints
http://fra.slotbolt.trade:1234/submithttp://ams.slotbolt.trade:1234/submithttp://ny.slotbolt.trade:1234/submithttp://tokyo.slotbolt.trade:1234/submitAppend your API key as the api_key query parameter. Submit a signed, base64 encoded transaction with the JSON-RPC 2.0 sendTransaction method:
The current endpoint accepts legacy and v0 signed transactions. Version 1 is not supported.
POST http://fra.slotbolt.trade:1234/submit?api_key=<YOUR_API_KEY>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "sendTransaction",
"params": ["<SIGNED_TRANSACTION_BASE64>", {"encoding": "base64"}]
}
A valid submission returns HTTP 200 with the first signature from the signed transaction and your original request ID:
{
"jsonrpc": "2.0",
"result": "<FIRST_TRANSACTION_SIGNATURE>",
"id": 1
}
QUIC Access
In development. Please use JSON-RPC for now.
Keep Alive
Request the matching regional /ping endpoint every 30 seconds with the same api_key. Once the key is accepted, each /submit or /ping request consumes one regional TPS slot, including submits rejected for invalid parameters, transaction parsing, or Tip. Each region counts missing or invalid keys on /ping, /submit, and legacy POST / together by connecting IP. Before an IP block, these requests return HTTP 401; the threshold request or an existing connection may receive HTTP 403. Requests over the TPS limit return HTTP 429.
GET http://fra.slotbolt.trade:1234/ping?api_key=<YOUR_API_KEY>
Error codes
Submit errors use a JSON-RPC 2.0 error object with the original request ID when available. /ping returns the HTTP status without a JSON body.
| HTTP | JSON-RPC code | Meaning |
|---|---|---|
| 400 | -32700 | The request body is not valid JSON. |
| 400 | -32600 | Missing or invalid JSON-RPC version, method field missing or not a string, or missing or invalid request ID. |
| 400 | -32601 | Unsupported JSON-RPC method. |
| 400 | -32602 | Invalid parameters or signed transaction; a missing approved Tip also fails here. |
| 401 | -32001 | Missing, invalid, or revoked API key. |
| 403 | -32004 | Access denied. Contact SlotBolt Support for help. |
| 413 | -32600 | Request body is too large. |
| 429 | -32002 | API key TPS limit exceeded. Keep Alive shares the same allowance. |
| 503 | -32003 | Transaction logging is unavailable; retry later. |
| 503 | -32005 | Access check in progress; retry shortly. |
Code examples
Node.js 20+ · one Tip per transaction, sign, submit and keepalive
npm install @solana/web3.js@1
landing.mjs
// Save as landing.mjs. Send one JSON line per transaction on stdin.
import { readFileSync } from "node:fs";
import { createInterface } from "node:readline";
import {
ComputeBudgetProgram, Keypair, PublicKey, SystemProgram,
TransactionMessage, VersionedTransaction
} from "@solana/web3.js";
const region = process.env.SLOTBOLT_REGION;
if (!["fra", "ams", "ny", "tokyo"].includes(region)) throw new Error("Invalid region");
const key = process.env.SLOTBOLT_API_KEY;
const keypairFile = process.env.SLOTBOLT_KEYPAIR_FILE;
if (!key || !keypairFile) throw new Error("Set SLOTBOLT_API_KEY and SLOTBOLT_KEYPAIR_FILE");
const payer = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(readFileSync(keypairFile, "utf8"))));
const tipAccounts = [
"5kw4XdUjk7k9rPH8cTNFqgtpnwhWekVAXEKB2medmoq",
"4tgpD2mKc6ojaGdJhxvjvo4fRDRJCQUimcvt9SX842Qt",
"GN1BZUWcoGLStou1M9RD98c9b7JdoXZoTTZR9a6WkdaT",
"3uANArQUJDsaBqFVNCP9C5HZV9YSU7b2upRH3d9pNHtM",
"4A68gTsn6pExwmEAn9CVdJxFvVxLdYertG83SR2BYmtD",
"CinsfRDHjvyfKJjwp8R9TJRCkoP6SGM2eHKJiWyehpP7",
"EKb54KnRaXxQaaDLqDSvGvRL76whuf5CKtum1qSCSMnk",
"AsMLHiUCZoojNF1b6sDRPKrbc6RkeDHPZyGPa5hK5ToY",
"GnPxqoD63TXYrTsXZNsxyt2iZaperxTUz344KUHgBJLJ",
"HiKfsAEYBL21cZSmhLjV5VEcgjFH5yzGGXXDQcVTztKS",
"4TTTLWoJNMAHWyFVUDfXasZLfXTt54h4zVpE6wYXa2fH",
"J8PJG3HQfwxw9yG5w7DzuwnfadAfaikyGyDSLkt2cpGi",
"8qKJzmNeckLTEHnNdmN198quQ1omU2FshgSLYQeB3oww",
"7yQ4me6yFn6LDX9jAZ4Sy3jdg6m15uJM15tB1EBC3Jbz",
"9qc8oYmvSyxaruvnqReY8WXk1ua9r4gRvqKQpEGEVaWc",
"HPbzYkQ8pkC9UyBEWMtLopQGLxUzpzboXRbrqn9Ue9kg",
"GQVb9JkQGzQfkXZAbMS66p7dUs7BKbJRD5soSX8m66MW",
"3jShf4bRzuXPsHtkAAwwvAPCt4H4sEcncKPjo1jZGdWg",
"F2378NNuCQMng3kDU4exgDoZ6LCSfHS2o2pRH2jXYWjE",
"82yeCQBs34DJDfj3ybwrbfUDmpdHQpxMNvoRdnhRmw7E"
].map((address) => new PublicKey(address));
let nextTipIndex = payer.publicKey.toBytes()[0] % tipAccounts.length;
const base = "http://" + region + ".slotbolt.trade:1234";
const query = new URLSearchParams({ api_key: key });
async function ping() {
const response = await fetch(base + "/ping?" + query);
if (!response.ok) throw new Error("Ping failed: HTTP " + response.status);
}
setInterval(() => { ping().catch(console.error); }, 30_000);
const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });
for await (const line of lines) {
if (!line.trim()) continue;
const { recipient, lamports, recentBlockhash } = JSON.parse(line);
if (!Number.isSafeInteger(lamports) || lamports <= 0) throw new Error("Invalid lamports");
// Select one Tip Account for this input line; submit one transaction below.
const tipAccount = tipAccounts[nextTipIndex];
nextTipIndex = (nextTipIndex + 1) % tipAccounts.length;
const message = new TransactionMessage({
payerKey: payer.publicKey,
recentBlockhash,
instructions: [
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 60_000_000 }),
SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: new PublicKey(recipient), lamports }),
SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: tipAccount, lamports: 110_000_000 })
]
}).compileToV0Message();
const tx = new VersionedTransaction(message);
tx.sign([payer]);
const signedTx = Buffer.from(tx.serialize()).toString("base64");
const response = await fetch(base + "/submit?" + query, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0", id: 1, method: "sendTransaction",
params: [signedTx, { encoding: "base64" }]
})
});
const reply = await response.json().catch(() => null);
if (reply?.error) throw new Error("SlotBolt JSON-RPC " + reply.error.code + ": " + reply.error.message);
if (!response.ok) throw new Error("Submit failed: HTTP " + response.status);
if (!reply) throw new Error("Submit returned no JSON-RPC body");
console.log("Accepted by SlotBolt API:", reply.result);
}
Rust · one Tip per transaction, sign, submit and keepalive
Cargo.toml
[dependencies]
tokio = { version = "1.45.1", features = ["macros", "rt-multi-thread", "time", "io-std", "io-util"] }
reqwest = { version = "0.11.27", default-features = false, features = ["json", "rustls-tls"] }
serde_json = "1.0.140"
solana-sdk = "=2.2.1"
base64 = "0.22.1"
bincode = "1.3.3"
src/main.rs
use std::{env, str::FromStr, time::Duration};
use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde_json::{json, Value};
use solana_sdk::{
compute_budget::ComputeBudgetInstruction,
hash::Hash,
message::{v0, VersionedMessage},
pubkey::Pubkey,
signature::{read_keypair_file, Signer},
system_instruction,
transaction::VersionedTransaction,
};
use tokio::io::{self, AsyncBufReadExt, BufReader};
const TIP_ACCOUNTS: [&str; 20] = [
"5kw4XdUjk7k9rPH8cTNFqgtpnwhWekVAXEKB2medmoq",
"4tgpD2mKc6ojaGdJhxvjvo4fRDRJCQUimcvt9SX842Qt",
"GN1BZUWcoGLStou1M9RD98c9b7JdoXZoTTZR9a6WkdaT",
"3uANArQUJDsaBqFVNCP9C5HZV9YSU7b2upRH3d9pNHtM",
"4A68gTsn6pExwmEAn9CVdJxFvVxLdYertG83SR2BYmtD",
"CinsfRDHjvyfKJjwp8R9TJRCkoP6SGM2eHKJiWyehpP7",
"EKb54KnRaXxQaaDLqDSvGvRL76whuf5CKtum1qSCSMnk",
"AsMLHiUCZoojNF1b6sDRPKrbc6RkeDHPZyGPa5hK5ToY",
"GnPxqoD63TXYrTsXZNsxyt2iZaperxTUz344KUHgBJLJ",
"HiKfsAEYBL21cZSmhLjV5VEcgjFH5yzGGXXDQcVTztKS",
"4TTTLWoJNMAHWyFVUDfXasZLfXTt54h4zVpE6wYXa2fH",
"J8PJG3HQfwxw9yG5w7DzuwnfadAfaikyGyDSLkt2cpGi",
"8qKJzmNeckLTEHnNdmN198quQ1omU2FshgSLYQeB3oww",
"7yQ4me6yFn6LDX9jAZ4Sy3jdg6m15uJM15tB1EBC3Jbz",
"9qc8oYmvSyxaruvnqReY8WXk1ua9r4gRvqKQpEGEVaWc",
"HPbzYkQ8pkC9UyBEWMtLopQGLxUzpzboXRbrqn9Ue9kg",
"GQVb9JkQGzQfkXZAbMS66p7dUs7BKbJRD5soSX8m66MW",
"3jShf4bRzuXPsHtkAAwwvAPCt4H4sEcncKPjo1jZGdWg",
"F2378NNuCQMng3kDU4exgDoZ6LCSfHS2o2pRH2jXYWjE",
"82yeCQBs34DJDfj3ybwrbfUDmpdHQpxMNvoRdnhRmw7E"
];
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let region = env::var("SLOTBOLT_REGION")?;
if !matches!(region.as_str(), "fra" | "ams" | "ny" | "tokyo") {
return Err("invalid region".into());
}
let key = env::var("SLOTBOLT_API_KEY")?;
let payer = read_keypair_file(env::var("SLOTBOLT_KEYPAIR_FILE")?)?;
let mut next_tip_index = payer.pubkey().to_bytes()[0] as usize % TIP_ACCOUNTS.len();
let base = format!("http://{region}.slotbolt.trade:1234");
let client = reqwest::Client::builder().timeout(Duration::from_secs(5)).build()?;
let ping_client = client.clone();
let ping_key = key.clone();
let ping_url = format!("{base}/ping");
tokio::spawn(async move {
let mut cadence = tokio::time::interval(Duration::from_secs(30));
cadence.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
loop {
cadence.tick().await;
match ping_client.get(&ping_url).query(&[("api_key", &ping_key)]).send().await {
Ok(response) if response.status().is_success() => {}
Ok(response) => eprintln!("Ping failed: HTTP {}", response.status()),
Err(error) => eprintln!("Ping failed: {error}"),
}
}
});
let mut lines = BufReader::new(io::stdin()).lines();
while let Some(line) = lines.next_line().await? {
if line.trim().is_empty() { continue; }
let trade: Value = serde_json::from_str(&line)?;
let recipient = Pubkey::from_str(trade["recipient"].as_str().ok_or("missing recipient")?)?;
let lamports = trade["lamports"].as_u64().filter(|value| *value > 0).ok_or("invalid lamports")?;
let blockhash = Hash::from_str(trade["recentBlockhash"].as_str().ok_or("missing recentBlockhash")?)?;
// Select one Tip Account for this input line; submit one transaction below.
let tip = Pubkey::from_str(TIP_ACCOUNTS[next_tip_index])?;
next_tip_index = (next_tip_index + 1) % TIP_ACCOUNTS.len();
let instructions = vec![
ComputeBudgetInstruction::set_compute_unit_limit(200_000),
ComputeBudgetInstruction::set_compute_unit_price(60_000_000),
system_instruction::transfer(&payer.pubkey(), &recipient, lamports),
system_instruction::transfer(&payer.pubkey(), &tip, 110_000_000),
];
let message = v0::Message::try_compile(&payer.pubkey(), &instructions, &[], blockhash)?;
let tx = VersionedTransaction::try_new(VersionedMessage::V0(message), &[&payer])?;
let signed_tx = STANDARD.encode(bincode::serialize(&tx)?);
let response = client.post(format!("{base}/submit"))
.query(&[("api_key", &key)])
.json(&json!({
"jsonrpc": "2.0", "id": 1, "method": "sendTransaction",
"params": [signed_tx, { "encoding": "base64" }]
}))
.send().await?;
let status = response.status();
let reply: Value = response.json().await?;
if let Some(error) = reply.get("error") {
return Err(format!("SlotBolt JSON-RPC error: {error}").into());
}
if !status.is_success() { return Err(format!("Submit failed: HTTP {status}").into()); }
println!("Accepted by SlotBolt API: {}", reply["result"]);
}
Ok(())
}
Fast Shred Stream from Frankfurt.
Receive Shred data from our validator partners at your Solana RPC node's public TVU UDP IP and port. Frankfurt delivery is available; Tokyo and New York are at capacity. The Frankfurt distributor forwards incoming Shreds to your configured TVU UDP destination.
Open the SlotBolt invite and complete Discord Verify to unlock service controls.
Select Subscribe Shred Stream and choose a term. Frankfurt is available; Tokyo and New York are at capacity. No region selection is needed.
Use the order's private Solana USDC payment details. Select Enter TXID on the payment request message, paste the transaction signature in the dialog, then select Submit to start payment verification.
In Manage Shred Stream, view the expiry, renew the term, and set your node's TVU UDP IP:port.
Find your node's TVU port
On the machine running your Solana RPC node, inspect the validator's advertised contact info. Use its TVU UDP address and port, not its RPC or TPU port. Replace the ledger path with your own.
agave-validator --ledger /path/to/ledger contact-info | grep 'TVU:'
Set your delivery target to that node's reachable public IP:TVU-port. Once configured, Manage Shred Stream privately shows the sender IP to allow through your UDP firewall; ask SlotBolt Support only if it is absent. For older Solana validators, replace agave-validator with solana-validator.
Shred Stream plans
New subscriptions and renewals use the same six terms. In Manage Shred Stream, choose Renew Shred Stream, select a term, and submit the TXID from the private payment request. An active term extends its expiry; an expired term restarts from the new payment's block time. The first purchase timestamp remains on record.
Payment verification and delivery target updates are handled in the private Shred Stream control. Need a custom setup? Talk to us on Discord ↗
Your own high performance edge.
Managed Frankfurt RPC and gRPC on AMD EPYC Turin hardware. Our engineers tune the Jito-Solana client and Yellowstone stack for high frequency quantitative trading. Frankfurt Shred Stream is included.
We manage the RPC node and provide you with RPC and gRPC access. We can also build and run an RPC node optimized for high-frequency trading with the server provider you specify. You can provision and fully manage your own server with a third-party provider and connect it to the same router as our hosted RPC node; ping latency can be below 0.050 ms. We cannot access your server; your assets remain under your control.
The node uses Jito-Solana by default and exposes RPC and gRPC. Heavy Account Indexing and account enumeration RPC APIs are disabled.
All provisioning and payment arrangements take place in a private ticket. An administrator confirms the Frankfurt Shred destination, any case-by-case credit for an existing Shred subscription, and the 30-day service start date. You can also request a gRPC speed test. Join SlotBolt Discord, open Dedicated Node, and choose Create Ticket ↗