Accepted Does Not Mean Landed
A successful reply means the Apex endpoint holds your transaction and is racing it to the leaders until it lands or its blockhash expires. It does not mean the transaction landed. Confirm every send withgetSignatureStatuses on a Solana RPC. See Best practices.
Apex checks a transaction in this order: API key, rate limit, transaction validity and size, then the tip. The first failed check decides the error. Nothing is sent anywhere until every check passes, so a rejected transaction costs nothing.
JSON-RPC Errors
Plain HTTP Errors
On/send and /send-bin, success is HTTP 200 with {"signature": "..."}. /send-batch replies 200 with {"attempted": n, "accepted": n, "rejected": n, "results": [...]}, where each result, in frame order, is either {"signature": "..."} or {"error": "<label>", "message": "..."}. /send-bundle replies 200 with {"bundle_id": "...", "signatures": ["...", "..."]}.
Errors are JSON with a machine-readable label and a human-readable message:
Branch on the HTTP status first. It is the stable part of the contract. Treat a label you do not recognize under
400 as “fix the transaction”./send-batch replies 200 whenever the request itself is well formed, and reports each frame on its own inside results. Check rejected and each entry rather than the status code alone. See POST /send-batch.
The bundle label (HTTP 400) means a bundle was refused as a whole: more than 4 members, a duplicate member, bundles not available for this key or endpoint, or the block engine refused it. A problem with one member keeps its own label. A member over 1232 bytes comes back as malformed with a message naming the 1232 byte limit, two tipped members as multiple_tips, and no tipped member as no_tip.
Tip Errors in Detail
QUIC Admission Codes
A bidirectional QUIC stream answers with one admission code. A unidirectional stream returns nothing, so use a bidirectional stream while you integrate.
If the QUIC handshake itself is refused, the connection closes with application error
1 (no client certificate), 2 (unknown key), or 3 (too many connections). See Authentication.
In the Rust client, send_transaction_with_response turns a rejection into Error::Rejected { code, message }, and a transaction over 4096 bytes fails locally with Error::TooLarge before anything is sent.
Which Errors to Retry
You do not need to resend an accepted transaction. Apex already retries it until it lands or the blockhash expires. If it has not landed by then, rebuild it with a fresh blockhash and send the new transaction.
Rate Limits
Rate limits are per API key and set by the key’s tier. The limit counts transactions per second across every transport: JSON-RPC, the HTTP routes, and QUIC all draw from the same allowance, and each frame of a batch counts as one transaction. When you exceed the limit you get-32029, HTTP 429, or admission code 2. The rejected transaction is not queued, so resend it after a moment if it still matters.
Your tier’s limit and tip floor are shown next to the key in the OrbitFlare dashboard under Dashboard > Apex. To raise them, contact the team on Discord.