Before using an RPC endpoint, check that it serves the network you intended. A successful HTTP response alone cannot tell you that. For Boba, Soneium and World Chain mainnet, the expected chain IDs are 288, 1868 and 480 respectively.

This example checks each network with one JSON-RPC batch containing eth_chainId and eth_blockNumber. The code uses the built-in fetch in Node.js 22 and was verified offline on Node.js 22.22.0; there are no dependencies, models, accounts or API keys to configure. The requests only read data. They do not sign or broadcast transactions.

The endpoints come from the public chain catalog. Its saved November 12 snapshot permits both methods and batches of up to ten items on these three public endpoints. Each batch below contains two items. Public method, range and rate-limit policies still apply; this is not a promise that every RPC method is available anonymously.

Run the check

Save the following as example.mjs, then run:

node example.mjs 

The script processes networks sequentially. It rejects redirects, requires HTTP 200 and validates both response IDs without depending on their order. Each result must be a nonnegative hexadecimal JSON-RPC quantity. A mismatched chain ID, RPC error, malformed response or request failure stops the script before the next network and sets a nonzero exit status. There are no retries or fallback providers.

const networks = [ ['Boba', 288n, 'https://api.blockvectra.com/v1/boba_mainnet/public'], ['Soneium', 1868n, 'https://api.blockvectra.com/v1/soneium_mainnet/public'], ['World Chain', 480n, 'https://api.blockvectra.com/v1/world_mainnet/public'], ]; const quantity = value => { if (typeof value !== 'string' || !/^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$/.test(value)) { throw new Error('Invalid hexadecimal quantity'); } return BigInt(value); }; try { for (const [name, expected, url] of networks) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 10_000); try { const response = await fetch(url, { method: 'POST', redirect: 'error', signal: controller.signal, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify([ { jsonrpc: '2.0', id: 'chain-id', method: 'eth_chainId', params: [] }, { jsonrpc: '2.0', id: 'block-number', method: 'eth_blockNumber', params: [] }, ]), }); if (response.status !== 200) throw new Error(`HTTP ${response.status}`); const batch = await response.json(); if (!Array.isArray(batch) || batch.length !== 2) throw new Error('Invalid batch'); const results = new Map(); for (const item of batch) { if (!item || typeof item !== 'object' || Array.isArray(item) || item.jsonrpc !== '2.0' || Object.hasOwn(item, 'error') || !['chain-id', 'block-number'].includes(item.id) || results.has(item.id)) { throw new Error('Invalid response ID, version, or RPC error'); } results.set(item.id, quantity(item.result)); } if (results.size !== 2 || results.get('chain-id') !== expected) { throw new Error(`${name}: chain ID mismatch`); } console.log(`${name}: chainId=${expected}, blockNumber=${results.get('block-number')}`); } finally { clearTimeout(timer); } } } catch (error) { console.error(error instanceof Error ? error.message : 'Request failed'); process.exitCode = 1; } 

Read the output narrowly

Captured responses at 2026-11-12 07:01 UTC:

Boba: chainId=288, blockNumber=41731014 Soneium: chainId=1868, blockNumber=30666059 World Chain: chainId=480, blockNumber=37565615 

Those block numbers are a dated output example, not values to assert in your application. Your run will usually return different heights. A valid height is not an independent freshness or canonical-chain check.

For an Agent or a larger application, treat a nonzero exit as a reason to stop and inspect the error before proceeding. Do not silently continue on another network, and do not turn this identity check into a transaction-sending step.

These two reads do not verify WebSocket subscriptions, indexed Data API availability or historical completeness. For the next read-only steps and authentication distinctions, see the developer quickstart.