Overview

Glaive is a high-performance Solana transaction sender. It maintains persistent connections to current and upcoming slot leaders, delivering your transactions directly into the validator processing pipeline.

The API is JSON-RPC compatible. You can use it as a drop-in replacement for your existing RPC endpoint when sending transactions.

Quickstart

Get a transaction submitted through Glaive in under a minute.

1. Get an API key

Contact the Glaive team to get an API key.

2. Send a transaction

curl "http://ams1.glaive.trade?key=YOUR_API_KEY" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
      "<base64-encoded-tx>",
      { "encoding": "base64" }
    ]
  }'
const res = await fetch(
  `http://ams1.glaive.trade?key=${process.env.GLAIVE_KEY}`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "sendTransaction",
      params: [
        "<base64-encoded-tx>",
        { encoding: "base64" },
      ],
    }),
  }
);

const { result: signature } = await res.json();
let client = reqwest::Client::new();
let url = format!("http://ams1.glaive.trade?key={}", api_key);

let res = client.post(&url)
    .json(&serde_json::json!({
        "jsonrpc": "2.0",
        "id": 1,
        "method": "sendTransaction",
        "params": [
            "<base64-encoded-tx>",
            { "encoding": "base64" }
        ]
    }))
    .send()
    .await?;

let body: serde_json::Value = res.json().await?;
let signature = &body["result"];
body := []byte(`{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
        "<base64-encoded-tx>",
        { "encoding": "base64" }
    ]
}`)

url := fmt.Sprintf("http://ams1.glaive.trade?key=%s", apiKey)
resp, err := http.Post(url, "application/json", bytes.NewReader(body))
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
signature := result["result"]

Authentication

All requests require a key query parameter. Each API key has an individually configured rate limit (TPS).

http://ams1.glaive.trade?key=your-api-key-here

Keys are UUID v4 strings. Treat them like passwords. Do not commit them to source control or share them in client-side code.

Don't have a key yet? Contact us to get one.

Regions

Glaive runs in multiple regions. Use the endpoint closest to your infrastructure.

RegionEndpointStatus
Amsterdam 1ams1.glaive.tradeLive
Amsterdam 2ams2.glaive.tradeLive
Frankfurtfra.glaive.tradeLive
New York (Newark)ny.glaive.tradeLive

Endpoint

Glaive runs regional endpoints. Send requests to the nearest region:

POST http://ams1.glaive.trade?key=YOUR_API_KEY

The endpoint accepts standard Solana JSON-RPC format.

sendTransaction

Submit a signed transaction for delivery to current and upcoming leaders. Glaive offers three ways to submit transactions:

JSON-RPC

POST /

Submit a signed transaction using the standard Solana JSON-RPC format.

Request

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sendTransaction",
  "params": [
    "<base64-encoded-transaction>",
    {
      "encoding": "base64"
    }
  ]
}

Parameters

FieldTypeDescription
params[0]stringBase64-encoded signed transaction
encodingstringTransaction encoding: base64 or base58. Default: base64

Response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<transaction-signature>"
}

Example

curl "http://ams1.glaive.trade?key=YOUR_API_KEY" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
      "<base64-encoded-transaction>",
      { "encoding": "base64" }
    ]
  }'
// transaction = a signed VersionedTransaction
const serialized = transaction.serialize();
const base64Tx = Buffer.from(serialized).toString("base64");

const res = await fetch(
  `http://ams1.glaive.trade?key=${process.env.GLAIVE_KEY}`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "sendTransaction",
      params: [base64Tx, { encoding: "base64" }],
    }),
  }
);

const { result, error } = await res.json();
if (error) throw new Error(error.message);
console.log("Signature:", result);
// tx = a signed VersionedTransaction
let serialized = bincode::serialize(&tx)?;
let base64_tx = base64::engine::general_purpose::STANDARD.encode(&serialized);

let client = reqwest::Client::new();
let url = format!("http://ams1.glaive.trade?key={}", api_key);

let res = client.post(&url)
    .json(&serde_json::json!({
        "jsonrpc": "2.0",
        "id": 1,
        "method": "sendTransaction",
        "params": [base64_tx, { "encoding": "base64" }]
    }))
    .send()
    .await?;

let body: serde_json::Value = res.json().await?;
println!("Signature: {}", body["result"]);
// serializedTx = signed transaction bytes ([]byte)
base64Tx := base64.StdEncoding.EncodeToString(serializedTx)

body := []byte(`{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "sendTransaction",
    "params": [
        "` + base64Tx + `",
        { "encoding": "base64" }
    ]
}`)

url := fmt.Sprintf("http://ams1.glaive.trade?key=%s", apiKey)
resp, err := http.Post(url, "application/json", bytes.NewReader(body))
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
if e, ok := result["error"]; ok {
    log.Fatalf("Error: %v", e)
}
fmt.Println("Signature:", result["result"])

Plain

POST /plain

A lightweight alternative to JSON-RPC. Post a base64-encoded transaction as the raw request body to /plain. No JSON wrapping needed.

Request

POST http://ams1.glaive.trade/plain?api-key=YOUR_API_KEY

The request body is the base64-encoded signed transaction as plain text.

Query Parameters

ParameterTypeDescription
api-keystringYour API key (required when rate limiting is enabled)
mev-protectstringSet to true to enable MEV protection

Response

{
  "result": "<transaction-signature>"
}

Error

Returns HTTP 400 with:

{
  "error": "<error-message>"
}

Example

curl "http://ams1.glaive.trade/plain?api-key=YOUR_API_KEY" \
  -X POST \
  -d '<base64-encoded-transaction>'
// transaction = a signed VersionedTransaction
const serialized = transaction.serialize();
const base64Tx = Buffer.from(serialized).toString("base64");

const res = await fetch(
  `http://ams1.glaive.trade/plain?api-key=${process.env.GLAIVE_KEY}`,
  {
    method: "POST",
    body: base64Tx,
  }
);

const { result, error } = await res.json();
if (error) throw new Error(error);
console.log("Signature:", result);
// tx = a signed VersionedTransaction
let serialized = bincode::serialize(&tx)?;
let base64_tx = base64::engine::general_purpose::STANDARD.encode(&serialized);

let client = reqwest::Client::new();
let url = format!("http://ams1.glaive.trade/plain?api-key={}", api_key);

let res = client.post(&url)
    .body(base64_tx)
    .send()
    .await?;

let body: serde_json::Value = res.json().await?;
println!("Signature: {}", body["result"]);
// serializedTx = signed transaction bytes ([]byte)
base64Tx := base64.StdEncoding.EncodeToString(serializedTx)

url := fmt.Sprintf("http://ams1.glaive.trade/plain?api-key=%s", apiKey)
resp, err := http.Post(url, "text/plain", strings.NewReader(base64Tx))
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
if e, ok := result["error"]; ok {
    log.Fatalf("Error: %v", e)
}
fmt.Println("Signature:", result["result"])

Binary

POST /binary

The lowest overhead option. Post the raw binary transaction bytes to /binary. No encoding, no JSON.

Request

POST http://ams1.glaive.trade/binary?api-key=YOUR_API_KEY

The request body is the raw serialized transaction bytes.

Query Parameters

ParameterTypeDescription
api-keystringYour API key (required when rate limiting is enabled)
mev-protectstringSet to true to enable MEV protection

Response

{
  "result": "<transaction-signature>"
}

Error

Returns HTTP 400 with:

{
  "error": "<error-message>"
}

Example

curl "http://ams1.glaive.trade/binary?api-key=YOUR_API_KEY" \
  -X POST \
  -H "Content-Type: application/octet-stream" \
  --data-binary "$SERIALIZED_TX_BYTES"
// transaction = a signed VersionedTransaction
const serialized = transaction.serialize();

const res = await fetch(
  `http://ams1.glaive.trade/binary?api-key=${process.env.GLAIVE_KEY}`,
  {
    method: "POST",
    body: serialized,
  }
);

const { result, error } = await res.json();
if (error) throw new Error(error);
console.log("Signature:", result);
// tx = a signed VersionedTransaction
let serialized = bincode::serialize(&tx)?;

let client = reqwest::Client::new();
let url = format!("http://ams1.glaive.trade/binary?api-key={}", api_key);

let res = client.post(&url)
    .body(serialized)
    .send()
    .await?;

let body: serde_json::Value = res.json().await?;
println!("Signature: {}", body["result"]);
// serializedTx = signed transaction bytes ([]byte)
url := fmt.Sprintf("http://ams1.glaive.trade/binary?api-key=%s", apiKey)
resp, err := http.Post(url, "application/octet-stream", bytes.NewReader(serializedTx))
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
if e, ok := result["error"]; ok {
    log.Fatalf("Error: %v", e)
}
fmt.Println("Signature:", result["result"])

MEV Protection

Glaive can exclude validators known to sandwich transactions from your transaction's delivery path. This is disabled by default. To opt in, add mev_protect=true to the query string:

POST http://ams1.glaive.trade?key=YOUR_API_KEY&mev_protect=true

When enabled, Glaive will skip any leader that appears on its sandwiching validator blacklist. The blacklist is maintained by the Glaive team and updated as new sandwich activity is detected.

Without mev_protect=true, transactions are sent to all upcoming leaders regardless of known sandwich behavior.

For the /plain and /binary endpoints, use mev-protect=true instead.

Minimum Tip

All transactions sent through Glaive must include a transfer of at least 0.001 SOL (1,000,000 lamports) to one of the Glaive tip accounts. Transactions without a valid tip will be rejected with a -32000 JSON-RPC error.

Tip Accounts

Your transaction must include a SOL transfer to any one of these Glaive tip addresses:

GLaiv4GMRYQmthatDS98uQT4HoucgxWT8NeJz6oSwxeU
GLaivL5uPrDpvd1wTtvat38KGqb5WLhEdqQfnmNd3oNr
GLaivinAWh21NaJMhtExtD5G2gZs1xnvaYVZmwqobWZL
GLaivJSUL71FcocYa8tks5vpVyYzvaDMHtyrzfQF2ABr
GLaivRU6eDKrta3p3psFAWPEFLzCjeMHGpPUuQqTjtyv
GLaivq5dU8qHayz9Qf13LjPfVy3SmUhbmickfGiZdmfh
import { SystemProgram, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";

const GLAIVE_TIP_ACCOUNTS = [
  "GLaiv4GMRYQmthatDS98uQT4HoucgxWT8NeJz6oSwxeU",
  "GLaivL5uPrDpvd1wTtvat38KGqb5WLhEdqQfnmNd3oNr",
  "GLaivinAWh21NaJMhtExtD5G2gZs1xnvaYVZmwqobWZL",
  "GLaivJSUL71FcocYa8tks5vpVyYzvaDMHtyrzfQF2ABr",
  "GLaivRU6eDKrta3p3psFAWPEFLzCjeMHGpPUuQqTjtyv",
  "GLaivq5dU8qHayz9Qf13LjPfVy3SmUhbmickfGiZdmfh",
];

const TIP_LAMPORTS = 0.001 * LAMPORTS_PER_SOL; // 1_000_000
const tipAccount = GLAIVE_TIP_ACCOUNTS[Math.floor(Math.random() * GLAIVE_TIP_ACCOUNTS.length)];

tx.add(
  SystemProgram.transfer({
    fromPubkey: payer.publicKey,
    toPubkey: new PublicKey(tipAccount),
    lamports: TIP_LAMPORTS,
  })
);
use solana_sdk::{system_instruction, pubkey::Pubkey};
use rand::seq::SliceRandom;
use std::str::FromStr;

const TIP_LAMPORTS: u64 = 1_000_000; // 0.001 SOL

const GLAIVE_TIP_ACCOUNTS: &[&str] = &[
    "GLaiv4GMRYQmthatDS98uQT4HoucgxWT8NeJz6oSwxeU",
    "GLaivL5uPrDpvd1wTtvat38KGqb5WLhEdqQfnmNd3oNr",
    "GLaivinAWh21NaJMhtExtD5G2gZs1xnvaYVZmwqobWZL",
    "GLaivJSUL71FcocYa8tks5vpVyYzvaDMHtyrzfQF2ABr",
    "GLaivRU6eDKrta3p3psFAWPEFLzCjeMHGpPUuQqTjtyv",
    "GLaivq5dU8qHayz9Qf13LjPfVy3SmUhbmickfGiZdmfh",
];

let tip_account = GLAIVE_TIP_ACCOUNTS
    .choose(&mut rand::thread_rng())
    .unwrap();
let tip_pubkey = Pubkey::from_str(tip_account).unwrap();

let tip_ix = system_instruction::transfer(
    &payer.pubkey(),
    &tip_pubkey,
    TIP_LAMPORTS,
);
tx.add(tip_ix);
import (
    "math/rand"
    "github.com/gagliardetto/solana-go"
    "github.com/gagliardetto/solana-go/programs/system"
)

const TipLamports = 1_000_000 // 0.001 SOL

var GlaiveTipAccounts = []solana.PublicKey{
    solana.MustPublicKeyFromBase58("GLaiv4GMRYQmthatDS98uQT4HoucgxWT8NeJz6oSwxeU"),
    solana.MustPublicKeyFromBase58("GLaivL5uPrDpvd1wTtvat38KGqb5WLhEdqQfnmNd3oNr"),
    solana.MustPublicKeyFromBase58("GLaivinAWh21NaJMhtExtD5G2gZs1xnvaYVZmwqobWZL"),
    solana.MustPublicKeyFromBase58("GLaivJSUL71FcocYa8tks5vpVyYzvaDMHtyrzfQF2ABr"),
    solana.MustPublicKeyFromBase58("GLaivRU6eDKrta3p3psFAWPEFLzCjeMHGpPUuQqTjtyv"),
    solana.MustPublicKeyFromBase58("GLaivq5dU8qHayz9Qf13LjPfVy3SmUhbmickfGiZdmfh"),
}

tipAccount := GlaiveTipAccounts[rand.Intn(len(GlaiveTipAccounts))]

tipIx := system.NewTransferInstruction(
    TipLamports,
    payer.PublicKey(),
    tipAccount,
).Build()

Keep Alive

If you maintain persistent HTTP connections to Glaive (recommended for lowest latency), send periodic requests to the /health endpoint to keep them alive.

curl https://<region>.glaive.trade/health

The endpoint returns 200 OK with no authentication required. A ping every 15 to 30 seconds is enough to prevent idle connection timeouts.

Rate Limits

Each API key has a configured transactions-per-second (TPS) limit. When exceeded, requests return 429 Too Many Requests.

Error Codes

HTTP Status Codes

HTTP StatusMeaning
200Request processed (check JSON-RPC response for result or error)
401Missing or invalid API key
429Rate limit exceeded

JSON-RPC Error Codes

Validation and processing errors return HTTP 200 with a JSON-RPC error object in the response body.

CodeMeaning
-32600Invalid request (malformed JSON-RPC)
-32601Method not found (only sendTransaction is supported)
-32000Transaction rejected (invalid encoding, missing tip, etc.)

FAQ

Does Glaive replace my RPC provider?

Only for sendTransaction. You still need a standard RPC for reading state, fetching blockhashes, and other queries.

Which encoding formats are supported?

Base64 (default) and base58. We recommend base64 for smaller payload size.

What happens if the leader rotates mid-send?

Glaive fans out to multiple upcoming leaders simultaneously. Leader rotation during delivery is expected and handled automatically.

Is there a testnet endpoint?

Not currently. Glaive targets mainnet-beta only.

Contact

To get an API key or ask questions, reach out to us directly.