# 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](/nozomi/endpoints)) and tip enough to win your slot (see [Tipping](/nozomi/tipping-and-faq)).

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](/nozomi/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.

## Get Access

Getting started is self-service: create an account and generate your API key in the dashboard. [**Sign up →**](https://dashboard.nozomi.temporal.xyz/sign-up?utm_source=docs\&utm_medium=cta\&utm_campaign=nozomi_access\&utm_content=readme)

## Community & Support

Join our [**Discord**](https://discord.gg/8Tw6wMdvtx) for announcements and updates. For help, reach the team in Discord or through live chat in the dashboard.


# 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 →**](https://dashboard.nozomi.temporal.xyz/sign-up?utm_source=docs\&utm_medium=cta\&utm_campaign=nozomi_access\&utm_content=endpoints)

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   | `/`                     | Transaction signature |
| API v2     | `/api/sendTransaction2` | Empty body, `200 OK`  |
| Batch Send | `/api/sendBatch`        | Empty body, `200 OK`  |

## Base URLs

| Type        | URL                        | Notes                    |
| ----------- | -------------------------- | ------------------------ |
| Auto-routed | `nozomi.temporal.xyz`      | Via Cloudflare proxy     |
| Geo-DNS     | `edge.nozomi.temporal.xyz` | Routes to nearest region |

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>`

## Choosing a Submission Method

Nozomi offers several ways to submit. They differ in overhead, not in queue priority: your [tip](/nozomi/tipping-and-faq) decides priority regardless of method.

| Method                                              | Overhead   | Returns signature? | Notes                                                                                                     |
| --------------------------------------------------- | ---------- | ------------------ | --------------------------------------------------------------------------------------------------------- |
| [Batch Send](/nozomi/batch-send)                    | **Lowest** | No                 | Compact binary; **fastest path even for a single transaction**.                                           |
| [API v2](/nozomi/transaction-submission-api-v2)     | Low        | No                 | Plain-text base64 body, no JSON parsing.                                                                  |
| [JSON-RPC](/nozomi/transaction-submission-json-rpc) | Higher     | Yes                | Drop-in Solana RPC replacement; use when you need the signature back.                                     |
| [QUIC Client](/nozomi/transaction-submission-quic)  | n/a        | No                 | Only for workloads that cannot hold a persistent connection open. Not faster than a warm HTTP connection. |

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

## Regional Endpoints

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                |
| ----------- | -------------------------- | ------------------------- |
| 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` |
| Los Angeles | `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` |

All servers run custom hardware modifications.

## Direct vs Cloudflare

**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.

## Best Practices

#### Lowest latency

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 [TCP Keep-Alive](/nozomi/keeping-your-tcp-connection-alive). If your workload genuinely cannot hold a connection open, consider the [QUIC Client](/nozomi/transaction-submission-quic).

#### Send to multiple regions

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.

#### One key, not many

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 [priority](/nozomi/faq#priority). If you need more throughput, request a higher rate limit (see [FAQ → Rate Limits](/nozomi/faq#rate-limits)).

#### Frontend clients

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) | Tip is forwarded to the third-party block builder your transaction routes through |
| Staked connections               | Tip pays for Nozomi's staked connections                                          |

## How Prioritization Works

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.

### Tuning your bid

* 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 Stream](/nozomi/tip-stream) (`/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.

## Retries

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 [priority](/nozomi/faq#priority).

{% hint style="info" %}
**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.
{% endhint %}

## Tip Addresses

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                                       |
| -- | --------------------------------------------- |
| 1  | `TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq` |
| 2  | `noz3jAjPiHuBPqiSPkkugaJDkJscPuRhYnSpbi8UvC4` |
| 3  | `noz3str9KXfpKknefHji8L1mPgimezaiUyCHYMDv1GE` |
| 4  | `noz6uoYCDijhu1V7cutCpwxNiSovEwLdRHPwmgCGDNo` |
| 5  | `noz9EPNcT7WH6Sou3sr3GGjHQYVkN3DNirpbvDkv9YJ` |
| 6  | `nozc5yT15LazbLTFVZzoNZCwjh3yUtW86LoUyqsBu4L` |
| 7  | `nozFrhfnNGoyqwVuwPAW4aaGqempx4PU6g6D9CJMv7Z` |
| 8  | `nozievPk7HyK1Rqy1MPJwVQ7qQg2QoJGyP71oeDwbsu` |
| 9  | `noznbgwYnBLDHu8wcQVCEw6kDrXkPdKkydGJGNXGvL7` |
| 10 | `nozNVWs5N8mgzuD3qigrCG2UoKxZttxzZ85pvAQVrbP` |
| 11 | `nozpEGbwx4BcGp6pvEdAh1JoC2CQGZdU6HbNP1v2p6P` |
| 12 | `nozrhjhkCr3zXT3BiT4WCodYCUFeQvcdUkM7MqhKqge` |
| 13 | `nozrwQtWhEdrA6W8dkbt9gnUaMs52PdAv5byipnadq3` |
| 14 | `nozUacTVWub3cL4mJmGCYjKZTnE9RbdY5AP46iQgbPJ` |
| 15 | `nozWCyTPppJjRuw2fpzDhhWbW355fzosWSzrrMYB1Qk` |
| 16 | `nozWNju6dY353eMkMqURqwQEoM3SFgEKC6psLCSfUne` |
| 17 | `nozxNBgWohjR75vdspfxR5H9ceC7XXH99xpxhVGt3Bb` |

## Private Tip Addresses

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.

## Front-Running Protection

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.


# 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.

## Request

| Field        | Value                          |
| ------------ | ------------------------------ |
| Method       | `POST`                         |
| Path         | `/?c=<YOUR_API_KEY>`           |
| Content-Type | `application/json`             |
| Encoding     | **base64** (must be specified) |

**Important:** Solana defaults to base58 encoding. You must explicitly set `"encoding": "base64"` or you will get malformed transaction errors.

## Request Body

```json
{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
        "<YOUR_BASE_64_ENCODED_TXN_BYTES>",
        { "encoding": "base64" }
    ]
}
```

## Response

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

```json
{
    "jsonrpc": "2.0",
    "id": 1,
    "result": "<TRANSACTION_SIGNATURE>"
}
```

## When to Use

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 [API v2](/nozomi/transaction-submission-api-v2) instead.


# Rust

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

```rust
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.

```python
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.

```javascript
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);
});
```


# TypeScript

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

```typescript
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);
});

```


# cURL

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

```bash
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"
      }
    ]
  }'
```


# 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`                             |
| Body         | Base64-encoded transaction bytes         |

The body must be **base64**. API v2 does not accept raw binary transaction bytes. If you want to submit raw binary, use [Batch Send](/nozomi/batch-send), which works for a single transaction too.

## Response

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.

## Why Use API v2

| Advantage         | Detail                                          |
| ----------------- | ----------------------------------------------- |
| 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              |

## When to Use

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 [JSON-RPC](/nozomi/transaction-submission-json-rpc) instead.


# Rust

Use full service RPC for fetching latest blockhash.

```rust

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");
    }
}
```


# Python

Use full service RPC for fetching latest blockhash.

```python
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())
```


# JavaScript

Use full service RPC for fetching latest blockhash.

```javascript
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);
});
```


# TypeScript

Use full service RPC for fetching latest blockhash.

```typescript
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);
});
```


# cURL

```bash
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>'
```


# 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`        |
| Body         | Binary-encoded transaction batch  |

## Limits

| Constraint                 | Value        |
| -------------------------- | ------------ |
| Max transactions per batch | 16           |
| Min transaction size       | 66 bytes     |
| Max transaction size       | 1,232 bytes  |
| Max body size              | 19,744 bytes |

## Wire Format

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

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

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

## Response

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

| Status | Meaning                                                           |
| ------ | ----------------------------------------------------------------- |
| `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                                                    |

All error responses are plain text with a descriptive message.

## Partial Success

`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.

## When to Use

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 [JSON-RPC](/nozomi/transaction-submission-json-rpc) instead. For the lowest latency overall, send batches over a **direct `http://` endpoint** on a warm connection (see [Regions & Endpoints → Best Practices](/nozomi/endpoints#best-practices)).


# JavaScript

```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) {
    if (tx.length < 66 || tx.length > 1232) {
      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

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


# QUIC Client

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

Nozomi provides a native QUIC client for submitting transactions:

[**github.com/temporalxyz/nozomi-quic-client →**](https://github.com/temporalxyz/nozomi-quic-client)

## When to Use It

QUIC is **not** faster than the HTTP endpoints when you keep a warm connection open. Over a persistent, reused TCP connection, [Batch Send](/nozomi/batch-send) over a direct `http://` endpoint is the lowest-latency path (see [Regions & Endpoints → Choosing a submission method](/nozomi/endpoints#choosing-a-submission-method)).

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.
* 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](/nozomi/keeping-your-tcp-connection-alive) to keep it warm.

## Notes

* The transport you choose does **not** change your priority in Nozomi's queue: priority is driven by your [tip](/nozomi/tipping-and-faq), 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.


# Tip Stream

Stream Nozomi Tip Floors by Percentile

#### REST Endpoint

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

#### WebSocket

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

#### Schema

```json
[
  {
    "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"
  }
]
```


# TCP Keep-Alive

## How Do I Keep the Connection Alive?

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

***

### Strategy

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.

We recommend using the `/ping` endpoint:

`GET /ping`

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

***

### Suggested Interval

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

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

***

### Notes

* 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](/nozomi/transaction-submission-quic) reconnects more cheaply.


# 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. |
| **Expired**   | The transaction expired before landing. Either the blockhash expired, or, for a durable-nonce transaction, the nonce was already advanced by another transaction.                                |

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

## How a Slot Is Won

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 [Tipping → How prioritization works](/nozomi/tipping-and-faq#how-prioritization-works) for how to tune your bid.

## Common Self-Inflicted Causes

* **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 [Tipping → Retries](/nozomi/tipping-and-faq#retries).
* **Tip below the minimum.** Transactions tipping under the minimum are **silently dropped**: no error is returned. See [Tipping → Overview](/nozomi/tipping-and-faq#overview).
* **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.
* **Client-side retry loops.** Nozomi already retries server-side. Re-sending the same transaction yourself burns your rate limit and lowers your priority. See [FAQ → Rate Limits](/nozomi/faq#rate-limits).
* **Low priority.** A high failure rate lowers your priority. See [FAQ → Priority](/nozomi/faq#priority).

## Benchmarking Correctly

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 [FAQ → Rate Limits](/nozomi/faq#rate-limits).

## Error Reference

| Status | Meaning                                                                                                                                           |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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 [FAQ → Rate Limits](/nozomi/faq#rate-limits). |
| `500`  | Internal error: retry.                                                                                                                            |

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

## Escalating

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.


# FAQ

## How do I get access?

Nozomi is self-service: create an account and generate an API key in the dashboard. [**Sign up →**](https://dashboard.nozomi.temporal.xyz/sign-up?utm_source=docs\&utm_medium=cta\&utm_campaign=nozomi_access\&utm_content=faq)

## Support & Community

Join our [**Discord**](https://discord.gg/8Tw6wMdvtx) for announcements and updates.

Need help? You can reach the team:

* In our [**Discord**](https://discord.gg/8Tw6wMdvtx).
* 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 [Regions & Endpoints → Send to multiple regions](/nozomi/endpoints#send-to-multiple-regions).
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).

## Requesting a Higher Limit

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.

## Priority

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.

## Does Nozomi Simulate Transactions?

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

## Why Didn't My Transaction Land?

See the [Troubleshooting](/nozomi/troubleshooting) 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.

## How Do I Protect Against Sandwiching / MEV?

Default keys are speed-optimized and are not sandwich-protected. A separate **MEV Protect** key is available on request. See [Tipping → Front-Running Protection](/nozomi/tipping-and-faq#front-running-protection). As a backstop on any key, set a strict slippage tolerance and enforce `minAmountOut`.

## Does Nozomi Offer a Shredstream or Bundle Feed?

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. For tip data, use the [Tip Stream](/nozomi/tip-stream).

## Managing Keys & Account

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.


