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.
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:
- Slow: extra hops through the gossip network before a leader sees it
- Public: Transactions visible in mempool (vulnerable to front-running)
- Unreliable: Packets dropped during congestion
- 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):
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:
{
"tx": "BASE64_ENCODED_TRANSACTION",
"simulate": false
}Authentication Methods
Option 1: Header Authentication (Recommended)
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
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
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
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:
{
"status": "accepted",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}Error responses:
| Error Code | Description | Solution |
|---|---|---|
tip_missing | No tip to AllenHark wallet | Add tip transfer instruction |
tip_insufficient | Tip below the minimum | Increase tip amount |
simulate_forbidden | simulate: true not allowed | Set simulate: false |
invalid_api_key | API key invalid or missing | Check API key value |
rate_limit_exceeded | Too many requests | Reduce request rate or contact us |
invalid_transaction | Transaction deserialization failed | Verify transaction format |
tip_hidden_in_alt | Tip account only reachable via an address lookup table | Put the tip wallet in the static account keys |
[!NOTE] Unknown keys are currently reported as
rate_limit_exceeded;invalid_api_keyis 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)
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)
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
// 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
// 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
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:
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
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
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:
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
- Discord: Join our community
- Email: support@allenhark.com
Emergency Support
For production issues affecting trading operations:
- Priority support: Available for high-volume customers
Next Steps
- Create your API key - In the console's Relay tab
- Test on devnet - Validate integration without risk
- Deploy to production - Start with HTTP, migrate to QUIC
- Monitor performance - Track latency and success rates
- Optimize tips - Adjust based on network conditions
Ready to upgrade your Solana trading infrastructure?
