Complete Guide to AllenHark Relay: HTTP & QUIC Integration for Solana Trading

Step-by-step guide to integrating AllenHark Relay for private Solana transaction submission. Learn API authentication, tip validation, QUIC vs HTTP setup, and best practices for MEV trading.

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

AllenHark Relay is a production-grade transaction relay service designed for traders, market makers, and automated systems that need low-latency transaction dispatch to the Solana network.

This comprehensive integration guide covers everything from API key authentication to advanced QUIC Protocol setup, tip validation requirements, and performance optimization strategies.

What is AllenHark Relay?

AllenHark Relay is a private transaction propagation network that bypasses the public Solana gossip network to deliver your transactions directly to validator TPU (Transaction Processing Unit) ports with:

  • Persistent QUIC connections — no per-request handshake when the transaction is ready
  • MEV protection through private transaction routing
  • Leader-aware delivery to current and upcoming block leaders
  • Jito bundles: up to five transactions, atomic and in order
  • API key authentication with configurable rate limits

Why Use a Private Relay?

Standard Solana RPC submission has critical limitations:

  1. Slow: extra hops through the gossip network before a leader sees it
  2. Public: Transactions visible in mempool (vulnerable to front-running)
  3. Unreliable: Packets dropped during congestion
  4. No prioritization: Your high-value trades compete with spam

AllenHark Relay solves these problems with private connections straight to the current leader.


Service Overview

Two Integration Options

AllenHark Relay supports two protocols to balance simplicity and performance:

Option 1: QUIC Protocol (Recommended)

Best for: Production trading systems, HFT bots, MEV operations

Advantages:

  • No per-request handshake — the connection is already open
  • 0-RTT connection resumption (instant reconnection)
  • Connection multiplexing (multiple transactions over one connection)
  • Consistently lower dispatch overhead than HTTPS

Port: 4433
Endpoint: relay.allenhark.com:4433 (Frankfurt alias — 88.216.36.108:4433)

Other regions: ams.relay.allenhark.com / 84.32.104.38:4433, ny.relay.allenhark.com / 74.214.174.50:4433, tyo.relay.allenhark.com / 88.216.188.28:4433.

Option 2: HTTPS REST API

Best for: Development, testing, low-frequency operations

Advantages:

  • Simple integration (standard HTTP client)
  • Wide language support (any HTTP library)
  • Easy debugging (use curl, Postman, etc.)

Latency: set by your distance to the region, plus a handshake on every new connection — use QUIC or WebSocket when latency matters
Endpoint: https://relay.allenhark.com/v1/sendTx

[!TIP] Start with HTTPS for development, migrate to QUIC for production. The API format is identical.


Getting Started

Step 1: Obtain API Credentials

AllenHark Relay uses API key authentication. Create your key in the Relay tab of the console.

You'll receive:

  • API Key: an ak_-prefixed key, e.g. ak_1234567890
  • Rate Limit: a limit per key, raised by the optional Relay Dedicated upgrade
  • Support: Discord community access

The key is required on every request: as an x-api-key header, or as an ?api-key= query parameter. Over QUIC it is the first line of the stream; over WebSocket it is presented at upgrade.

Step 2: Understand Tip Requirements

You pay per transaction. Every transaction must include a tip transfer of at least 0.001 SOL to one of the AllenHark tip wallets, paid inside the transaction itself as a top-level SystemProgram.transfer instruction.

Versioned (v0) transactions with address lookup tables are supported, provided the tip wallet is present in the static account keys. If the tip account is only reachable through a lookup table, the relay rejects the transaction with tip_hidden_in_alt.

Tip Wallets (select random):

JavaScript
const TIP_WALLETS = [
  "hark1zxc5Rz3K8Kquz79WPWFEgNCFeJnsMJ16f22uNP",
  "harkm2BTWxZuszoNpZnfe84jRbQTg6KGHaQBmWzDGQQ",
  "hark4CwtTnN2y9FaxjcFBAJdJqQrpouu5pgEixfqdEz",
  "harkoJfnM6dxrJydx5eVmDVwAgwC94KbhuxF69UbXwP",
  "hark6hUDUTekc1DGxWdJcuyDZwf6pJdCxd4SXAVtta6",
  "harkoTvFpKSrEQduYrNHXCurARVT19Ud3BnFhVxabos",
  "harkEpXoJv5qVzHaN7HSuUAd6PHjyMcFMcDYBMDJCEQ",
  "harkyXDdZSoJGyCxa24t2QXx1poPyp8YfghbtpzGSzK",
  "harkR2YJ4Dpt4UDJTcBirjnSPBhNpQFcoFkNpCkVqNk",
  "harkRBygM8pHYe4K8eBjfxyEX19oJn3LepFjvNbLbyi",
  "harkYFxB6DuUFNwDLvA5CQ66KpfRvFgUoVypMagNcmd",
];

function getRandomTipWallet() {
  return TIP_WALLETS[Math.floor(Math.random() * TIP_WALLETS.length)];
}

Minimum tip amount: see the relay docs; the relay rejects a lower tip with tip_insufficient.


Integration Guide: HTTPS REST API

The simplest way to get started. Perfect for development and testing.

API Specification

Endpoint: POST https://relay.allenhark.com/v1/sendTx
Alternative: POST https://relay.allenhark.com/broadcast

Request format:

JSON
{
  "tx": "BASE64_ENCODED_TRANSACTION",
  "simulate": false
}

Authentication Methods

Option 1: Header Authentication (Recommended)

Terminal
curl -X POST https://relay.allenhark.com/v1/sendTx \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "tx": "BASE64_ENCODED_TRANSACTION",
    "simulate": false
  }'

Option 2: Query Parameter

Terminal
curl -X POST "https://relay.allenhark.com/v1/sendTx?api-key=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tx": "BASE64_ENCODED_TRANSACTION",
    "simulate": false
  }'

JavaScript/TypeScript Example

TypeScript
import fetch from 'node-fetch';
import { Transaction, Keypair, SystemProgram, LAMPORTS_PER_SOL, Connection } from '@solana/web3.js';
import bs58 from 'bs58';

const ALLENHARK_API_KEY = process.env.ALLENHARK_API_KEY!;
const TIP_WALLETS = [
  "hark1zxc5Rz3K8Kquz79WPWFEgNCFeJnsMJ16f22uNP",
  // ... (include all tip wallets)
];

async function sendTransactionViaRelay(
  transaction: Transaction,
  payer: Keypair
): Promise<{ status: string; request_id: string }> {
  // Add tip instruction
  const tipWallet = TIP_WALLETS[Math.floor(Math.random() * TIP_WALLETS.length)];
  const tipAmount = 0.001 * LAMPORTS_PER_SOL; // 0.001 SOL

  transaction.add(
    SystemProgram.transfer({
      fromPubkey: payer.publicKey,
      toPubkey: new PublicKey(tipWallet),
      lamports: tipAmount,
    })
  );

  // Get recent blockhash
  const connection = new Connection('https://api.mainnet-beta.solana.com');
  transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
  transaction.feePayer = payer.publicKey;

  // Sign transaction
  transaction.sign(payer);

  // Serialize to base64
  const txBase64 = transaction.serialize().toString('base64');

  // Submit to relay
  const response = await fetch('https://relay.allenhark.com/v1/sendTx', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': ALLENHARK_API_KEY,
    },
    body: JSON.stringify({
      tx: txBase64,
      simulate: false,
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Relay error: ${error.code} - ${error.message}`);
  }

  return await response.json();
}

// Usage
const tx = new Transaction();
// ... add your instructions ...

const result = await sendTransactionViaRelay(tx, myKeypair);
console.log(`Transaction relayed: ${result.request_id}`);

Rust Example

Rust
use reqwest::Client;
use serde_json::json;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    signature::{Keypair, Signer},
    system_instruction,
    transaction::Transaction,
    pubkey::Pubkey,
};
use std::str::FromStr;

const TIP_WALLETS: [&str; 11] = [
    "hark1zxc5Rz3K8Kquz79WPWFEgNCFeJnsMJ16f22uNP",
    // ... (include all)
];

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("ALLENHARK_API_KEY")?;
    let payer = Keypair::new(); // Your keypair

    // Create your transaction
    let rpc_client = RpcClient::new("https://api.mainnet-beta.solana.com");
    let recent_blockhash = rpc_client.get_latest_blockhash()?;

    // Add tip instruction
    let tip_wallet = Pubkey::from_str(
        TIP_WALLETS[rand::random::<usize>() % TIP_WALLETS.len()]
    )?;
    
    let tip_instruction = system::transfer(
        &payer.pubkey(),
        &tip_wallet,
        1_000_000, // 0.001 SOL in lamports
    );

    let mut transaction = Transaction::new_with_payer(
        &[tip_instruction /* , ...your other instructions */],
        Some(&payer.pubkey()),
    );
    
    transaction.sign(&[&payer], recent_blockhash);

    // Serialize to base64
    let tx_bytes = bincode::serialize(&transaction)?;
    let tx_base64 = base64::encode(&tx_bytes);

    // Send to relay
    let client = Client::new();
    let response = client
        .post("https://relay.allenhark.com/v1/sendTx")
        .header("x-api-key", api_key)
        .json(&json!({
            "tx": tx_base64,
            "simulate": false
        }))
        .send()
        .await?;

    if response.status().is_success() {
        let result: serde_json::Value = response.json().await?;
        println!("✓ Transaction relayed: {}", result["request_id"]);
    } else {
        let error: serde_json::Value = response.json().await?;
        eprintln!("✗ Error: {} - {}", error["code"], error["message"]);
    }

    Ok(())
}

Response Handling

Success response:

JSON
{
  "status": "accepted",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}

Error responses:

Error CodeDescriptionSolution
tip_missingNo tip to AllenHark walletAdd tip transfer instruction
tip_insufficientTip below the minimumIncrease tip amount
simulate_forbiddensimulate: true not allowedSet simulate: false
invalid_api_keyAPI key invalid or missingCheck API key value
rate_limit_exceededToo many requestsReduce request rate or contact us
invalid_transactionTransaction deserialization failedVerify transaction format
tip_hidden_in_altTip account only reachable via an address lookup tablePut the tip wallet in the static account keys

[!NOTE] Unknown keys are currently reported as rate_limit_exceeded; invalid_api_key is being rolled out.


Integration Guide: QUIC Protocol

For production systems where dispatch overhead matters.

QUIC Advantages Over HTTPS

  • 0-RTT Connection Resumption: No handshake overhead after first connection
  • Independent Stream Processing: No head-of-line blocking
  • Connection Multiplexing: many concurrent streams over one connection

Tradeoff: More complex implementation (requires QUIC library)

Rust Example (Quinn - Production Ready)

Rust
use quinn::{ClientConfig, Endpoint};
use rustls::RootCertStore;
use serde_json::json;
use std::sync::Arc;

struct RelayClient {
    endpoint: Endpoint,
    api_key: String,
}

impl RelayClient {
    pub async fn new(api_key: String) -> Result<Self, Box<dyn std::error::Error>> {
        // Configure TLS with system root certificates
        let mut roots = RootCertStore::empty();
        roots.add_trust_anchors(
            webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
                rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
                    ta.subject,
                    ta.spki,
                    ta.name_constraints,
                )
            }),
        );

        let client_config = ClientConfig::with_root_certificates(roots);
        
        let mut endpoint = Endpoint::client("0.0.0.0:0".parse()?)?;
        endpoint.set_default_client_config(client_config);

        Ok(Self { endpoint, api_key })
    }

    pub async fn send_transaction(
        &self,
        tx_base64: &str,
    ) -> Result<String, Box<dyn std::error::Error>> {
        // Connect to relay (uses 0-RTT on subsequent connections)
        let conn = self
            .endpoint
            .connect("relay.allenhark.com:4433".parse()?, "relay.allenhark.com")?
            .await?;

        // Open bidirectional stream
        let (mut send, mut recv) = conn.open_bi().await?;

        // Send API key header
        send.write_all(format!("api-key: {}\n", self.api_key).as_bytes())
            .await?;

        // Send transaction payload
        let payload = json!({
            "tx": tx_base64,
            "simulate": false
        });
        send.write_all(&serde_json::to_vec(&payload)?).await?;
        send.finish().await?;

        // Read response
        let response_bytes = recv.read_to_end(4096).await?;
        let response: serde_json::Value = serde_json::from_slice(&response_bytes)?;

        Ok(response["request_id"].as_str().unwrap().to_string())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = RelayClient::new(std::env::var("ALLENHARK_API_KEY")?).await?;
    
    // First request: pays the QUIC handshake
    let request_id = client.send_transaction("BASE64_TX_1").await?;
    println!("Transaction 1: {}", request_id);
    
    // Subsequent requests: 0-RTT resumption, no handshake
    let request_id = client.send_transaction("BASE64_TX_2").await?;
    println!("Transaction 2: {} (0-RTT)", request_id);
    
    Ok(())
}

Python Example (aioquic)

Python
import asyncio
import json
import os
from aioquic.asyncio import connect
from aioquic.quic.configuration import QuicConfiguration

class RelayClient:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.host = "relay.allenhark.com"
        self.port = 4433
    
    async def send_transaction(self, tx_base64: str) -> dict:
        """Send transaction via QUIC and return response."""
        config = QuicConfiguration(is_client=True)
        config.verify_mode = True
        
        async with connect(
            self.host,
            self.port,
            configuration=config,
        ) as client:
            # Open stream
            stream_id = client._quic.get_next_available_stream_id()
            
            # Send API key
            client._quic.send_stream_data(
                stream_id,
                f"api-key: {self.api_key}\n".encode()
            )
            
            # Send transaction
            payload = json.dumps({
                "tx": tx_base64,
                "simulate": False
            })
            client._quic.send_stream_data(
                stream_id,
                payload.encode(),
                end_stream=True
            )
            
            # Read response
            response_data = await client._quic.receive_stream_data(stream_id)
            return json.loads(response_data.decode())

async def main():
    client = RelayClient(os.environ["ALLENHARK_API_KEY"])
    
    # Send transaction
    response = await client.send_transaction("BASE64_TX")
    print(f"✓ Transaction relayed: {response['request_id']}")

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

Rate Limits and Best Practices

Default Rate Limits

  • Default: 10 requests/second per API key
  • Daily limit: Unlimited

Custom Rate Limits

For high-volume trading (>1,000 tx/day), contact us for:

  • Dedicated rate limits
  • Custom SLA agreements
  • Priority support

Best Practices

1. Connection Pooling

QUIC: Maintain persistent connection

Rust
// Good: Reuse connection
let client = RelayClient::new(api_key).await?;
for tx in transactions {
    client.send_transaction(&tx).await?;
}

// Bad: New connection per TX (loses 0-RTT benefit)
for tx in transactions {
    let client = RelayClient::new(api_key).await?;
    client.send_transaction(&tx).await?;
}

HTTPS: Use HTTP keep-alive

TypeScript
// Good: Reuse HTTP agent
const agent = new https.Agent({ keepAlive: true });
const api = axios.create({
  baseURL: 'https://relay.allenhark.com',
  httpsAgent: agent,
});

// Use for all requests
await api.post('/v1/sendTx', payload);

2. Error Handling with Retry Logic

TypeScript
async function sendWithRetry(
  transaction: string,
  maxRetries: number = 3
): Promise<any> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fetch('https://relay.allenhark.com/v1/sendTx', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': API_KEY,
        },
        body: JSON.stringify({ tx: transaction, simulate: false }),
      });
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      
      // Exponential backoff
      await new Promise(resolve => 
        setTimeout(resolve, 100 * Math.pow(2, attempt))
      );
    }
  }
}

3. Dynamic Tip Adjustment

Increase tips during network congestion:

TypeScript
const TIP_LEVELS = {
  LOW: 0.001,      // Normal conditions
  MEDIUM: 0.003,   // High congestion
  HIGH: 0.005,     // Extreme congestion
};

function getTipAmount(networkCongestion: 'low' | 'medium' | 'high'): number {
  return TIP_LEVELS[networkCongestion.toUpperCase()] * LAMPORTS_PER_SOL;
}

4. Monitor Request Latency

Rust
use std::time::Instant;

let start = Instant::now();
let response = client.send_transaction(&tx).await?;
let latency = start.elapsed();

// Log slow requests
if latency.as_millis() > 10 {
    warn!("High latency: {}ms", latency.as_millis());
}

Performance Optimization

Colocation

To take distance out of the path, run your trading infrastructure on a server in the same city as a relay region.

Benefits:

  • A short hop from your bot to the relay
  • Root access on your own server
  • One provider for the server and the relay

Learn more about colocation

Remote vs Colocated

The rule of thumb is simple, and it's about geography rather than protocol tricks: the time to the relay is set by your distance to the nearest region. If you're far from every region, no amount of protocol tuning recovers the transit time — either move closer (pick the nearest of Frankfurt, Amsterdam, New York, Tokyo) or colocate. When latency matters, use QUIC or WebSocket rather than HTTPS.


Monitoring and Health Checks

Ping Endpoint

Verify relay connectivity:

Terminal
curl https://relay.allenhark.com/ping
# Response: {"status": "ok", "ts": 1701234567890}

Monitoring Dashboard

Track your relay usage:

  • Request count
  • Error rates
  • Latency percentiles
  • Rate limit status

Access Dashboard (coming soon)


Pricing

You pay AllenHark Relay per transaction: a tip of at least 0.001 SOL to an AllenHark tip wallet, inside each transaction you send. There is no invoice for it.

For volume, Relay Dedicated is the optional monthly upgrade, paid at checkout in SOL, USDC or USDT. The current price is on the pricing page.

What Relay Dedicated adds:

  • Tip-free sends from the signer wallets you whitelist
  • A higher rate limit on your key
  • QUIC, WebSocket and HTTPS, and Jito bundles

For higher volumes, contact us.


Getting Help

Documentation

Support Channels

Emergency Support

For production issues affecting trading operations:

  • Priority support: Available for high-volume customers

Next Steps

  1. Create your API key - In the console's Relay tab
  2. Test on devnet - Validate integration without risk
  3. Deploy to production - Start with HTTP, migrate to QUIC
  4. Monitor performance - Track latency and success rates
  5. Optimize tips - Adjust based on network conditions

Ready to upgrade your Solana trading infrastructure?

Create an API key | View Documentation