ZeroCopyZeroCopy
API reference

API reference

The SWQoS JSON-RPC envelope, authentication, method index, error format and status codes.

SWQoS provides JSON-RPC, raw HTTP and QUIC for sending transactions and bundles. Use your Solana RPC provider for blockhashes, balances and confirmation.

Endpoint

Send JSON-RPC requests with POST to any path of a SWQoS host other than /binary and /plaintext. https://sol.zerocopy.sh/, https://sol.zerocopy.sh/rpc and https://fra.sol.zerocopy.sh/ are equivalent. Endpoints lists the hosts and ports.

RequestServed as
POST /binaryRaw transaction or bundle bytes
POST /plaintextRaw payload as base64 text
GET /healthHealth check, no key
GET /tip_floorTip floor
POST to any other pathJSON-RPC
Any other method or path404 with reason invalid_request, problem not_found

Every JSON-RPC method needs an API key: the api-key query parameter, an x-api-key header or Authorization: Bearer. See Authentication.

Request

A request body is one JSON object of at most 65,536 bytes, received within 2 seconds.

MemberRule
jsonrpcExactly "2.0".
methodOne of the methods below.
paramsAn array. Required by sendTransaction and sendBundle; the other methods ignore it.
idOptional. Echoed in the response exactly as sent; null when absent.

Batches (a JSON array of requests) are refused with problem batch; send one request per call. HTTP/2 connections can carry many requests at once.

Request
{"jsonrpc":"2.0","id":1,"method":"getTipAccounts"}

Methods

MethodParamsResult
sendTransaction[transaction, options?]Base58 signature string
sendBundle[[transaction, ...], options?], 1 to 4 transactions64-character hex bundle id
getTipAccountsnoneArray of base58 tip account addresses
getBundleTipAccountsnoneThe same array
getTipFloornoneArray of zero or one tip floor objects
getHealthnone"ok"
getSlotnoneInteger slot

Any other method name is refused with problem unknown_method and code -32601. There is no getLatestBlockhash, getSignatureStatuses, simulateTransaction, bundle status method or WebSocket subscription.

Responses

A result comes back with HTTP 200:

Result shape
{"jsonrpc":"2.0","result":RESULT,"id":ID}

A refusal carries the rejection in error.data:

Rejection (example from sendTransaction)
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32602,
    "message": "tip missing: add a System transfer of at least 1,000,000 lamports to one of the tip accounts (getTipAccounts or getBundleTipAccounts), or declare a tip",
    "data": {
      "reason": "missing_tip",
      "minimum": 1000000,
      "found": 0,
      "message": "tip missing: add a System transfer of at least 1,000,000 lamports to one of the tip accounts (getTipAccounts or getBundleTipAccounts), or declare a tip",
      "retryable": false,
      "docs": "https://docs.zerocopy.sh/errors#missing_tip"
    }
  },
  "id": 1
}
FieldMeaning
data.reasonStable snake_case reason. Match on this.
Other data fieldsThe numbers behind the refusal, specific to each reason. See Errors.
data.message, error.messageThe same sentence: what happened and what to change. Wording can change.
data.retryableWhether sending the same request again, unchanged, can succeed.
data.docshttps://docs.zerocopy.sh/errors# followed by the reason.

A failed preflight simulation also puts Solana's simulation result fields, such as err, logs and unitsConsumed, at the top level of data. An unhealthy getHealth is the one error without data: {"code":-32005,"message":"Node is unhealthy"}.

id is null when the request was refused before its id could be read: a missing or unknown key, a body that is too large, unreadable or late, invalid JSON, a batch, or a body that is not a JSON object.

Status codes

JSON-RPC answers most refusals with HTTP 200, as Solana RPC does. It uses another status where a client acts on it.

invalid_request problemJSON-RPC code
parse_error-32700
not_found, body_timeout, body_unreadable, batch, not_json_rpc-32600
unknown_method-32601
missing_params, invalid_params, transaction_not_string, invalid_options, invalid_commitment-32602

Codes -32001 and -32003 have other meanings in Solana's RPC (a cleaned-up block and a failed signature verification). Identify a SWQoS refusal by error.data.reason, not by the numeric code.

Solana client libraries

sendTransaction has Solana's request shape, so Solana clients can call it. They do not surface SWQoS rejections in full. With @solana/kit 8.5:

  • A response with HTTP status 401, 402, 429 or 503 throws SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR carrying only statusCode and headers. Kit does not read the body, so reason, retry_after_ms and unpaid_lamports are lost.
  • A simulation_failed refusal throws Kit's preflight failure error, which keeps the simulation fields.
  • A refusal with code -32602, -32600, -32601 or -32700 throws an error that keeps only the message text, not data.reason or its fields.

Use Kit, or another Solana client, for building, signing and the happy path. Use a plain HTTP request, like the helper below, when your code needs to act on the reason.

Client helper

The JSON-RPC examples in this reference use this helper. It needs only fetch, and works in Bun and Node.js 18 or later. It reads the key from SWQOS_API_KEY and the endpoint from SWQOS_URL.

swqos.ts
export type Rejection = {
  reason: string
  message: string
  retryable: boolean
  docs: string
  [field: string]: unknown
}

type Reply<T> =
  | { result: T }
  | { error: { code: number; message: string; data?: Rejection } }

export class SwqosError extends Error {
  readonly status: number
  readonly code: number
  readonly rejection: Rejection | undefined

  constructor(status: number, code: number, message: string, rejection?: Rejection) {
    super(rejection?.message ?? message)
    this.status = status
    this.code = code
    this.rejection = rejection
  }
}

export function required(name: string): string {
  const value = process.env[name]
  if (!value) throw new Error(`set ${name}`)
  return value
}

export const url = process.env.SWQOS_URL ?? 'https://sol.zerocopy.sh'
const apiKey = required('SWQOS_API_KEY')

export async function call<T>(method: string, params?: unknown[]): Promise<T> {
  const response = await fetch(url, {
    method: 'POST',
    headers: { 'content-type': 'application/json', 'x-api-key': apiKey },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
  })
  const reply = (await response.json()) as Reply<T>
  if ('error' in reply) {
    const { code, message, data } = reply.error
    throw new SwqosError(response.status, code, message, data)
  }
  return reply.result
}

Other interfaces

  • Raw HTTP: /binary, /plaintext, /health, /tip_floor and the payload envelope.
  • QUIC: ALPN zerocopy-swqos/1, client-certificate keys, streams and reply frames.
  • Local agent: the same methods on your own host, with listed differences.

On this page