orbitflare-apex-go is the Go client for Apex. It keeps one persistent QUIC connection per Apex endpoint, authenticates with a client certificate derived from your API key, sends one serialized transaction per stream, and reconnects with 0-RTT when the connection drops. It has the same features as the Rust client, and its client certificate is byte-identical to the Rust one.
Install
The module isgithub.com/orbitflare/orbitflare-apex-go; the source is on GitHub:
Transactions are built with
solana-go, which covers legacy, v0, and v1 messages. The QUIC path does not depend on the rpc package.
The module requires Go 1.26 or newer.
Quick Start
A complete program. It builds a memo transaction with a compute budget and a tip, sends it over QUIC, and waits for confirmation. It reads the same environment variables as the Quickstart:APEX_API_KEY, KEYPAIR_PATH, and SOLANA_RPC_URL.
main.go
SendTransaction returns as soon as the transaction is written to the stream, in microseconds, with no acknowledgement. The snippets below drop into this program: they reuse its ctx, client, apiKey, payer, tipAccount, blockhash, solanaRPC, and tx. Add the standard library imports a snippet uses, such as errors, encoding/base64, or sync.
Send With an Answer
The bidirectional stream reads Apex’s answer before returning: the signature when the transaction is accepted, or a*apex.RejectedError with the admission code and message. It costs one extra round trip, so use it while integrating, or when you need the reason inline.
SendWithResponse(ctx, wire) does the same with bytes you already hold and returns the raw apex.Admission (Accepted, Signature, Code, Message).
v1 Transactions
v1 transactions go up to 4096 bytes. The compute budget lives in the message’ssolana.TransactionConfig instead of ComputeBudget instructions, and every limit left unset is 0, so set the compute unit limit, the loaded accounts data size limit, and, for priority, the fee. See Transaction v1.
Raw Bytes
If your transaction is already serialized (from another signer, another process, or a file), send the bytes as they are. Nothing re-encodes them.apex.EncodePacket(wire, mevProtect, maxRetries) returns the exact packet the client writes to the stream: an 8-byte length, the transaction, and a 3-byte trailer. It is the reference for other languages.
HTTP
Therpc package covers every HTTP route on the Apex endpoint. It sends the key as the x-api-key header.
SendTransaction is JSON-RPC sendTransaction, a drop-in for existing code. SendTransactionBinary posts the raw bytes to /send-bin, the cheapest HTTP path. The last two arguments are mevProtect and maxRetries; nil leaves the endpoint’s default retry budget.
Batches
SendBatch sends up to 16 transactions in one request. Each one is admitted on its own, and the result says which were.
Bundles
SendBundle sends one to four transactions that land in order, all or nothing, with exactly one of them tipped. Poll BundleStatuses until the bundle lands or fails. See Bundles.
Concurrent Sends
A*apex.Client is safe for concurrent use, and every send gets its own stream on the one connection. Create the client once and send from as many goroutines as you like.
Regions
Each Apex endpoint is anapex.Region constant. apex.ParseRegion accepts a code or a city name, and apex.AllRegions lists them.
QUICEndpoint() returns <code>.apex.orbitflare.com:7001 and RPCURL() returns http://<code>.apex.orbitflare.com. See Endpoints and regions.
Transports
The transport does not change priority or routing. The tip does.
API
Every call that touches the network takes a
context.Context, so you can cancel a send or give it a deadline. Call Close when you are done; it stops the background reconnect and closes the socket.
Options
The zero value of every field gives you the defaults, so set only what you need:Keep-Alive and Reconnects
The connection stays open with a QUIC PING every second, against an idle timeout of 30 seconds on the Apex endpoint. On a warm connection a send is one stream open and one write. If the connection drops:- A background goroutine notices and re-handshakes right away, so the next send usually finds a live connection. If the endpoint itself closed the connection (an unknown key, too many connections), the goroutine doubles its wait after each attempt, up to 30 seconds, instead of retrying hard.
- A reconnect uses a cached session ticket and sends the waiting transaction in the handshake’s first flight (0-RTT). If the endpoint declines the early data, the client resends it after the handshake.
- A send that fails because the connection is gone reconnects and retries once. Set
DisableAutoReconnectto handle that yourself. - Each reconnect looks the host name up again, so a client on
apex.Globalfollows the load balancer to the next nearest endpoint when its own one goes down, without a restart. The lookup happens in that background reconnect, not on a send, and if it fails or takes more than two seconds the client keeps the address it had. An IP address endpoint is never looked up.
Errors
Errors wrap a sentinel, soerrors.Is tells you the stage that failed, and errors.As gives you the typed ones.
rpc package returns *rpc.Error with a code and message for a JSON-RPC or plain HTTP rejection, and rpc.ErrBadResponse for a response it cannot read. A plain HTTP rejection has code 0 and the message label: message, with the labels listed in Errors and rate limits.
Check Your Key
Print the certificate key your API key derives to. It must match the client pubkey shown next to the key on your dashboard; if it does not, the key was copied wrong.Examples in the Repository
All examples readAPEX_API_KEY, KEYPAIR_PATH (default payer.json), SOLANA_RPC_URL, and optionally APEX_REGION, APEX_QUIC, APEX_RPC, TIP_LAMPORTS, APEX_TX_VERSION (legacy or v1), APEX_MEMO_BYTES, and APEX_CU_LIMIT. Each sends a tipped memo and reports the slot it landed in.
What the Client Does Not Do
- Build or sign transactions, or choose your priority fee.
- Simulate or run preflight checks. Nothing between you and the leader does.