Skip to Content
Platform GuideSticky Sessions

Sticky Sessions

Each region of an RPC endpoint is served by several nodes, and by default every request goes to whichever node is least busy. Nodes import each new block independently, so two consecutive requests can be answered by nodes at slightly different heights.

For most calls this makes no difference. It matters when one call depends on the answer to the previous one. For example:

  1. eth_blockNumber returns block N.
  2. eth_getBlockByNumber(N) goes to a node that hasn’t imported N yet and returns null.

Sticky sessions keep a series of requests on the same node, so each request sees the chain as the previous one left it.

How to Use It

Add the X-Spectrum-Session header to your HTTP requests:

X-Spectrum-Session: <session value>
  • Use a random value, such as a UUID generated when each worker starts, and send it with all of that worker’s requests. A common name such as worker-1 would share a node with anyone else who picked it. The value can be up to 64 characters. Don’t put secrets or personal data in it.
  • All requests with the same value go to the same node while that node is healthy.
  • Requests without the header are load-balanced as usual.
  • There is nothing to enable in the dashboard, and the header costs no extra credits.
curl https://YOUR_ENDPOINT_URL \ -H "Content-Type: application/json" \ -H "X-Spectrum-Session: 3f2c8e4a-9b1d-4c6e-a7f0-5d2b9e8c1a64" \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'

Availability

Sticky sessions work for HTTP requests on every chain, in every region, except Boost endpoints on Optimism and Hyperliquid for now. WebSocket connections to pruned endpoints don’t need the header: each connection already stays on one node.

How It Behaves

One node per value, in each region. Each region picks the node for a value on its own, so send all requests of a session to the same regional URL.

Failover. If that node becomes unavailable, the session moves to another healthy node and returns when the node is back. Other sessions don’t move. Since a session can move this way, still retry a null result after about a second for calls that depend on the latest block.

Use one value per worker or process, not one for your whole application. All requests with the same value go to a single node, so heavy parallel traffic under one value is limited by what that node can handle.

Client Examples

ethers.js (v6)

import { FetchRequest, JsonRpcProvider } from 'ethers'; const request = new FetchRequest('https://YOUR_ENDPOINT_URL'); request.setHeader('X-Spectrum-Session', crypto.randomUUID()); const provider = new JsonRpcProvider(request);

viem

import { createPublicClient, http } from 'viem'; import { mainnet } from 'viem/chains'; const client = createPublicClient({ chain: mainnet, transport: http('https://YOUR_ENDPOINT_URL', { fetchOptions: { headers: { 'X-Spectrum-Session': crypto.randomUUID() } }, }), });

web3.py

import uuid from web3 import Web3 w3 = Web3(Web3.HTTPProvider( 'https://YOUR_ENDPOINT_URL', request_kwargs={'headers': {'X-Spectrum-Session': uuid.uuid4().hex}}, ))