Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Network & Protocol Guides12 min read

Polkadot WebSocket RPC: WSS Endpoints, Subscriptions, and Reconnection

Learn how Polkadot's WebSocket RPC works, how to use @polkadot/api subscriptions, and how to handle disconnections and secure WSS endpoints.

TL;DR

This guide explains Polkadot's WebSocket JSON-RPC interface, covering wss vs ws, the default port 9944, how @polkadot/api's WsProvider manages subscriptions and reconnection, and how to secure WSS with nginx. It includes a runnable subscription script and a troubleshooting checklist for common issues like timeouts and 'fetch failed'.

Direct Answer: What You Need to Know About Polkadot WebSocket RPC

Polkadot nodes expose a WebSocket JSON-RPC endpoint (default port 9944) that allows clients to query chain data and, crucially, subscribe to real-time updates. The secure variant is wss:// (WebSocket Secure), which encrypts traffic using TLS. For production applications, you should always use wss:// endpoints, such as those provided by OnFinality's Polkadot network page. The @polkadot/api library abstracts the WebSocket connection via its WsProvider, handling subscriptions and reconnection logic. This guide explains the mechanics, provides a runnable example, and offers troubleshooting steps for common connection issues.

If you're building a dApp or indexer, you'll likely use @polkadot/api to subscribe to new blocks, finalized heads, or storage changes. Understanding how the WebSocket connection works under the hood helps you diagnose issues like timeouts or unexpected disconnections. Let's dive into the architecture.

How Polkadot WebSocket RPC Works: wss vs ws and Port 9944

Polkadot, built on Substrate, uses JSON-RPC over WebSocket for all real-time interactions. The default WebSocket port is 9944, while the HTTP RPC port is 9933. The ws:// scheme is unencrypted, while wss:// uses TLS encryption. For any production use, you must use wss:// to prevent eavesdropping and man-in-the-middle attacks. The Polkadot developer secure WebSocket guide explains how to set up a secure WebSocket proxy.

When you connect to a Polkadot node via WebSocket, the client sends JSON-RPC requests and subscriptions. Subscriptions are long-lived requests where the server pushes notifications. For example, chain_subscribeNewHeads sends a notification each time a new block is imported. The WebSocket protocol includes a ping/pong keepalive mechanism to detect dead connections. If a client does not receive a pong within a certain timeout, it can consider the connection dead and attempt to reconnect.

  • Default WebSocket port: 9944
  • Secure WebSocket: wss:// (TLS encrypted)
  • Subscriptions: chain_subscribeNewHeads, chain_subscribeFinalizedHeads, state_subscribeStorage
  • Keepalive: ping/pong frames to maintain connection

How @polkadot/api WsProvider Manages Connections and Subscriptions

The @polkadot/api library uses WsProvider to manage WebSocket connections. It handles the low-level WebSocket protocol, including reconnection logic and subscription management. When you create an API instance with a WSS endpoint, WsProvider establishes the connection and automatically reconnects if the connection drops. It uses an exponential backoff strategy, starting with a short delay and increasing it up to a maximum, to avoid overwhelming the server.

Subscriptions are managed via a subscription ID. When you call api.rpc.chain.subscribeNewHeads(), the provider sends a chain_subscribeNewHeads request. The server responds with a subscription ID, and the provider maps that ID to a callback. If the connection drops, the provider reconnects and automatically re-subscribes to all active subscriptions, using the same subscription IDs. This ensures your application continues to receive updates without manual intervention.

However, there are known issues. For example, a common problem is that WsProvider timeouts are not caught in try-catch blocks, as discussed on Substrate Stack Exchange. This can lead to unhandled promise rejections. Also, 'fetch failed' errors often occur when the WebSocket connection is not properly established, often due to network issues or incorrect endpoint URLs.

Runnable Example: Subscribing to New Heads with @polkadot/api

Below is a complete, runnable script that connects to a Polkadot WSS endpoint, subscribes to new block headers, and logs the block number and hash. It also demonstrates how to handle disconnections and reconnections. To run it, you need Node.js and the @polkadot/api package installed (npm install @polkadot/api).

The script uses a public WSS endpoint from OnFinality (replace with your own endpoint if needed). It sets up a subscription and logs every new head. It also listens for 'connected' and 'disconnected' events to show reconnection behavior.

// polkadot-subscribe.js
const { ApiPromise, WsProvider } = require('@polkadot/api');

const WS_URL = 'wss://polkadot.api.onfinality.io/public-ws';

async function main() {
  const provider = new WsProvider(WS_URL);
  const api = await ApiPromise.create({ provider });

  // Log connection events
  provider.on('connected', () => console.log('Connected to', WS_URL));
  provider.on('disconnected', () => console.log('Disconnected from', WS_URL));
  provider.on('error', (err) => console.error('Provider error:', err));

  // Subscribe to new heads
  const unsub = await api.rpc.chain.subscribeNewHeads((head) => {
    console.log(`New block #${head.number} hash: ${head.hash}`);
  });

  // Keep the process alive
  process.on('SIGINT', async () => {
    await unsub();
    await api.disconnect();
    process.exit(0);
  });
}

main().catch(console.error);

Expected Output and How to Verify

When you run the script, you should see output similar to the following (actual block numbers and hashes will vary):

Connected to wss://polkadot.api.onfinality.io/public-ws
New block #12345678 hash: 0x1234...abcd
New block #12345679 hash: 0x5678...ef01
...

To verify that the subscription is working, you can compare the block numbers with the latest block on a block explorer like Polkadot Subscan. The block number should increase by 1 every 6 seconds (the average block time on Polkadot). If you don't see new blocks, check your network connection and the endpoint URL. Also, ensure that your firewall allows WebSocket connections on port 443 (for wss://).

Common Failures and Fixes: WsProvider Timeout and 'fetch failed'

Two common issues developers face are WsProvider timeouts and 'fetch failed' errors. A timeout occurs when the WebSocket connection is established but the server does not respond within a certain time. This can happen if the node is overloaded or the network is slow. The WsProvider has a built-in timeout (default 60 seconds) for connection establishment. If the timeout is exceeded, it throws an error that may not be caught by a try-catch around the ApiPromise.create() call, as noted in Substrate Stack Exchange. To handle this, you can listen to the provider's 'error' event.

'fetch failed' errors typically occur when the WebSocket handshake fails, often due to an incorrect URL, a firewall blocking the connection, or a DNS issue. This error is thrown by the underlying fetch API used by the WebSocket implementation. To fix it, verify the endpoint URL, check that the server is reachable (e.g., using curl or a WebSocket client), and ensure your network allows outbound WebSocket connections.

For a deeper dive into why WebSocket RPC connections drop and how to fix them, see our guide on why WebSocket RPC connections drop.

  • Check endpoint URL and network connectivity
  • Listen to provider 'error' events to catch timeouts
  • Use a reliable WSS provider like OnFinality's RPC Assistant
  • Implement custom reconnection logic if needed

Securing WSS Behind nginx with TLS

If you run your own Polkadot node, you should expose it via a secure WebSocket endpoint. The Polkadot developer secure WebSocket guide recommends using nginx as a reverse proxy with TLS termination. This allows you to keep the node's native WebSocket port (9944) bound to localhost and expose a wss:// endpoint on port 443.

Here is a minimal nginx configuration that proxies WebSocket connections to a local Polkadot node. You need to have a TLS certificate (e.g., from Let's Encrypt) and configure the proxy_pass to http://127.0.0.1:9944 with the appropriate WebSocket headers.

server {
    listen 443 ssl;
    server_name rpc.example.com;

    ssl_certificate /etc/letsencrypt/live/rpc.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/rpc.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:9944;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;
    }
}

Tradeoffs and Limitations of WebSocket RPC

While WebSocket RPC is powerful, it has limitations. Subscriptions can consume significant resources on the node, especially if many clients subscribe to storage changes. Public endpoints often impose rate limits to protect the node. For high-throughput applications, consider using a dedicated endpoint or a service like OnFinality's API service that offers scalable infrastructure.

Another limitation is that WebSocket connections are stateful, which can be problematic in load-balanced environments. If a client connects to one server and then the load balancer routes subsequent requests to another, the subscription may break. Solutions include sticky sessions or using a centralized RPC provider that handles this transparently.

Finally, reconnection logic can lead to duplicate notifications if the client reconnects and re-subscribes. The @polkadot/api library handles this by using subscription IDs, but you should be aware of potential duplicate events in your application logic.

Next Steps and Further Resources

Now that you understand Polkadot WebSocket RPC, you can build real-time applications with confidence. To get started, explore the OnFinality learn hub for more guides, or check out our Polkadot network page for available endpoints. If you need a reliable RPC service, consider our pricing and API service.

For a comprehensive list of Polkadot RPC endpoints, use our RPC Assistant. And if you encounter connection issues, refer to our guide on why WebSocket RPC connections drop. Happy coding!

Never Worry about Infrastructure Again

OnFinality takes away the heavy lifting of DevOps so you can build smarter and faster.

Get Started