> ## Documentation Index
> Fetch the complete documentation index at: https://www.solanagun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket

> Send Solana transactions over a persistent WebSocket connection to Solana Gun.

export const EndpointsTable = ({protocol} = {}) => {
  const [copied, setCopied] = useState(null);
  const regions = [{
    name: "Frankfurt, Germany",
    code: "FRA1",
    host: "fra1.solanagun.com"
  }, {
    name: "Frankfurt, Germany",
    code: "FRA2",
    host: "fra2.solanagun.com"
  }, {
    name: "Frankfurt, Germany",
    code: "FRA3",
    host: "fra3.solanagun.com"
  }, {
    name: "Amsterdam, Netherlands",
    code: "AMS1",
    host: "ams1.solanagun.com"
  }, {
    name: "London, UK",
    code: "LON1",
    host: "lon1.solanagun.com"
  }, {
    name: "New York, USA",
    code: "NYC1",
    host: "nyc1.solanagun.com"
  }, {
    name: "Ashburn, USA",
    code: "ASH1",
    host: "ash1.solanagun.com"
  }, {
    name: "Tokyo, Japan",
    code: "TYO1",
    host: "tyo1.solanagun.com"
  }];
  const allProtocols = [{
    name: "RPC",
    url: host => `https://${host}:8875`
  }, {
    name: "WebSocket",
    url: host => `wss://${host}:8875`
  }, {
    name: "QUIC",
    url: host => `${host}:7000`
  }];
  const protocols = protocol ? allProtocols.filter(p => p.name === protocol) : allProtocols;
  const showProtocol = protocols.length > 1;
  const copy = value => {
    navigator.clipboard.writeText(value).then(() => {
      setCopied(value);
      setTimeout(() => setCopied(current => current === value ? null : current), 1500);
    });
  };
  const cell = "px-4 py-2 border-b border-white/10 align-middle";
  return <div className="not-prose my-6 overflow-x-auto rounded-xl border border-white/10" style={{
    backgroundColor: "#131313"
  }}>
      <table className="w-full text-sm text-left" style={{
    borderCollapse: "collapse"
  }}>
        <thead>
          <tr className="text-white/70">
            <th className={`${cell} font-semibold`}>Location</th>
            {showProtocol && <th className={`${cell} font-semibold`}>Protocol</th>}
            <th className={`${cell} font-semibold`}>Endpoint</th>
          </tr>
        </thead>
        <tbody className="text-white/90">
          {regions.map(region => protocols.map((proto, i) => {
    const value = proto.url(region.host);
    const isCopied = copied === value;
    return <tr key={value}>
                  {i === 0 && <td rowSpan={protocols.length} className={cell} style={{
      whiteSpace: "nowrap"
    }}>
                      <div className="font-medium">{region.name}</div>
                      <div className="text-xs text-white/50">{region.code}</div>
                    </td>}
                  {showProtocol && <td className={cell} style={{
      whiteSpace: "nowrap"
    }}>{proto.name}</td>}
                  <td className={cell}>
                    <div style={{
      display: "flex",
      alignItems: "center",
      gap: "12px"
    }}>
                      <span className="font-mono text-sm text-white" style={{
      whiteSpace: "nowrap"
    }}>{value}</span>
                      <span style={{
      position: "relative",
      display: "inline-flex",
      flexShrink: 0
    }}>
                        <button type="button" onClick={() => copy(value)} title="Copy" aria-label={`Copy ${value}`} className="pn-copy-button">
                          {isCopied ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                              <path d="M20 6 9 17l-5-5" />
                            </svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                              <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
                              <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
                            </svg>}
                        </button>
                        {isCopied && <span role="status" className="rounded-md bg-white px-2 py-1 text-xs font-medium text-black shadow" style={{
      position: "absolute",
      bottom: "calc(100% + 6px)",
      left: "50%",
      transform: "translateX(-50%)",
      whiteSpace: "nowrap",
      pointerEvents: "none"
    }}>
                            Copied!
                          </span>}
                      </span>
                    </div>
                  </td>
                </tr>;
  }))}
        </tbody>
      </table>
    </div>;
};

## Before you begin

* Use your Solana Gun endpoint ([see below](#endpoints)). Replace the placeholders with your connection details.
* Have a funded Solana wallet and its local keypair JSON file. The example sends 0.0021 SOL (2,100,000 lamports) to an existing wallet, plus the network fee.
* Use a regular Solana RPC to fetch a recent blockhash. The example uses [a public Solana RPC endpoint](https://solana-rpc.publicnode.com).

## Endpoints

Choose a host close to your application and copy the endpoint.

<EndpointsTable protocol="WebSocket" />

## Send a transaction

Choose your language below. Each example signs locally and submits directly over WebSocket.

<Info>
  `skipPreflight` must be enabled because Solana Gun does not simulate individual transactions. The examples set it for you.
</Info>

<Tabs>
  <Tab title="Python">
    <Steps>
      <Step title="Install dependencies">
        Install the Python SDK and the [WebSocket library](https://websockets.readthedocs.io/en/15.0.1/reference/asyncio/client.html):

        ```bash theme={"system"}
        python3 -m pip install solana==0.36.9 websockets==15.0.1
        ```
      </Step>

      <Step title="Write the code">
        Save the code as `send_ws.py` and replace the endpoint, keypair path, and recipient address:

        ```python send_ws.py theme={"system"}
        import asyncio
        import base64
        import json
        from pathlib import Path

        from solana.rpc.async_api import AsyncClient
        from solders.keypair import Keypair
        from solders.message import MessageV0
        from solders.pubkey import Pubkey
        from solders.system_program import TransferParams, transfer
        from solders.transaction import VersionedTransaction
        from websockets.asyncio.client import connect

        KEYPAIR_PATH = "/path/to/keypair.json"
        RPC_URL = "https://solana-rpc.publicnode.com"
        GUN_URL = "wss://fra1.solanagun.com:8875/<TOKEN>"
        RECIPIENT_ADDRESS = "<RECIPIENT_ADDRESS>"
        AMOUNT_LAMPORTS = 1_000_000

        async def main():
            payer = Keypair.from_json(Path(KEYPAIR_PATH).read_text())
            recipient = Pubkey.from_string(RECIPIENT_ADDRESS)

            async with AsyncClient(RPC_URL, timeout=30) as rpc:
                blockhash = (await rpc.get_latest_blockhash(commitment="confirmed")).value.blockhash

            instruction = transfer(TransferParams(
                from_pubkey=payer.pubkey(),
                to_pubkey=recipient,
                lamports=AMOUNT_LAMPORTS,
            ))
            message = MessageV0.try_compile(payer.pubkey(), [instruction], [], blockhash)
            transaction = VersionedTransaction(message, [payer])

            async with connect(GUN_URL, open_timeout=30) as gun:
                await gun.send(json.dumps({
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "sendTransaction",
                    "params": [
                        base64.b64encode(bytes(transaction)).decode(),
                        {"encoding": "base64", "skipPreflight": True},
                    ],
                }))

                response = json.loads(await asyncio.wait_for(gun.recv(), timeout=30))
                if "error" in response:
                    raise RuntimeError(response["error"])

                print("Submitted. Transaction signature:", response["result"])

        asyncio.run(main())
        ```
      </Step>

      <Step title="Run">
        ```bash theme={"system"}
        python3 send_ws.py
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Submission response

A successful call returns the transaction signature and means Solana Gun accepted it for forwarding.

<Note>
  It does not guarantee confirmation on Solana.
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.