Only this pageAll pages
Powered by GitBook
1 of 28

Temporal Docs

Nozomi

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Introduction

Land transactions faster and more consistently.

Nozomi is a fully custom proprietary client written by HPC and HFT engineers designed to land transactions as fast as possible.

How It Works

Nozomi runs custom hardware and staked connections across the cluster, forwarding your transaction to current and upcoming leaders and automatically optimizing the delivery path for each leader. You do not need to select endpoints based on the leader schedule. Nozomi handles routing for you. Your job is to submit fast (see Regions & Endpoints) and tip enough to win your slot (see Tipping).

Nozomi does not simulate your transactions; it routes them for the fastest possible delivery. If a transaction is not landing the way you expect, start with Troubleshooting.

Who Should Use It

  • Sniper Bots — Snipe tokens and mints before the competition.

  • DeFi Apps — Create a better UX so user transactions do not stall or fail.

  • Traders — Land with speed and precision to capture the most opportunities.

  • Liquidators — Be first to a liquidation transaction.

  • Jito Bundle Users — Get through the block engine faster and more efficiently.

  • Algorithmic Traders — Get predictability with your bots in all market conditions.

Getting started is self-service: create an account and generate your API key in the dashboard.

Join our for announcements and updates. For help, reach the team in Discord or through live chat in the dashboard.

JSON-RPC

Standard Solana JSON-RPC transaction submission through Nozomi.

The standard way to send transactions through Nozomi. Compatible with any Solana client: just replace your RPC URL with the Nozomi endpoint.

Field
Value

Get Access

Community & Support

Sign up →
Discord

Important: Solana defaults to base58 encoding. You must explicitly set "encoding": "base64" or you will get malformed transaction errors. are accepted here too and must use base64.

Returns the transaction signature as a JSON-RPC result on success.

Use JSON-RPC when you want a drop-in replacement for your existing Solana RPC. It returns a transaction signature and works with standard Solana client libraries.

For lower latency in browser clients or performance-sensitive backends, consider instead.

Method

POST

Path

/?c=<YOUR_API_KEY>

Content-Type

application/json

Request

Encoding

base64 (must be specified)

Request Body

Response

When to Use

v1 transactions
API v2
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
        "<YOUR_BASE_64_ENCODED_TXN_BYTES>",
        { "encoding": "base64" }
    ]
}
{
    "jsonrpc": "2.0",
    "id": 1,
    "result": "<TRANSACTION_SIGNATURE>"
}

cURL

Please specify base64 encoding, Solana recognizes base58 as default. If you do not specify, you might get malformed transaction error

cURL

curl https://nozomi.temporal.xyz/?c=<YOUR_API_KEY> \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
      "<YOUR_BASE_64_ENCODED_TXN_BYTES>",
      {
        "encoding": "base64"
      }
    ]
  }'
curl https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY> \
  -X POST \
  -H "Content-Type: text/plain" \
  -d '<YOUR_BASE_64_ENCODED_TXN_BYTES>'

TypeScript

Use full service RPC for fetching latest blockhash. Nozomi only supports sendTransaction.

import { Connection, PublicKey, Keypair, TransactionInstruction, SystemProgram, TransactionMessage, VersionedTransaction, TransactionSignature } from "@solana/web3.js";

const NOZOMI_ENDPOINT = "https://nozomi.temporal.xyz/?c=<YOUR_API_KEY>";
const NOZOMI_TIP = new PublicKey("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
const MIN_TIP_AMOUNT = 1_000_000;

const SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com";

async function sendNozomiTxn(ixns: TransactionInstruction[], signer: Keypair, nozomiRpcClient: Connection, solanaRpcClient: Connection): Promise<TransactionSignature> {
    const tipIxn = SystemProgram.transfer({
        fromPubkey: signer.publicKey,
        toPubkey: NOZOMI_TIP,
        lamports: MIN_TIP_AMOUNT
    });
    ixns.push(tipIxn);

    const { blockhash } = await solanaRpcClient.getLatestBlockhash();

    const messageV0 = new TransactionMessage({
        payerKey: signer.publicKey,
        recentBlockhash: blockhash,
        instructions: ixns,
    }).compileToV0Message();

    const versionedTxn = new VersionedTransaction(messageV0);

    versionedTxn.sign([signer]);

    return await nozomiRpcClient.sendTransaction(versionedTxn);
}

function buildIxns(): TransactionInstruction[] {
    // your instruction building logic here..
    return [];
}

async function main() {
    const nozomiRpcClient = new Connection(NOZOMI_ENDPOINT);

    const solanaRpcClient = new Connection(SOLANA_RPC_ENDPOINT);

    // replace with actual keypair loading logic
    const signer = Keypair.generate();

    const ixns = buildIxns();

    const signature = await sendNozomiTxn(ixns, signer, nozomiRpcClient, solanaRpcClient);

    console.log(`Transaction sent with signature: ${signature}`);
}

main().catch(err => {
    console.error(err);
});

Rust

Use full service RPC for fetching latest blockhash. Nozomi only supports sendTransaction.

use solana_client::rpc_client::RpcClient;
use solana_sdk::{message::Instruction, pubkey, pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};

const NOZOMI_ENDPOINT: &str = "https://nozomi.temporal.xyz/?c=<YOUR_API_KEY>";
const NOZOMI_TIP: Pubkey = pubkey!("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
const MIN_TIP_AMOUNT: u64 = 1_000_000;

const SOLANA_RPC_ENDPOINT: &str = "https://api.mainnet-beta.solana.com";

fn send_nozomi_txn(ixns: &mut Vec<Instruction>, signer: &Keypair, nozomi_rpc_client: &RpcClient, solana_rpc_client: &RpcClient) {
    let tip_ix = solana_system_interface::instruction::transfer(&signer.pubkey(), &NOZOMI_TIP, MIN_TIP_AMOUNT);
    ixns.push(tip_ix);

    let blockhash = solana_rpc_client.get_latest_blockhash().unwrap();
    let tx = Transaction::new_signed_with_payer(ixns, Some(&signer.pubkey()), &[signer], blockhash);

    nozomi_rpc_client.send_transaction(&tx).unwrap();
}

fn build_ixns() -> Vec<Instruction> {
    // your instruction building logic here..
    vec![]
}

fn main() {
    let nozomi_rpc_client = RpcClient::new(NOZOMI_ENDPOINT.to_string());

    let solana_rpc_client = RpcClient::new(SOLANA_RPC_ENDPOINT.to_string());

    let keypair = Keypair::new();

    let mut ixns = build_ixns();

    send_nozomi_txn(&mut ixns, &keypair, &nozomi_rpc_client, &solana_rpc_client);
}

Python

Use full service RPC for fetching latest blockhash.. Nozomi only supports sendTransaction.

import asyncio

from typing import List

from solders.pubkey import Pubkey
from solders.keypair import Keypair
from solders.signature import Signature
from solders.instruction import Instruction
from solana.transaction import Transaction
from solders.system_program import transfer, TransferParams

from solana.rpc.async_api import AsyncClient

NOZOMI_ENDPOINT = "https://nozomi.temporal.xyz/?c=<YOUR_API_KEY>"
NOZOMI_TIP = Pubkey.from_string("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq")
MIN_TIP_AMOUNT = 1_000_000

SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com"

async def send_nozomi_txn(ixns: List[Instruction], signer: Keypair, nozomi_rpc_client: AsyncClient, solana_rpc_client: AsyncClient) -> Signature:
    tip_ixn = transfer(TransferParams(
        from_pubkey=signer.pubkey(),
        to_pubkey=NOZOMI_TIP,
        lamports=MIN_TIP_AMOUNT
    ))
    ixns.append(tip_ixn)

    blockhash = (await solana_rpc_client.get_latest_blockhash()).value.blockhash
    txn = Transaction()

    for ixn in ixns:
        txn.add(ixn)

    # solanapy does not expose an encoding option via TxOpts
    return (await nozomi_rpc_client.send_transaction(txn, signer, recent_blockhash=blockhash)).value

def build_ixns() -> List[Instruction]:
    # your instruction building logic here..
    return []

async def main():
    nozomi_rpc_client = AsyncClient(NOZOMI_ENDPOINT)

    solana_rpc_client = AsyncClient(SOLANA_RPC_ENDPOINT)

    # replace with actual keypair loading logic
    signer = Keypair()

    ixns = build_ixns()

    signature = await send_nozomi_txn(ixns, signer, nozomi_rpc_client, solana_rpc_client)

    print(f"Transaction sent with signature: {signature}")

if __name__ == "__main__":
    asyncio.run(main())

JavaScript

Use full service RPC for fetching latest blockhash. Nozomi only supports sendTransaction.

import { Connection, PublicKey, Keypair, SystemProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js";

const NOZOMI_ENDPOINT = "https://nozomi.temporal.xyz/?c=<YOUR_API_KEY>";
const NOZOMI_TIP = new PublicKey("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
const MIN_TIP_AMOUNT = 1_000_000;

const SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com";

async function sendNozomiTxn(ixns, signer, nozomiRpcClient, solanaRpcClient) {
    const tipIxn = SystemProgram.transfer({
        fromPubkey: signer.publicKey,
        toPubkey: NOZOMI_TIP,
        lamports: MIN_TIP_AMOUNT
    });
    ixns.push(tipIxn);

    const { blockhash } = await solanaRpcClient.getLatestBlockhash();

    const messageV0 = new TransactionMessage({
        payerKey: signer.publicKey,
        recentBlockhash: blockhash,
        instructions: ixns,
    }).compileToV0Message();

    const versionedTxn = new VersionedTransaction(messageV0);

    versionedTxn.sign([signer]);

    return await nozomiRpcClient.sendTransaction(versionedTxn);
}

function buildIxns() {
    // your instruction building logic here..
    return [];
}

async function main() {
    const nozomiRpcClient = new Connection(NOZOMI_ENDPOINT);

    const solanaRpcClient = new Connection(SOLANA_RPC_ENDPOINT);

    // replace with actual keypair loading logic
    const signer = Keypair.generate();

    const ixns = buildIxns();

    const signature = await sendNozomiTxn(ixns, signer, nozomiRpcClient, solanaRpcClient);

    console.log(`Transaction sent with signature: ${signature}`);
}

main().catch(err => {
    console.error(err);
});

JavaScript

function encodeBatch(rawTxs) {
  if (rawTxs.length === 0 || rawTxs.length > 16) {
    throw new Error("batch must contain 1-16 transactions");
  }

  let total = 0;
  for (const tx of rawTxs) {
    // v1 (first byte 0x81) may be 4096 bytes; legacy and v0 stay at 1232.
    const max = tx[0] === 0x81 ? 4096 : 1232;
    if (tx.length < 66 || tx.length > max) {
      throw new Error(`invalid tx size: ${tx.length}`);
    }
    total += 2 + tx.length;
  }

  const out = Buffer.allocUnsafe(total);
  let off = 0;
  for (const tx of rawTxs) {
    out.writeUInt16BE(tx.length, off);
    off += 2;
    Buffer.from(tx).copy(out, off);
    off += tx.length;
  }
  return out;
}

async function sendBatch(endpoint, apiKey, rawTxs) {
  const body = encodeBatch(rawTxs);
  const res = await fetch(
    `${endpoint}/api/sendBatch?c=${apiKey}`,
    {
      method: "POST",
      headers: { "Content-Type": "application/octet-stream" },
      body,
    }
  );

  if (!res.ok) {
    const text = await res.text();
    throw new Error(`sendBatch failed (${res.status}): ${text}`);
  }
}

cURL

curl -X POST \
  "https://nozomi.temporal.xyz/api/sendBatch?c=YOUR_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @batch.bin

cURL

The body is the binary bundle: each signed transaction prefixed with its length as a big-endian u16. Build it with the encoder from the JavaScript page, or with the shell below from base64-encoded signed transactions.

# tx1.b64 and tx2.b64 hold the base64 of each signed transaction; tx1 carries the tip.
: > bundle.bin
for f in tx1.b64 tx2.b64; do
  base64 -d "$f" > tx.bin
  len=$(stat -c %s tx.bin)
  printf "$(printf '\\x%02x\\x%02x' $((len >> 8)) $((len & 255)))" >> bundle.bin
  cat tx.bin >> bundle.bin
done

curl -X POST \
  "https://nozomi.temporal.xyz/api/sendBundle?c=YOUR_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @bundle.bin

200 with an empty body means the whole bundle was accepted. Any other status means nothing was sent; the body says why (see Response).

Tip Stream

Stream Nozomi Tip Floors by Percentile

REST Endpoint

curl https://api.nozomi.temporal.xyz/tip_floor

WebSocket

wscat -c wss://api.nozomi.temporal.xyz/tip_stream

Schema

[
  {
    "time": "string (ISO 8601 timestamp)",
    "landed_tips_25th_percentile": "number",
    "landed_tips_50th_percentile": "number",
    "landed_tips_75th_percentile": "number",
    "landed_tips_95th_percentile": "number",
    "landed_tips_99th_percentile": "number"
  }
]

Regions & Endpoints

Regional endpoint URLs for all Nozomi APIs.

Authentication

All requests require your API key passed as a query parameter:

?c=<YOUR_API_KEY>

Don't have an API key yet? Create one in the dashboard. Sign up →

There is no IP whitelisting. Access is controlled entirely by the API key. A 401 response always means the key is missing or invalid, never that your IP is blocked.

API Paths

Method
Path
Response

JSON-RPC

Type
URL
Notes

Auto-routed is recommended for most users. It will always route your request to the closest regional server.

Example: https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY>

Nozomi offers several ways to submit. They differ in overhead, not in queue priority: your decides priority regardless of method.

Method
Overhead
Returns signature?
Notes

For the absolute lowest latency, use Batch Send over a direct http:// endpoint on a warm, reused connection (see Best Practices below).

Pin to a specific datacenter for lowest latency if you are co-located. Each region is available as a direct connection or through Cloudflare.

Direct endpoints support both http:// and https://. Cloudflare endpoints support https:// only.

Region
Direct
Cloudflare

All servers run custom hardware modifications.

Direct endpoints connect straight to the Nozomi server with no intermediary. This gives the lowest possible latency for servers and co-located infrastructure. Direct endpoints also let you connect over plain http://, which is the fastest option, because https:// (TLS) has to encrypt every transaction you send, adding latency to each request. Use http:// for lowest latency; use https:// only when you need encryption in transit.

Cloudflare endpoints route through Cloudflare's network before reaching Nozomi. Residential ISPs often have better backbone connectivity to Cloudflare's edge than to individual datacenters, which can make proxied endpoints faster for users on home or mobile connections. Cloudflare also handles TLS termination at the edge, reducing handshake latency.

Use direct if you are running from a datacenter or VPS with good peering. Use Cloudflare if you are on a residential connection, have variable network quality, or are building a browser-based application.

For latency-critical workloads:

  • Use Batch Send over a direct http:// endpoint. Plain HTTP avoids per-transaction TLS encryption; batch avoids JSON and per-request overhead.

  • Co-locate near your target region and pin to it. Latency introduced before your request reaches Nozomi (your client → the region) can decide races that Nozomi cannot fix downstream.

  • Keep one warm connection open and reuse it. Establishing a new connection per transaction pays the handshake cost every time. See . If your workload genuinely cannot hold a connection open, consider the .

For the highest landing probability, send the same transaction to multiple regional endpoints simultaneously. Rate limits are applied per region, so sending the same transaction to multiple regions will not count against your rate limit. It effectively multiplies your throughput and adds redundancy.

Nozomi also cross-forwards internally between regional servers, so the Regions view in the dashboard reflects where your transactions landed, not the endpoints you submitted to.

Use a single API key and send each transaction once per key (per region). Splitting traffic across multiple keys, or rotating keys with a delay, does not improve landing. It raises your failure/spam rate and can hurt your . If you need more throughput, request a higher rate limit (see ).

If you are integrating Nozomi into a browser-based application:

  • Send each transaction to both a direct and a Cloudflare endpoint at the same time. Network conditions vary across users: some will be faster through Cloudflare, others through a direct connection. Sending to both ensures the fastest path wins.

  • Use API v2 or Batch Send with Content-Type: text/plain or application/octet-stream to skip the CORS preflight OPTIONS request. Standard JSON-RPC with Content-Type: application/json triggers a preflight that adds 50–100ms of latency.

Tipping

Overview

Every Nozomi transaction must include a tip: a standard Solana system transfer instruction to one of the Nozomi tip addresses. The default minimum tip is 0.001 SOL.

Transactions that tip below the minimum are silently dropped: you will not receive an error. If you are seeing transactions disappear with no response, check your tip amount first. A lower per-account minimum can be arranged for qualifying high-volume flows; reach out through the dashboard.

You only pay when your transaction lands. The tip is an instruction inside your transaction, so if the transaction fails, the tip is never charged. This means you can intentionally fail a transaction if, for example, you detect that someone else already captured the opportunity you were targeting.

How Tips Are Used

Delivery path
What happens

Block builder (Jito or Harmonic)

Your tip is the primary lever. Nozomi orders the transactions it holds by tip, and Nozomi uses your tip to bid on your behalf with the block builders it routes through (such as Jito and Harmonic). On a block-builder path, your tip is what drives your ordering. When two transactions tip the same amount, the one that arrived first wins, so latency still matters.

Your priority fee matters on its own when your transaction lands through a path that is not a block builder. Whether it helps depends on your strategy and where your flow tends to land, so it is worth testing rather than assuming it does.

When multiple transactions touch the same writable account, they compete in an auction over that conflicting state. More than one can still land in the same slot; your tip is what wins you position in that auction.

  • Start with 100% of your bid in the Nozomi tip.

  • Only add a priority fee if you observe it improving landing for your specific strategy.

  • If you do set a priority fee, it is evaluated per compute unit (compute unit price × compute unit limit), so over-requesting compute units dilutes it. Set a compute unit limit that reflects what your transaction actually uses.

Check the current (/tip_floor) to see what tips are landing right now. Those percentiles are landed tips across all users, not a landing-probability curve. The tip you actually need scales with how contended the accounts you touch are.

Nozomi automatically retries your transaction against a recent blockhash until it is either confirmed or the blockhash expires. Higher-tipped transactions are retried more aggressively. You do not need to implement retry logic on your end. Client-side resubmission of the same transaction wastes your rate limit and lowers your .

Send your tip to any one of the addresses below. Rotate to a different random address for each transaction: this avoids write lock contention on a single account and improves landing rates. Distributing across fee-payer accounts helps too, since a shared fee-payer serializes the same way a shared tip account does.

If you reference tip addresses through an address lookup table, every address in that table must be a recognized Nozomi tip account; an unrecognized address will not be credited as a tip.

#
Address

Dedicated (private) tip addresses are not self-serve. They are reserved for high-volume clients and partnerships and are provisioned case by case on request. Reach out through the dashboard with your expected volume and use case. Once approved, they appear in your dashboard.

At low or moderate volume, dedicated addresses do not improve landing: rotating the public addresses above already avoids write-lock contention. They matter only at high submission rates, and they let Nozomi attribute your flow for analytics.

Default Nozomi keys are optimized for speed and are not sandwich-protected. For protection, Nozomi offers a separate MEV Protect key that routes your transactions only through a whitelist of trusted validators, keeping them away from adversarial validators.

  • It is available on request: ask through the dashboard's live chat.

  • There is a tradeoff: routing through a smaller validator set is slower and carries a higher chance of expiration. The protection level is tunable: more protection means more latency.

  • Protection is reduced, not eliminated.

For additional protection on swaps, regardless of which key you use:

  • Set a strict slippage tolerance.

  • Calculate and enforce minAmountOut in your transaction instructions.

If you were using an MEV Protect key and still believe you were sandwiched, report it to support with the transaction signatures so it can be investigated.

API v2

High-performance transaction submission with reduced overhead.

A faster alternative to JSON-RPC that eliminates JSON parsing overhead and CORS preflight latency. Recommended for browser clients and performance-sensitive backends.

Request

Field
Value

Method

POST

Path

/api/sendTransaction2?c=<YOUR_API_KEY>

Content-Type

text/plain

The body must be base64. API v2 does not accept raw binary transaction bytes. If you want to submit raw binary, use , which works for a single transaction too.

Returns an empty body with 200 OK on success. Does not return a transaction signature. Compute and track the signature client-side before submitting, then verify landing on-chain or in the dashboard.

Advantage
Detail

Use API v2 when you don't need the transaction signature returned in the response, and want the lowest possible submission latency. Ideal for browser-based applications and high-frequency backends.

If you need a transaction signature in the response, use instead.

Rust

Use full service RPC for fetching latest blockhash.

Python

Use full service RPC for fetching latest blockhash.

JavaScript

Use full service RPC for fetching latest blockhash.

TypeScript

Use full service RPC for fetching latest blockhash.

TCP Keep-Alive

To keep your TCP connection to our server alive and avoid reconnecting, periodically send a request to the /ping endpoint.


The server supports persistent connections with a keep-alive timeout of 65 seconds. This means:

  • If your connection is idle for more than 65 seconds, it will be closed.

  • To keep it open, send any request before that timeout expires.

In normal operation you do not need to tear down and re-establish the connection. Keep one warm connection open and reuse it for every submission.

QUIC Client

Low-overhead QUIC submission for workloads that cannot hold a persistent connection open.

Nozomi provides a native QUIC client for submitting transactions:

QUIC is not faster than the HTTP endpoints when you keep a warm connection open. Over a persistent, reused TCP connection, over a direct http:// endpoint is the lowest-latency path (see ).

QUIC is useful for a specific case: workloads that cannot hold a single connection open and have to re-establish a connection frequently. QUIC's connection setup (including session resumption) is cheaper than repeatedly completing a fresh TCP + TLS handshake, so you pay less latency per reconnect.

Use QUIC when:

  • You cannot maintain one long-lived, warm connection to a region.

Body

Base64-encoded transaction bytes

No CORS preflight

Saves 50–100ms per request from browser clients

Faster encoding

Base64 is faster to encode/decode than base58

Smaller payload

No JSON wrapper, plain text body

Lower overhead

No JSON parsing on the server side

Response

Why Use API v2

When to Use

Batch Send
JSON-RPC

6

nozc5yT15LazbLTFVZzoNZCwjh3yUtW86LoUyqsBu4L

7

nozFrhfnNGoyqwVuwPAW4aaGqempx4PU6g6D9CJMv7Z

8

nozievPk7HyK1Rqy1MPJwVQ7qQg2QoJGyP71oeDwbsu

9

noznbgwYnBLDHu8wcQVCEw6kDrXkPdKkydGJGNXGvL7

10

nozNVWs5N8mgzuD3qigrCG2UoKxZttxzZ85pvAQVrbP

11

nozpEGbwx4BcGp6pvEdAh1JoC2CQGZdU6HbNP1v2p6P

12

nozrhjhkCr3zXT3BiT4WCodYCUFeQvcdUkM7MqhKqge

13

nozrwQtWhEdrA6W8dkbt9gnUaMs52PdAv5byipnadq3

14

nozUacTVWub3cL4mJmGCYjKZTnE9RbdY5AP46iQgbPJ

15

nozWCyTPppJjRuw2fpzDhhWbW355fzosWSzrrMYB1Qk

16

nozWNju6dY353eMkMqURqwQEoM3SFgEKC6psLCSfUne

17

nozxNBgWohjR75vdspfxR5H9ceC7XXH99xpxhVGt3Bb

Tip is forwarded to the third-party block builder your transaction routes through

Staked connections

Tip pays for Nozomi's staked connections

1

TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq

2

noz3jAjPiHuBPqiSPkkugaJDkJscPuRhYnSpbi8UvC4

3

noz3str9KXfpKknefHji8L1mPgimezaiUyCHYMDv1GE

4

noz6uoYCDijhu1V7cutCpwxNiSovEwLdRHPwmgCGDNo

5

How Prioritization Works

Tuning your bid

Retries

Test with and without durable nonces. Block builders may deprioritize durable-nonce transactions because they are associated with spam, so a durable nonce can hurt landing on some paths. It does not always. If your strategy uses durable nonces, benchmark your flow with and without them to see which lands better for you.

Tip Addresses

Private Tip Addresses

Front-Running Protection

Tip Stream
priority

noz9EPNcT7WH6Sou3sr3GGjHQYVkN3DNirpbvDkv9YJ

Your process is short-lived, serverless, or otherwise reconnects often.

  • Network conditions force frequent reconnection.

  • If you can keep a connection open, prefer Batch Send or API v2 over a direct http:// endpoint instead, and use TCP Keep-Alive to keep it warm.

    • The transport you choose does not change your priority in Nozomi's queue: priority is driven by your tip, not by QUIC vs HTTP.

    • Like API v2 and Batch Send, QUIC submission does not return a transaction signature. Compute and track the signature client-side before submitting.

    When to Use It

    github.com/temporalxyz/nozomi-quic-client →
    Batch Send
    Regions & Endpoints → Choosing a submission method

    Notes

    
    use solana_client::rpc_client::RpcClient;
    use solana_sdk::{message::Instruction, pubkey, pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
    use base64::{Engine as _, engine::general_purpose};
    
    const NOZOMI_ENDPOINT: &str = "https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY>";
    const NOZOMI_TIP: Pubkey = pubkey!("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
    const MIN_TIP_AMOUNT: u64 = 1_000_000;
    
    const SOLANA_RPC_ENDPOINT: &str = "https://api.mainnet-beta.solana.com";
    
    fn send_nozomi_txn(
        ixns: &mut Vec<Instruction>,
        signer: &Keypair,
        nozomi_endpoint: &str,
        solana_rpc_client: &RpcClient
    ) -> Result<(), Box<dyn std::error::Error>> {
        let tip_ixn = solana_system_interface::instruction::transfer(
            &signer.pubkey(),
            &NOZOMI_TIP,
            MIN_TIP_AMOUNT
        );
        ixns.push(tip_ixn);
    
        let blockhash = solana_rpc_client.get_latest_blockhash()?;
        let txn = Transaction::new_signed_with_payer(
            ixns,
            Some(&signer.pubkey()),
            &[signer],
            blockhash
        );
    
        let txn_bytes = bincode::serialize(&txn)?;
    
        let txn_base64 = general_purpose::STANDARD.encode(&txn_bytes);
    
        let client = reqwest::blocking::Client::new();
        let response = client
            .post(nozomi_endpoint)
            .header("Content-Type", "text/plain")
            .body(txn_base64)
            .send()?;
    
        // api v2 does not return a signature, just check for success
        if response.status().is_success() {
            Ok(())
        } else {
            Err(Box::new(response.error_for_status().unwrap_err()))
        }
    }
    
    fn build_ixns() -> Vec<Instruction> {
        // your instruction building logic here..
        vec![]
    }
    
    fn main() {
        let solana_rpc_client = RpcClient::new(SOLANA_RPC_ENDPOINT.to_string());
    
        // replace with actual keypair loading logic
        let signer = Keypair::new();
    
        let mut ixns = build_ixns();
    
        if send_nozomi_txn(
            &mut ixns,
            &signer,
            NOZOMI_ENDPOINT,
            &solana_rpc_client
        ).is_ok() {
            println!("Transaction sent successfully");
        }
    }
    import aiohttp
    import asyncio
    import base64
    
    from typing import List
    
    from solders.pubkey import Pubkey
    from solders.keypair import Keypair
    from solders.instruction import Instruction
    from solders.transaction import Transaction
    from solders.system_program import transfer, TransferParams
    from solders.hash import Hash
    
    from solana.rpc.async_api import AsyncClient
    
    NOZOMI_ENDPOINT = "https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY>"
    NOZOMI_TIP = Pubkey.from_string("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq")
    MIN_TIP_AMOUNT = 1_000_000
    
    SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com"
    
    async def send_nozomi_txn(
        ixns: List[Instruction],
        signer: Keypair,
        nozomi_endpoint: str,
        solana_rpc_client: AsyncClient
    ) -> None:
        tip_ixn = transfer(TransferParams(
            from_pubkey=signer.pubkey(),
            to_pubkey=NOZOMI_TIP,
            lamports=MIN_TIP_AMOUNT
        ))
        ixns.append(tip_ixn)
    
        blockhash_resp = await solana_rpc_client.get_latest_blockhash()
        blockhash = blockhash_resp.value.blockhash
    
        txn = Transaction.new_signed_with_payer(
            ixns,
            signer.pubkey(),
            [signer],
            blockhash
        )
    
        txn_bytes = bytes(txn)
        txn_base64 = base64.b64encode(txn_bytes).decode('utf-8')
    
        async with aiohttp.ClientSession() as session:
            async with session.post(
                nozomi_endpoint,
                headers={"Content-Type": "text/plain"},
                data=txn_base64
            ) as response:
                # api v2 does not return a signature, just check for success
                if response.status >= 200 and response.status < 300:
                    print("Transaction sent successfully")
                else:
                    error_text = await response.text()
                    raise Exception(f"Transaction failed with status {response.status}: {error_text}")
    
    def build_ixns() -> List[Instruction]:
        # your instruction building logic here..
        return []
    
    async def main():
        solana_rpc_client = AsyncClient(SOLANA_RPC_ENDPOINT)
    
        # replace with actual keypair loading logic
        signer = Keypair()
    
        ixns = build_ixns()
    
        await send_nozomi_txn(ixns, signer, NOZOMI_ENDPOINT, solana_rpc_client)
    
    if __name__ == "__main__":
        asyncio.run(main())
    import { Connection, PublicKey, Keypair, SystemProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
    
    const NOZOMI_ENDPOINT = "https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY>";
    const NOZOMI_TIP = new PublicKey("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
    const MIN_TIP_AMOUNT = 1_000_000;
    
    const SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com";
    
    async function sendNozomiTxn(ixns, signer, nozomiEndpoint, solanaRpcClient) {
        const tipIxn = SystemProgram.transfer({
            fromPubkey: signer.publicKey,
            toPubkey: NOZOMI_TIP,
            lamports: MIN_TIP_AMOUNT
        });
        ixns.push(tipIxn);
    
        const { blockhash } = await solanaRpcClient.getLatestBlockhash();
    
        const messageV0 = new TransactionMessage({
            payerKey: signer.publicKey,
            recentBlockhash: blockhash,
            instructions: ixns,
        }).compileToV0Message();
    
        const versionedTxn = new VersionedTransaction(messageV0);
        versionedTxn.sign([signer]);
    
        const txnBytes = versionedTxn.serialize();
        const txnBase64 = Buffer.from(txnBytes).toString('base64');
    
        const response = await fetch(nozomiEndpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'text/plain',
            },
            body: txnBase64
        });
    
        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Transaction failed with status ${response.status}: ${errorText}`);
        }
    
        // api v2 does not return a signature, just check for success
        console.log('Transaction sent successfully');
    }
    
    function buildIxns() {
        // your instruction building logic here..
        return [];
    }
    
    async function main() {
        const solanaRpcClient = new Connection(SOLANA_RPC_ENDPOINT);
    
        // replace with actual keypair loading logic
        const signer = Keypair.generate();
    
        const ixns = buildIxns();
    
        await sendNozomiTxn(ixns, signer, NOZOMI_ENDPOINT, solanaRpcClient);
    }
    
    main().catch(err => {
        console.error(err);
    });
    import { Connection, PublicKey, Keypair, TransactionInstruction, SystemProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
    
    const NOZOMI_ENDPOINT = "https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY>";
    const NOZOMI_TIP = new PublicKey("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
    const MIN_TIP_AMOUNT = 1_000_000;
    
    const SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com";
    
    async function sendNozomiTxn(
        ixns: TransactionInstruction[],
        signer: Keypair,
        nozomiEndpoint: string,
        solanaRpcClient: Connection
    ): Promise<void> {
        const tipIxn = SystemProgram.transfer({
            fromPubkey: signer.publicKey,
            toPubkey: NOZOMI_TIP,
            lamports: MIN_TIP_AMOUNT
        });
        ixns.push(tipIxn);
    
        const { blockhash } = await solanaRpcClient.getLatestBlockhash();
    
        const messageV0 = new TransactionMessage({
            payerKey: signer.publicKey,
            recentBlockhash: blockhash,
            instructions: ixns,
        }).compileToV0Message();
    
        const versionedTxn = new VersionedTransaction(messageV0);
        versionedTxn.sign([signer]);
    
        const txnBytes = versionedTxn.serialize();
        const txnBase64 = Buffer.from(txnBytes).toString('base64');
    
        const response = await fetch(nozomiEndpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'text/plain',
            },
            body: txnBase64
        });
    
        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Transaction failed with status ${response.status}: ${errorText}`);
        }
    
        // api v2 does not return a signature, just check for success
        console.log('Transaction sent successfully');
    }
    
    function buildIxns(): TransactionInstruction[] {
        // your instruction building logic here..
        return [];
    }
    
    async function main() {
        const solanaRpcClient = new Connection(SOLANA_RPC_ENDPOINT);
    
        // replace with actual keypair loading logic
        const signer = Keypair.generate();
    
        const ixns = buildIxns();
    
        await sendNozomiTxn(ixns, signer, NOZOMI_ENDPOINT, solanaRpcClient);
    }
    
    main().catch(err => {
        console.error(err);
    });
    We recommend using the /ping endpoint:

    GET /ping

    This endpoint is lightweight, fast, and designed specifically to maintain your connection.


    Send a request to /ping every 60 seconds to keep the connection alive reliably.


    • This is not a health check; it's just a way to prevent idle disconnects.

    • Avoid pinging more often than needed.

    • Reusing one warm connection is the lowest-latency setup. If your workload genuinely cannot keep a connection open and has to reconnect frequently, the QUIC Client reconnects more cheaply.

    How Do I Keep the Connection Alive?

    Strategy

    Suggested Interval

    Notes

    while true; do
      curl -s https://nozomi.temporal.xyz/ping > /dev/null
      sleep 60
    done

    Higher

    Yes

    Drop-in Solana RPC replacement; use when you need the signature back.

    Lowest

    No

    Same binary framing as Batch Send, but 1–4 transactions land atomically and in order, or not at all.

    n/a

    No

    Only for workloads that cannot hold a persistent connection open. Not faster than a warm HTTP connection.

    lax1.nozomi.temporal.xyz

    lax.nozomi.temporal.xyz

    Frankfurt

    fra2.nozomi.temporal.xyz

    fra.nozomi.temporal.xyz

    Amsterdam

    ams1.nozomi.temporal.xyz

    ams.nozomi.temporal.xyz

    London

    lon1.nozomi.temporal.xyz

    lon.nozomi.temporal.xyz

    Tokyo

    tyo1.nozomi.temporal.xyz

    tyo.nozomi.temporal.xyz

    Singapore

    sgp1.nozomi.temporal.xyz

    sgp.nozomi.temporal.xyz

    /

    Transaction signature

    API v2

    /api/sendTransaction2

    Empty body, 200 OK

    Batch Send

    /api/sendBatch

    Empty body, 200 OK

    Send Bundle

    /api/sendBundle

    Empty body, 200 OK

    Auto-routed

    nozomi.temporal.xyz

    Via Cloudflare proxy

    Geo-DNS

    edge.nozomi.temporal.xyz

    Routes to nearest region

    Batch Send

    Lowest

    No

    Compact binary; fastest path even for a single transaction.

    API v2

    Low

    No

    Plain-text base64 body, no JSON parsing.

    Pittsburgh

    pit1.nozomi.temporal.xyz

    pit.nozomi.temporal.xyz

    Newark

    ewr1.nozomi.temporal.xyz

    ewr.nozomi.temporal.xyz

    Ashburn

    ash1.nozomi.temporal.xyz

    ash.nozomi.temporal.xyz

    Base URLs

    Choosing a Submission Method

    Regional Endpoints

    Direct vs Cloudflare

    Best Practices

    Lowest latency

    Send to multiple regions

    One key, not many

    Frontend clients

    tip
    TCP Keep-Alive
    QUIC Client
    priority
    FAQ → Rate Limits

    Los Angeles

    Batch Send

    Submit multiple raw Solana transactions in a single request using a compact binary format. Reduces per-request overhead when you have multiple transactions ready to send.

    Request

    Field
    Value

    Method

    POST

    Path

    /api/sendBatch?c=<YOUR_API_KEY>

    Content-Type

    application/octet-stream

    Constraint
    Value

    Members may be legacy, v0 or v1 transactions in any mix; each is checked against its own version's limit.

    The body is a concatenation of length-prefixed transactions. No JSON, no separators.

    Each length prefix is a big-endian u16 indicating the size of the following transaction bytes.

    Returns an empty body with 200 OK when all transactions are accepted.

    Status
    Meaning

    All error responses are plain text with a descriptive message.

    sendBatch is stream-processed: transactions are forwarded as they are parsed. If transaction N fails, transactions 1 through N-1 may already be accepted. There is no rollback.

    Always track transaction signatures client-side before submitting so you can reconcile partial success.

    Batch Send is the lowest-overhead submission path (compact binary, no JSON, no per-transaction wrapper), which makes it the fastest option even for a single transaction. Use it when you want the lowest latency, and especially when you have multiple transactions ready to submit and want to minimize HTTP round trips.

    Because responses carry no transaction signature, compute and track signatures client-side before submitting, then verify landing on-chain or in the dashboard.

    For a drop-in Solana RPC replacement that returns a signature, use instead. For the lowest latency overall, send batches over a direct http:// endpoint on a warm connection (see ).

    FAQ

    How do I get access?

    Nozomi is self-service: create an account and generate an API key in the dashboard. Sign up →

    Support & Community

    Join our Discord for announcements and updates.

    Need help? You can reach the team:

    • In our Discord.

    • Through live chat in the dashboard (dashboard.nozomi.temporal.xyz).

    Rate Limits

    Each API key has a rate limit enforced per key, per region, per second. Because it is per region, sending the same transaction to multiple regions does not count against your limit in a single region. Fanning out is the first thing to try if you are hitting the limit.

    New keys start with a default of 5 requests/second. A 429 response means you exceeded your per-key, per-region rate, or your traffic was flagged as spam.

    When you hit 429:

    1. Fan out across regions. Limits are per region, so sending the same signed transaction to several regional endpoints multiplies your effective throughput. See .

    2. Use one key and stop client-side retries. Nozomi retries server-side; resubmitting the same transaction burns your limit and hurts your priority. Do not spread traffic across multiple keys.

    3. Then request an increase (below).

    Request a higher rate limit from your key in the dashboard (dashboard.nozomi.temporal.xyz). Requests are reviewed based on your landing rate, success rate, and overall transaction quality, not purchased. Keeping those healthy is what qualifies you for a higher limit.

    Nozomi prioritizes transactions based on your tip and your historical success and landing rates. Consistently landing successful transactions keeps your priority high; a high failure rate lowers it.

    To keep your priority high:

    • Don't send transactions you expect to fail: check account state before submitting.

    • Avoid stale blockhashes.

    • Let Nozomi handle retries instead of resubmitting client-side.

    No. Nozomi does not simulate transactions. It routes them for the fastest possible delivery.

    See the page. The most common causes are a tip below the minimum, over-requested compute units, client-side retry loops, and a low landing/success history.

    Default keys are speed-optimized and are not sandwich-protected. A separate MEV Protect key is available on request. See . As a backstop on any key, set a strict slippage tolerance and enforce minAmountOut.

    No. Nozomi does not offer a shredstream feed, and there is no Jito-style bundle-subscribe / gRPC subscription feed. The low-latency product is transaction submission, including atomic bundle submission via . For tip data, use the .

    The dashboard (dashboard.nozomi.temporal.xyz) is the self-service surface: create or import API keys, request rate-limit increases, view per-transaction tip / success analytics, and manage tip accounts.

    • Sign in with Google or with email and password.

    • Teams are supported. You can invite other people to your team from the dashboard.

    Requesting a Higher Limit

    Priority

    Does Nozomi Simulate Transactions?

    Why Didn't My Transaction Land?

    How Do I Protect Against Sandwiching / MEV?

    Does Nozomi Offer a Shredstream or Bundle Feed?

    Managing Keys & Account

    Regions & Endpoints → Send to multiple regions
    Troubleshooting
    Tipping → Front-Running Protection
    Send Bundle
    Tip Stream
    JSON-RPC
    Send Bundle
    QUIC Client

    Body

    Binary-encoded transaction batch

    Max transactions per batch

    16

    Min transaction size

    66 bytes

    Max transaction size

    1,232 bytes (legacy/v0), 4,096 bytes (v1)

    Max body size

    65,568 bytes

    200

    All accepted

    400

    Framing error, size violation, parse failure, or insufficient tip

    401

    Invalid or missing API key

    429

    Rate limited

    500

    Internal error

    Limits

    Wire Format

    Response

    Partial Success

    When to Use

    JSON-RPC
    Regions & Endpoints → Best Practices

    JavaScript

    Use full service RPC for fetching latest blockhash.

    Two transactions from one signer, tip in the first, sent as one atomic bundle. Both are signed with the same blockhash so they expire together.

    [len_hi][len_lo][tx_bytes...][len_hi][len_lo][tx_bytes...]...
    import { Connection, Keypair, PublicKey, SystemProgram, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
    import bs58 from "bs58";
    
    const NOZOMI_ENDPOINT = "https://nozomi.temporal.xyz";
    const NOZOMI_API_KEY = "<YOUR_API_KEY>";
    const NOZOMI_TIP = new PublicKey("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
    const TIP_LAMPORTS = 1_000_000; // 0.001 SOL, the default minimum
    
    const SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com";
    
    function encodeBundle(rawTxs) {
      if (rawTxs.length === 0 || rawTxs.length > 4) {
        throw new Error("bundle must contain 1-4 transactions");
      }
      let total = 0;
      for (const tx of rawTxs) {
        // v1 (first byte 0x81) may be 4096 bytes; legacy and v0 stay at 1232.
        const max = tx[0] === 0x81 ? 4096 : 1232;
        if (tx.length < 66 || tx.length > max) {
          throw new Error(`invalid tx size: ${tx.length}`);
        }
        total += 2 + tx.length;
      }
      const out = Buffer.allocUnsafe(total);
      let off = 0;
      for (const tx of rawTxs) {
        out.writeUInt16BE(tx.length, off);
        off += 2;
        Buffer.from(tx).copy(out, off);
        off += tx.length;
      }
      return out;
    }
    
    async function sendBundle(endpoint, apiKey, rawTxs) {
      const res = await fetch(`${endpoint}/api/sendBundle?c=${apiKey}`, {
        method: "POST",
        headers: { "Content-Type": "application/octet-stream" },
        body: encodeBundle(rawTxs),
      });
      if (!res.ok) {
        const text = await res.text();
        throw new Error(`sendBundle failed (${res.status}): ${text}`);
      }
    }
    
    function signTx(ixns, signer, blockhash) {
      const message = new TransactionMessage({
        payerKey: signer.publicKey,
        recentBlockhash: blockhash,
        instructions: ixns,
      }).compileToV0Message();
      const tx = new VersionedTransaction(message);
      tx.sign([signer]);
      return tx;
    }
    
    function buildFirstIxns() {
      // your instruction building logic for the first transaction..
      return [];
    }
    
    function buildSecondIxns() {
      // your instruction building logic for the second transaction..
      return [];
    }
    
    async function main() {
      const rpc = new Connection(SOLANA_RPC_ENDPOINT);
      const signer = Keypair.generate(); // replace with actual keypair loading logic
    
      // One blockhash for every member so the bundle expires as a unit.
      const { blockhash } = await rpc.getLatestBlockhash();
    
      // Tip goes in the first member; the second is untipped.
      const first = signTx(
        [
          ...buildFirstIxns(),
          SystemProgram.transfer({ fromPubkey: signer.publicKey, toPubkey: NOZOMI_TIP, lamports: TIP_LAMPORTS }),
        ],
        signer,
        blockhash
      );
      const second = signTx(buildSecondIxns(), signer, blockhash);
    
      // sendBundle returns no signatures: record them before submitting.
      const signatures = [first, second].map((tx) => bs58.encode(tx.signatures[0]));
      console.log("bundle signatures", signatures);
    
      await sendBundle(NOZOMI_ENDPOINT, NOZOMI_API_KEY, [first.serialize(), second.serialize()]);
      console.log("bundle accepted");
    
      // Confirm landing: every member lands in the same slot, or none does.
      const statuses = await rpc.getSignatureStatuses(signatures);
      console.log(statuses.value.map((s) => s?.slot ?? null));
    }
    
    main().catch((err) => {
      console.error(err);
    });

    Troubleshooting

    Why a transaction didn't land, and how to diagnose it.

    Most "Nozomi isn't landing my transactions" reports come down to a handful of causes. Work through this page before opening a ticket.

    Transaction Outcomes

    Outcome
    Meaning

    Landed

    The transaction was included in a block.

    Succeeded

    It landed and executed without error.

    Reverted

    It landed but failed on-chain (e.g. slippage, program error). You still land. Because the tip is an instruction inside the transaction, a reverted transaction that includes the tip still pays.

    A low success rate with a healthy landing rate is an on-chain problem (slippage, competition, program logic), not a delivery problem.

    What wins you a slot:

    • Your tip. Nozomi orders transactions by tip and uses it to bid with the block builders it routes through (such as Jito and Harmonic), so on a block-builder path your tip drives ordering. When tips are equal, earlier arrival wins.

    • Your priority fee. This matters on its own when you land through a path that is not a block builder. Whether it helps depends on your strategy, so test it rather than assuming.

    When multiple transactions touch the same writable account, they compete in an auction over that conflicting state. More than one can still land in the same slot; your tip wins you position in that auction.

    See for how to tune your bid.

    • Durable nonces. Block builders may deprioritize durable-nonce transactions because they are associated with spam, so they can hurt landing on some paths (not always). Benchmark your flow with and without durable nonces to see which lands better. See .

    • Tip below the minimum. Transactions tipping under the minimum are silently dropped: no error is returned. See .

    • Over-requested compute units. If you set a priority fee, it is measured per compute unit, so requesting more compute units than your transaction uses dilutes it. Set a compute unit limit that reflects actual usage.

    When you compare two setups, make sure the test is actually valid:

    • Do not race two paths with the same durable nonce or the identical signed transaction: only one can land, so the comparison is meaningless.

    • Allow a warm-up period on a newly issued key: your priority builds as your landing rate is observed.

    • Send each transaction once per key. Fan the same signed transaction out to multiple regions (limits are per-region) rather than duplicating it on one endpoint. See .

    Status
    Meaning

    A transient invalid instruction data response is usually safe to retry. If it persists, send support the transaction signature.

    Rejections specific to the new transaction format (-32005..-32009 on JSON-RPC, 400/503 on the binary APIs) are listed on the page. -32006, "not active on this cluster", means the SIMD-0296 feature gate is not live yet.

    If you have ruled out the above, open a ticket with example transaction signatures and the slot/epoch you expected them to land in. That lets support trace the delivery path.

    Client-side retry loops. Nozomi already retries server-side. Re-sending the same transaction yourself burns your rate limit and lowers your priority. See .
  • Low priority. A high failure rate lowers your priority. See .

  • Expired

    The transaction expired before landing. Either the blockhash expired, or, for a durable-nonce transaction, the nonce was already advanced by another transaction.

    400

    Bad request: encoding, size, framing, or an insufficient/missing tip.

    401

    Missing or invalid API key. Pass it as ?c=<YOUR_API_KEY>. There is no IP whitelisting; a 401 always means the key is wrong.

    429

    Rate limited: you exceeded your per-key, per-region limit, or your traffic was flagged as spam. See .

    500

    Internal error: retry.

    How a Slot Is Won

    Common Self-Inflicted Causes

    Benchmarking Correctly

    Error Reference

    Escalating

    Tipping → How prioritization works
    Tipping → Retries
    Tipping → Overview
    FAQ → Rate Limits
    Transaction v1
    FAQ → Rate Limits
    FAQ → Priority
    FAQ → Rate Limits

    Transaction v1

    Sending SIMD-0296 (v1) transactions through Nozomi.

    Nozomi accepts the new Solana transaction format defined by SIMD-0296 and serialized per SIMD-0385, alongside legacy and v0 transactions. Every submission method recognises it, nothing changes in how you authenticate, tip, or choose an endpoint.

    Availability

    Nozomi admits v1 transactions only while the SIMD-0296 feature gate is active on mainnet. Before activation every v1 submission is rejected with a specific error (see below), so you can ship v1 support ahead of time and let the network turn it on. Nozomi checks the feature gate itself; there is nothing to enable on your key.

    How Nozomi tells v1 apart

    A v1 transaction starts with the byte 0x81, followed by the signature count. Its signatures sit at the end of the serialized bytes rather than the front. Nozomi reads the version from the first byte, validates the envelope, and treats the transaction like any other from then on: same tip rules, same priority, same delivery.

    Encoding

    Use base64 everywhere. JSON-RPC requires "encoding": "base64" for every transaction, and for v1 in particular base58 is not supported beyond legacy sizes. API v2 takes the base64 body as before; Batch Send and Send Bundle take raw bytes.

    Size limits

    Method
    Maximum v1 transaction
    Maximum body

    v1 transactions may use the full 4,096 bytes the format allows on every method. Legacy and v0 transactions keep their 1,232-byte limit. A transaction over 1,232 bytes must be base64 or raw binary; base58 cannot carry it.

    JSON-RPC returns these as error.code in a 200 response. API v2, Batch Send and Send Bundle return the HTTP status with a one-line body.

    Condition
    JSON-RPC code
    HTTP status
    What to do

    Everything else behaves as for legacy transactions: 401 for a bad key, 429 for rate limits, 400 for a missing or insufficient tip.

    The v1 types live in the solana-message 4.x and solana-transaction 4.x crates, serialized with wincode. This example self-transfers one lamport and tips Nozomi, then posts through API v2.

    Points that differ from legacy transactions:

    • The compute unit limit and priority fee are fields of TransactionConfig, not ComputeBudget instructions.

    • Serialize with wincode, not bincode.

    • The signature is the last 64 bytes of the serialized transaction, not the first.

    Other SDKs will expose the same format as they add SIMD-0385 support. Nozomi only sees the bytes, so any correctly serialized v1 transaction is accepted.

    A single Batch Send or Send Bundle body may mix legacy, v0 and v1 members; each is checked on its own. Nozomi never rewrites your transaction, so a v1 transaction is delivered to the leader exactly as you signed it.

    Send Bundle

    Submit up to four transactions that land together, in order, or not at all.

    Submit a group of 1–4 signed transactions as one atomic unit. Either every transaction in the bundle lands in the same slot, in the order you sent them, or none of them lands. Use it when one transaction only makes sense if another one landed first: a setup step followed by a swap, a swap followed by your own backrun, or a position change followed by a cleanup.

    Bundles use the same compact binary framing as . The difference is the guarantee: Batch Send forwards each transaction independently, while Send Bundle delivers the whole group to block builders as one unit.

    Field
    Value

    -32007

    400

    The first byte is neither a legacy signature count nor 0x81.

    Too large

    -32002

    400

    Over 4,096 bytes, or a legacy/v0 transaction over 1,232.

    Large v1 has no delivery path right now

    -32009

    503

    Transient on that region; retry, or fall back to a smaller transaction. Send Bundle never returns this: bundle members go to the block builders, not the leader link.

    Base58 input too long

    -32008

    n/a

    Switch to base64.

    JSON-RPC

    4,096 bytes

    96 KiB (JSON-RPC batch)

    API v2

    4,096 bytes

    5,472 bytes of base64

    Batch Send

    4,096 bytes per member

    65,568 bytes

    Send Bundle

    4,096 bytes per member

    v1 not yet active on mainnet

    -32006

    400

    Wait for the feature gate; send legacy/v0 meanwhile.

    v1 disabled on this endpoint

    -32005

    400

    Temporary operator kill switch; retry later or use another region.

    Errors

    Building a v1 transaction in Rust

    Mixing versions

    16,392 bytes

    Unknown transaction prefix

    [dependencies]
    solana-message = "4.4"
    solana-transaction = { version = "4.1", features = ["wincode"] }
    wincode = "0.5"
    solana-hash = "3"
    solana-keypair = "3"
    solana-pubkey = "3"
    solana-signer = "3"
    solana-system-interface = { version = "2", features = ["bincode"] }
    solana-rpc-client = "3"
    base64 = "0.22"
    reqwest = { version = "0.12", features = ["blocking"] }
    use solana_keypair::Keypair;
    use solana_message::{v1, VersionedMessage};
    use solana_pubkey::pubkey;
    use solana_signer::Signer;
    use solana_system_interface::instruction::transfer;
    use solana_transaction::versioned::VersionedTransaction;
    
    const NOZOMI_TIP: solana_pubkey::Pubkey = pubkey!("TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq");
    const MIN_TIP_AMOUNT: u64 = 1_000_000;
    
    fn build_v1(signer: &Keypair, blockhash: solana_hash::Hash) -> Vec<u8> {
        let ixs = vec![
            transfer(&signer.pubkey(), &signer.pubkey(), 1),
            transfer(&signer.pubkey(), &NOZOMI_TIP, MIN_TIP_AMOUNT),
        ];
        // Compute limit and priority fee move into the v1 header; no ComputeBudget instructions.
        let config = v1::TransactionConfig::empty()
            .with_compute_unit_limit(20_000)
            .with_priority_fee(0);
        let message = v1::Message::try_compile_with_config(&signer.pubkey(), &ixs, blockhash, config).unwrap();
        let tx = VersionedTransaction::try_new(VersionedMessage::V1(message), &[signer]).unwrap();
        wincode::serialize(&tx).unwrap() // first byte is 0x81
    }
    
    fn main() {
        let signer = Keypair::new();
        let rpc = solana_rpc_client::rpc_client::RpcClient::new("https://api.mainnet-beta.solana.com".to_string());
        let blockhash = rpc.get_latest_blockhash().unwrap();
        let bytes = build_v1(&signer, blockhash);
    
        let body = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &bytes);
        let response = reqwest::blocking::Client::new()
            .post("https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY>")
            .header("content-type", "text/plain")
            .body(body)
            .send()
            .unwrap();
        assert!(response.status().is_success(), "{}", response.text().unwrap_or_default());
    }

    /api/sendBundle?c=<YOUR_API_KEY>

    Content-Type

    application/octet-stream

    Body

    Binary-encoded bundle, 1–4 length-prefixed transactions

    Send Bundle is available on every regional and auto-routed endpoint, over http:// and https://.

    Constraint
    Value

    Transactions per bundle

    1–4

    Min transaction size

    66 bytes

    Max transaction size

    1,232 bytes (legacy/v0), 4,096 bytes ()

    Max body size

    16,392 bytes

    Duplicate signatures within a bundle

    Rejected

    Members may be legacy, v0 or v1 transactions in any mix; each is checked against its own version's limit. A large v1 member needs no special delivery path: bundles always go to the block builders.

    The body is a concatenation of length-prefixed transactions. No JSON, no base64, no outer count, no separators. Order matters: transactions land in the order they appear in the body.

    Each length prefix is a big-endian u16 giving the size of the following signed transaction bytes. A zero length, a truncated final transaction, or trailing bytes rejects the whole request.

    At least one transaction in the bundle must pay a Nozomi tip of at least your account's minimum (default 0.001 SOL) and never less than 1,001 lamports. The other transactions may be untipped. Tip exemptions do not apply to bundles.

    The tip can be in any member, but it is common to put it in the first. Because the tip is an instruction inside your own transaction, you only pay it when the bundle lands. Rotate tip addresses between bundles as you would for single transactions.

    Returns an empty body with 200 OK when the whole bundle is accepted and queued for delivery. Landing is determined asynchronously; compute and track your signatures client-side before submitting, then confirm on-chain or in the dashboard.

    Unlike Batch Send, a bundle is validated in full before anything is queued. There is no partial acceptance: any error below means nothing from that request was sent.

    Status
    Body
    Meaning

    200

    empty

    Bundle accepted

    400

    Failed to parse transaction

    Framing error, more than four members, a duplicate signature, or a transaction whose instructions cannot be read

    400

    Malformed transaction string

    A member under 66 bytes

    All error bodies are plain text.

    • Block builders only. Bundles are delivered to block-builder paths (such as Jito and Harmonic), which are the paths that can guarantee atomic inclusion. They do not go over staked connections or direct-to-leader paths.

    • Nozomi appends its own fee transaction. You may see one extra small transaction from Nozomi immediately after your members in the block. Your transactions are delivered byte-for-byte as you signed them.

    • Once per builder by default. A bundle is submitted once to each eligible block builder. If your strategy needs repeated resubmission until the blockhash expires, ask support to configure it for your key.

    • No cross-region forwarding. Single transactions are cross-forwarded between Nozomi regions; bundles are not. For redundancy, send the same bundle to more than one yourself. Rate limits are per region.

    • Deduplication. Re-sending an identical bundle (same signatures in the same order) shortly after the first is accepted with 200 but not delivered again. A bundle with a different member set or order is treated as new. Once any member confirms, every pending copy of the bundle is dropped and further submissions return 409.

    • Sign every member with the same recent blockhash so they expire together. Durable-nonce members are accepted, including bundles where every member uses a durable nonce; see the note on durable nonces in Tipping → Retries.

    • Put transactions in the body in the order they must execute.

    • Keep each member's compute-unit limit close to what it uses. Bundles compete for block space as a unit.

    • Do not include a transaction that may already have landed on its own. If it confirms before the bundle is delivered, the bundle is dropped.

    Use Send Bundle when correctness depends on ordering or all-or-nothing execution across several transactions. If you simply have several independent transactions to submit, use Batch Send: it is lower overhead, has no per-bundle size cap, and lands each transaction on whichever path is fastest.

    From a browser, application/octet-stream skips the CORS preflight OPTIONS request, the same as Batch Send.

    Method

    POST

    Request

    Batch Send

    Path

    [len_hi][len_lo][tx_bytes...][len_hi][len_lo][tx_bytes...]...

    Limits

    Wire Format

    Tipping

    Response

    How Bundles Are Delivered

    Building a Bundle

    When to Use

    400

    Transaction too large

    A legacy/v0 member over 1,232 bytes, a v1 member over 4,096 bytes, or a body over 16,392 bytes

    400

    Insufficient tip

    No member pays a recognized tip at or above the minimum

    401

    Unauthorized

    Missing or invalid API key

    403

    Forbidden

    Your key is not permitted to send bundles, or a member uses a program your key is restricted from

    409

    Transaction already confirmed

    A member has already landed on-chain; the bundle can no longer be atomic

    429

    Too Many Requests

    Rate limited. Each member consumes one rate-limit token, so a four-transaction bundle costs four

    500

    Internal Server Error

    Retry

    503

    not ready

    Bundle delivery is temporarily unavailable on this server; retry against another region

    regional endpoint
    v1