SWQoS vs Jito vs a Relay: Which Solana Sender to Use

What SWQoS and staked connections are, how Jito bundles and tips work, what a relay adds, and which Solana sender fits each case. With Node.js code.

  • November 1, 2026
  • Infrastructure
  • AllenHark Team
A quiet stack of pagesAn abstract drawing of light on the page's dark ground. A neat stack of thin, solid pages, the top one lit, with faint streams of light drifting toward it from the left.A quiet stack of pagesAn abstract drawing of light on the page's dark ground. A neat stack of thin, solid pages, the top one lit, with faint streams of light drifting toward it from the left.

Ask how Solana traders "send fast" and you'll hear three answers: SWQoS, Jito, a relay. They aren't three versions of one thing. SWQoS is a rule inside the leader's validator, Jito is a separate auction for bundles, and a relay is a service that pushes your signed transaction down several paths, often including the other two.

This post explains each, how they fit together and which to use when, with Node.js code at the end.

Disclosure: AllenHark runs AllenHark Relay, one of the senders compared here. Facts about other senders come from their own docs, linked, checked 31 October 2026.

The short answer

SWQoSJitoA relay (sender)
What it isHow a leader shares out its QUIC connection and stream capacity, by the sender's stakeAn off-chain Block Engine that auctions bundles to leaders running a Jito clientA service that takes your signed transaction and sends it down several paths at once
Who can use itNodes whose identity key has stake, and the RPC nodes and senders they peer withAnyone, directly or through a relayAnyone; most need an API key
What it costs, in principleNothing on-chain; you reach it through stake or a providerA tip to a Jito tip account, set by auction, plus normal feesUsually a tip inside each transaction, or a plan
Atomic?NoYes, for bundles of up to five transactionsSingle transactions no; bundles go through Jito
Use it whenYou don't choose it: every Agave leader applies it, so what matters is whether your path is stakedThe trade is worthless unless every leg lands, in orderYou want one transaction in fast, on any leader

What are staked connections?

A staked connection is a QUIC connection to a Solana leader's transaction port (the TPU) from a node whose identity key holds stake. The leader reads that identity from the connection's TLS certificate, looks up its stake, and gives it more connections, more concurrent streams and a share of transaction capacity in proportion to the stake. Connections with no stake share a smaller pool and are throttled first when the leader is busy. That rule is stake-weighted quality of service, or SWQoS. A validator can also assign stake to a trusted peer, such as its own RPC node, with --staked-nodes-overrides, so that peer's connections count as staked too.

Your wallet's SOL balance plays no part in it. What counts is the stake behind the identity key of the machine that opens the connection.

How a transaction reaches the leader

A signed transaction can reach the current leader four ways:

  1. An RPC node's sendTransaction. On Agave, the node forwards it over QUIC to the next two leaders by default, retrying every 2 seconds until it lands or its blockhash expires. It connects with the RPC node's own identity, so it is staked only if that identity is.
  2. Straight to the leader's TPU over QUIC from your own machine: an unstaked peer, unless your identity has stake. See how to submit directly to the leader TPU.
  3. Jito's Block Engine, which auctions bundles to leaders running a Jito client.
  4. A relay, which does several of these at once.

Whichever path you use, a blockhash lasts about 150 blocks, which is about 30 seconds, and a slot is about 200 ms.

Getting in, then getting ordered

Three levers decide whether a transaction lands, and they act at different points:

LeverWhere it actsWhat it decides
SWQoSThe leader's QUIC serverWhether your packet gets through when the leader is busy
Priority feeThe leader's schedulerWhere your transaction sits in the queue once it is inside
Jito tipJito's Block Engine auctionWhich bundles the Block Engine hands to a Jito leader

A priority fee raises the chance that the leader schedules your transaction ahead of competing ones; Agave ranks transactions by fee income per unit of estimated cost. But the scheduler only sees what got through the QUIC server, so no priority fee helps a packet that was throttled at the door. That's one reason transactions arrive late even with priority fees.

SWQoS in detail

Solana's guide sums up the idea with an example: a validator with 0.5% of the stake may send up to 0.5% of the packets the leader takes in. The details live in Agave's streamer code.

What the leader does

When a QUIC connection opens, the leader takes the public key from the peer's TLS certificate and looks up its stake, with any --staked-nodes-overrides entry taking precedence. These are the defaults in Agave 4.4.0, released 28 October 2026:

Staked peerUnstaked peer
Connection slots on the leader2,000; when full, a new peer can evict a randomly sampled peer with less stake3,000; when full, the oldest are pruned
Connections per peer168
Concurrent streams per connection128 to 512, scaled by stake128
Transactions a second per peerUp to the leader's whole capacity until staked load passes 76% of it, then a share in proportion to stake, never below 210400 a second, cut to 100 once total load passes 70% of capacity

Two more rules: a peer with less than 1/50,000 of total stake counts as unstaked, and unstaked traffic never throttles staked peers. Agave 4.3 instead set aside 80% of stream capacity for staked peers, the figure in Solana's guide, and capped each unstaked peer at 200 transactions a second. Validators running Firedancer use separate code.

How an RPC node gets it

Per Solana's guide, a validator with stake lists the RPC node's identity in its --staked-nodes-overrides file, and the RPC node sends to that validator's QUIC TPU port with --rpc-send-transaction-tpu-peer HOST:PORT, the "peer(s) to broadcast transactions to instead of the current leader" in Agave's help text. Such agreements "must be made directly between RPC operators and Validators", and the guide warns validators against doing this for untrusted RPC nodes.

An override changes how that one validator treats you. Every other leader looks at the stake behind your connection's identity.

Why you can't buy SWQoS for your bot

  • Your wallet doesn't count. SWQoS keys on the identity that opens the QUIC connection, not the wallet that signs the transaction.
  • Staking your SOL doesn't help your bot. Delegated stake belongs to the validator. Solana's guide says it is "not necessary, recommended or effective to delegate stake to RPC servers".
  • A little stake isn't enough. Below 1/50,000 of total stake, Agave treats you as unstaked.

So you get SWQoS by running a staked validator, or by sending through a provider whose connections are staked. Most senders below sell exactly that.

Jito in detail

Validators running Jito-Solana, a modified Agave client, connect to Jito's Block Engine. Traders and bots send it transactions and bundles; it simulates them, runs an auction and passes the winners to the validator. Tips go to validators and their stakers.

  • Bundles: up to five transactions, run in order and atomically within one slot, or not at all. The tip is any instruction, top-level or inside a program call, that sends SOL to one of eight tip accounts; Jito warns against putting tip accounts in address lookup tables.
  • The auction: bundles compete on tip, in parallel auctions for bundles that touch different accounts.
  • sendBundle vs sendTransaction: with sendBundle, "only the Jito tip matters". sendTransaction forwards one transaction to the validator with MEV protection; with bundleOnly=true it goes as a one-transaction bundle with revert protection. For sendTransaction, Jito suggests a 70/30 split between priority fee and tip.
  • Only Jito leaders count: "Tips do not prioritize transactions for validators that are not Jito-Solana leaders." Not every leader runs a Jito client, so a bundle can wait several slots for one, or expire with its blockhash.
  • Skipped blocks: Jito warns that if a block holding your bundle is skipped, its transactions can be rebroadcast and land one by one, outside the bundle rules. Guard with checks on account state before and after.
  • Limits: 1 request per second per IP per region by default, with no auth key needed.

Jito's newer BAM (Block Assembly Marketplace) hands scheduling to a BAM Node that sequences transactions and Jito bundles for connected validators; per its docs, even transactions sent straight to a BAM validator's TPU go through BAM before execution. bam.dev has its current state. For tips and failures, see how to send Jito bundles and the Jito bundles guide.

What a relay adds

A relay, or sender, takes your signed transaction and sends it down several paths at once: staked QUIC, direct QUIC and UDP to upcoming leaders, RPC nodes, and block builders such as Jito. If its connections are staked, you get SWQoS without stake of your own, and one request reaches Jito and non-Jito leaders alike.

The senders below charge a tip: a SOL transfer to their tip account inside your transaction, which moves only if the transaction lands and succeeds. Bundles aren't fanned out: split into separate transactions, a bundle would lose its atomicity, so a relay sends it to Jito whole.

AllenHark Relay sends each transaction through five channels at once: stake-weighted QUIC, QUIC and UDP to the leaders, an RPC fan-out, and the Jito Block Engine with other relayers. It lands transactions in the same block, at 0.1 ms over QUIC, and also takes WebSocket and HTTPS. Bundles of up to five go to the Jito Block Engine only, with a Jito tip on top. Each key gets 10 requests per second by default, more on request, and Relay Dedicated, a monthly add-on, lets whitelisted wallets send with no relay tip. It is the service first sold as 0-slot, not the 0slot (0slot.trade) below. Slipstream is a separate AllenHark product: a sender-agnostic relay that routes through the worker nearest the current leader, with 0.2 ms leader RTT.

Senders compared

How we compared: each row says what the sender's own docs say about how it sends and how you pay, checked 31 October 2026. We didn't benchmark landing rates or latency, and we list no prices or tip amounts: follow the links for current figures.

SenderHow it sends, per its docsHow you pay
AllenHark RelayFive channels at once, including stake-weighted QUIC and the Jito Block Engine; bundles go to Jito onlyA tip inside each transaction to an AllenHark tip wallet, or Relay Dedicated
Helius SenderSender Max uses all its pathways at once (Helius, Jito, Harmonic, Rakurai and others); the SWQOS-only tier takes a single fast path; sendBundle takes up to four transactionsA tip to a designated tip account plus a required priority fee in every transaction; no API credits
Nozomi (Temporal)Block builders (Jito, Harmonic) and Nozomi's staked connections; orders what it holds by tip and retries until confirmed or the blockhash expiresA tip to a Nozomi tip address, paid only when the transaction lands
0slotA staked_conn sendTransaction that "directly connects to our validator node", with dedicated SWQoS per userA tip to a 0slot tip account in each transaction, on plans sized by transactions per second
bloXroute Trader APIsubmit with options for staked connections (useStakedRPCs), leader-risk scoring that delays or skips risky leaders (frontRunningProtection), or revert protection, which sends to the Jito Block Engine and Paladin leaders onlyA tip to a bloXroute tipping address in every transaction, plus a Trader API subscription
Jito Block Engine, directsendTransaction and sendBundle; bundles land only with Jito-client leadersA tip to a Jito tip account, set by auction

Sources: AllenHark Relay docs; Helius Sender; Nozomi tipping and submission; 0slot docs and home page; bloXroute submit, tipping and landing modes; Jito low-latency send. Checked 31 October 2026.

Which sender to use

One fast swap

A snipe, an exit, any single swap that must land now: use a relay. One request covers staked connections, Jito leaders and the rest, for one tip. Send over QUIC when milliseconds count, and keep a priority fee: off the block-builder paths, the leader's scheduler orders by fee. More in the fastest way to land Solana transactions.

An atomic multi-leg trade

Arbitrage, or setup plus trade plus cleanup, where leg two is worthless without leg one: use a Jito bundle, directly or through a relay. With AllenHark Relay that means two tips, the relay's and Jito's. Expect to wait for a Jito leader, and re-sign with a fresh blockhash if the bundle expires. If the legs fit in one transaction, that is atomic on every leader: see DEX arbitrage in one transaction.

You run your own staked validator

Your validator's identity already has stake, so the connections it opens are staked. To give your own RPC node or sender the same treatment, follow Solana's peering setup with --staked-nodes-overrides and --rpc-send-transaction-tpu-peer. For bundles you still need Jito, directly or through a relay.

Low-volume, not urgent

Payments, claims, transfers that can wait a few seconds: a reliable RPC's sendTransaction with a sensible priority fee is enough, with no tips. AllenHark RPC gives 100 requests per second on the standard plan, and Dedicated RPC has no rate limits.

A bot sending through several senders at once

Each sender wants its own tip inside the transaction. One transaction carrying every tip pays them all when it lands; separate copies with one tip each and the same blockhash can land more than once: two buys, two tips.

The fix is a durable nonce. Every copy uses the same nonce value in place of a recent blockhash. The first copy processed advances the nonce; any other copy then fails the nonce check, and per Solana's docs "the entire transaction is dropped. No fees collected, no state changes." At most one copy lands, and one tip is paid. AllenHark's managed bots do this for you: they send through AllenHark Relay by default, Advanced mode adds your own Nozomi, 0slot, Jito and Helius Sender accounts, and with two or more senders on, each sender gets its own copy (Fast senders).

Two caveats: Nozomi's docs warn that block builders may deprioritize durable-nonce transactions, which are associated with spam, and Solana's docs say durable nonces may be deprecated in a future release.

Code: send one transaction through AllenHark Relay

This script builds a transaction with a compute-unit price, a stand-in for your swap (1 lamport to yourself) and the relay tip, signs it, posts it to AllenHark Relay's HTTPS endpoint and polls until it lands, printing the send slot and the landing slot.

The relay checks the tip first: a top-level SystemProgram.transfer to one of its tip wallets, with the destination in the static account keys, not behind a lookup table. The minimum is in the relay docs. The script reads the tip from RELAY_TIP_LAMPORTS and the key file's path from KEYPAIR; nothing is hard-coded.

JavaScript
// send-via-relay.mjs: build one transaction with a priority fee and a relay tip,
// send it through AllenHark Relay over HTTPS, and report the slot it landed in.
// npm i @solana/web3.js@1   (Node 18 or later, for fetch)
import fs from "node:fs";
import {
  ComputeBudgetProgram,
  Connection,
  Keypair,
  PublicKey,
  SystemProgram,
  TransactionMessage,
  VersionedTransaction,
} from "@solana/web3.js";

const RPC_URL = process.env.RPC_URL ?? "https://api.mainnet.solana.com";
const RELAY_URL = process.env.RELAY_URL ?? "https://relay.allenhark.com/v1/sendTx";
const API_KEY = process.env.RELAY_API_KEY; // from the Relay tab of your console
const TIP_LAMPORTS = Number(process.env.RELAY_TIP_LAMPORTS); // at least the minimum in the relay docs
if (!API_KEY || !process.env.KEYPAIR || !(TIP_LAMPORTS > 0)) {
  throw new Error("Set RELAY_API_KEY, KEYPAIR (path to a keypair JSON file) and RELAY_TIP_LAMPORTS");
}

// AllenHark tip wallets, from the relay docs. Pick one at random for each transaction.
const TIP_WALLETS = [
  "hark1zxc5Rz3K8Kquz79WPWFEgNCFeJnsMJ16f22uNP",
  "harkm2BTWxZuszoNpZnfe84jRbQTg6KGHaQBmWzDGQQ",
  "hark4CwtTnN2y9FaxjcFBAJdJqQrpouu5pgEixfqdEz",
  "harkoJfnM6dxrJydx5eVmDVwAgwC94KbhuxF69UbXwP",
  "hark6hUDUTekc1DGxWdJcuyDZwf6pJdCxd4SXAVtta6",
  "harkoTvFpKSrEQduYrNHXCurARVT19Ud3BnFhVxabos",
  "harkEpXoJv5qVzHaN7HSuUAd6PHjyMcFMcDYBMDJCEQ",
  "harkyXDdZSoJGyCxa24t2QXx1poPyp8YfghbtpzGSzK",
  "harkR2YJ4Dpt4UDJTcBirjnSPBhNpQFcoFkNpCkVqNk",
  "harkRBygM8pHYe4K8eBjfxyEX19oJn3LepFjvNbLbyi",
  "harkYFxB6DuUFNwDLvA5CQ66KpfRvFgUoVypMagNcmd",
];

const payer = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(fs.readFileSync(process.env.KEYPAIR, "utf8"))),
);
const connection = new Connection(RPC_URL, "confirmed");
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// Priority fee in micro-lamports per compute unit: the 75th percentile of recent
// fees on the accounts the transaction writes. For a real swap, pass the pool accounts.
async function computeUnitPrice(writable) {
  const fees = (await connection.getRecentPrioritizationFees({ lockedWritableAccounts: writable }))
    .map((f) => f.prioritizationFee)
    .sort((a, b) => a - b);
  return Math.max(1, fees[Math.floor(fees.length * 0.75)] ?? 0);
}

const tipWallet = new PublicKey(TIP_WALLETS[Math.floor(Math.random() * TIP_WALLETS.length)]);
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash("confirmed");

const instructions = [
  ComputeBudgetProgram.setComputeUnitLimit({ units: 10_000 }),
  ComputeBudgetProgram.setComputeUnitPrice({ microLamports: await computeUnitPrice([payer.publicKey]) }),
  // Stand-in for your swap: move 1 lamport to yourself.
  SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: payer.publicKey, lamports: 1 }),
  // The relay tip: a top-level transfer whose destination sits in the static account
  // keys. No lookup table is used, so it does.
  SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: tipWallet, lamports: TIP_LAMPORTS }),
];
const tx = new VersionedTransaction(
  new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: blockhash, instructions }).compileToV0Message(),
);
tx.sign([payer]);

const sentSlot = await connection.getSlot("processed");
const res = await fetch(RELAY_URL, {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-api-key": API_KEY },
  body: JSON.stringify({ tx: Buffer.from(tx.serialize()).toString("base64"), simulate: false }),
});
const reply = await res.json();
if (reply.status !== "accepted") throw new Error(`Relay rejected it: ${reply.error ?? res.status}`);
console.log(`accepted ${reply.signature}, sent at slot ${sentSlot}`);

// Accepted is not landed. Poll until it is confirmed or its blockhash expires.
for (let i = 0; ; i++) {
  const [status] = (await connection.getSignatureStatuses([reply.signature])).value;
  if (status && (status.confirmationStatus === "confirmed" || status.confirmationStatus === "finalized")) {
    const outcome = status.err ? `failed: ${JSON.stringify(status.err)}` : "succeeded";
    console.log(`landed in slot ${status.slot} (${status.slot - sentSlot} after the send slot), ${outcome}`);
    break;
  }
  if (i % 5 === 4 && (await connection.getBlockHeight("confirmed")) > lastValidBlockHeight) {
    console.log("blockhash expired before it landed: rebuild with a fresh blockhash and send again");
    break;
  }
  await sleep(200);
}

Set RELAY_API_KEY, KEYPAIR and RELAY_TIP_LAMPORTS, and point RPC_URL at your own RPC; the public endpoint is rate-limited.

  • The send slot is your RPC's view, which can trail the leader a little. Treat the gap as a rough measure, not a benchmark.
  • Accepted is not landed. The relay answers once it has sent the transaction on; an expired blockhash, moved state or a tip too low for the congestion can still stop it.
  • HTTPS is the simple path. For the lowest latency use QUIC or WebSocket; the relay docs have QUIC clients in Rust and Python.
  • Bundles go to /v1/sendBundle as a txs array, with a Jito tip; see atomic bundles.

How we tested it: we ran the script unchanged against a local solana-test-validator, with RELAY_URL pointing at a mock of the endpoint built from the relay docs. The mock checked the x-api-key header, a body of exactly tx and simulate: false, the signature and the tip transfer, then passed the transaction to the local validator, where it landed and succeeded on three runs. The read calls also ran against mainnet's public RPC. Nothing was sent to mainnet or the real relay.

Code: one copy per sender, one tip

This builds one copy of a trade per sender, each with that sender's tip, all on one durable nonce. You need a nonce account whose authority is your trading wallet; SystemProgram.createNonceAccount makes one.

JavaScript
// nonce-variants.mjs: one trade, one copy per sender, at most one copy lands.
// Every copy uses the same durable nonce in place of a blockhash. The first copy
// to be processed advances the nonce; the others then fail the nonce check and are
// dropped without a fee.
import { SystemProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js";

// senders: [{ name, tipAccount: PublicKey, tipLamports }] from your own config.
// swapIxs: your compute-budget and swap instructions.
export async function buildVariants(connection, payer, noncePubkey, swapIxs, senders) {
  const { nonce } = await connection.getNonce(noncePubkey, "confirmed");
  return senders.map(({ name, tipAccount, tipLamports }) => {
    const instructions = [
      // Must be the first instruction of a durable-nonce transaction.
      SystemProgram.nonceAdvance({ noncePubkey, authorizedPubkey: payer.publicKey }),
      ...swapIxs,
      SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: tipAccount, lamports: tipLamports }),
    ];
    const tx = new VersionedTransaction(
      new TransactionMessage({ payerKey: payer.publicKey, recentBlockhash: nonce, instructions }).compileToV0Message(),
    );
    tx.sign([payer]);
    return { name, tx };
  });
}
// Send each copy to its own sender at the same time, then watch every signature:
// the first one to confirm is your trade, and only its tip was paid.

On a local test validator we sent three copies with three different tip accounts at once. In each of three runs, one copy landed, two were dropped, one tip account was paid, and resending a dropped copy was refused. Mind the two caveats above before relying on it.

FAQ

What is SWQoS on Solana?

Stake-weighted quality of service: a leader shares out QUIC connections, streams and transaction capacity on its transaction port by the stake of the identity behind each connection. Unstaked peers share a smaller, capped pool and are throttled first.

What are staked connections?

QUIC connections to a leader from a node whose identity has stake, or has stake assigned by that validator with --staked-nodes-overrides. A sender with staked connections gets through a busy leader more often than one without.

Is Jito the same as SWQoS?

No. SWQoS is how an Agave leader admits QUIC traffic. Jito is a separate auction, and only Jito-client leaders take its bundles. A relay can use both for one transaction.

Jito or a relay: which should I use?

For one transaction that must land fast, a relay. For legs that must land together, a Jito bundle, directly or through a relay that submits bundles.

Do I still need a priority fee if I pay a tip?

Usually. The tip pays the sender or bids in Jito's auction; the priority fee orders you in the leader's scheduler on paths that aren't a block builder. Helius Sender requires both, and Jito suggests a split for sendTransaction.

Did Alpenglow change how transactions are sent?

Transaction formats, fees and execution are unchanged. The routing change Solana flags is that leaders no longer emit ticks, so a sender that timed the slot from tick counts in shreds now needs its own slot clock. See Alpenglow for RPC and gRPC users.

Sources

Checked 31 October 2026.