Beam RPC: A Developer's Guide (Chain ID 4337, Gaming L1)

By John Sullivan · October 29, 2026 · 11 min read · #beam #beam rpc #gaming #avalanche l1 #developer guide

Beam RPC (chain ID 4337) is the JSON-RPC surface of an Avalanche L1 built for games. The EVM is familiar; the chain's block production is not. In a 1,000-block sample on October 29, 2026, the median gap between blocks was 9 seconds, the average was 21.05 seconds, and the longest gap was 573 seconds. Every sampled block carried a 1,000 gwei base fee, the gas limit was 25,000,000, and eth_getLogs stopped at 5,000 blocks.

This guide covers the endpoint, active fees, block timing, finality, receipts, logs, archive reads, tracing, WebSocket subscriptions, and the places where a generic EVM template gives the wrong answer.

The Beam RPC endpoint

https://rpc.swiftnodes.io/rpc/beam?key=YOUR_API_KEY
wss://rpc.swiftnodes.io/ws/beam?key=YOUR_API_KEY

The proxy slug is beam. Chain ID is 4337 (0x10f1), the native gas token is BEAM with 18 decimals, and the public explorer is subnets.avax.network/beam. The official HTTP endpoint is https://build.onbeam.com/rpc; the official WebSocket endpoint is wss://build.onbeam.com/ws. Both are listed in Beam's RPC settings.

import { createPublicClient, defineChain, http } from "viem";

const beam = defineChain({
  id: 4337,
  name: "Beam",
  nativeCurrency: { name: "Beam", symbol: "BEAM", decimals: 18 },
  rpcUrls: {
    default: { http: ["https://rpc.swiftnodes.io/rpc/beam?key=YOUR_API_KEY"] },
  },
});

const client = createPublicClient({ chain: beam, transport: http() });
const chainId = await client.getChainId(); // 4337
curl -sS https://rpc.swiftnodes.io/rpc/beam?key=YOUR_API_KEY \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'
# {"jsonrpc":"2.0","id":1,"result":"0x10f1"}

Beam is an Avalanche L1, not an Ethereum rollup. The network overview lists the mainnet subnet and blockchain IDs, and the gas token page confirms BEAM is the native currency. That means there is no Ethereum L1 data-fee field to copy from an OP Stack integration. A game's transaction budget is paid on Beam in BEAM.

The bridge is powered by LayerZero at bridge.onbeam.com. If your game uses sponsored transactions or account abstraction through the Beam SDK, that logic sits above JSON-RPC; an ordinary RPC client still needs a funded account for a standard transaction.

What we measured

The measurements were made on October 29, 2026 against the official endpoint, with spot checks through the SwiftNodes beam route. The block sample covered 1,000 consecutive blocks from 9,779,381 to 9,780,380, starting at 2026-10-29T00:41:41Z and ending at 2026-10-29T06:32:14Z.

Property Measured value
Chain ID 4337 (0x10f1)
Native gas token BEAM, 18 decimals
Block interval 21.05 s average; 9 s median; 42 s p90; 247 s p99; 573 s maximum
Blocks in sample 1,000 over 21,033 seconds (5.84 hours)
Transactions 1,155 total; 1.155 per block; 7 maximum; no empty blocks
Block gas limit 25,000,000
Gas used 103,162 average; 38,025 median; 673,427 maximum; 0.41% of limit
Base fee 1,000 gwei in every sampled block
eth_gasPrice 1,000.000000001 gwei
eth_maxPriorityFeePerGas 1 wei
Plain transfer 21,000 gas; 0.021 BEAM at the base fee

The interval distribution is the important part. Only 349 of the 999 intervals were 2 seconds or shorter. Another 88 were 3–5 seconds, 151 were 6–10 seconds, 261 were 11–30 seconds, and 150 were longer than 30 seconds. A 2-second target block rate is a target, not a promise that a new block exists every 2 seconds.

That pattern matters for indexers and game servers. A polling loop that uses wall-clock time instead of block numbers will repeatedly ask for a block that does not exist yet, or skip work when a quiet period produces a long gap. Key jobs to block numbers, record both the block number and timestamp, and treat the timestamp as chain data rather than a local clock.

Fees: read the active config

Beam's base fee was 1,000 gwei (0xe8d4a51000 wei) in every block in the sample. eth_gasPrice returned 1,000.000000001 gwei, while eth_maxPriorityFeePerGas returned 1 wei. The eth_feeHistory rewards were mostly zero; one block in a later five-block sample showed a 200 gwei priority reward. A simple transfer at 21,000 gas therefore cost 0.021 BEAM; contract calls add execution gas on top.

The confusing part is which fee configuration is authoritative. The live eth_feeConfig call (Beam API reference) returned:

{
  "feeConfig": {
    "gasLimit": 25000000,
    "targetBlockRate": 2,
    "minBaseFee": 1000000000000,
    "targetGas": 50000000,
    "baseFeeChangeDenominator": 36,
    "maxBlockGasCost": 100000000,
    "blockGasCostStep": 10000000
  },
  "lastChangedAt": 3397066
}

The eth_getChainConfig call returned a different, older configuration: an 8,000,000 gas limit, a 10 gwei minimum base fee and a denominator of 72. The gas documentation also shows a genesis fee config rather than the active one. The block header and eth_feeConfig agreed on 25,000,000 gas and 1,000 gwei; the genesis values did not.

Use this at startup, not a hard-coded constant:

const feeConfig = await client.request({ method: "eth_feeConfig", params: ["latest"] });
const block = await client.getBlock({ blockTag: "latest" });
const baseFee = block.baseFeePerGas; // 1,000 gwei in the sample

The practical fee rule is simple: build an EIP-1559 transaction with a priority fee of at least 1 wei, and set maxFeePerGas above the base fee reported by the block. The minimum priority is not the same as a recommended tip; a busy game transaction may need a larger tip to be included promptly. Keep the base fee in the fee display so a future fee-config change does not turn into a stale estimate.

Finality and confirmation

In the snapshot, eth_getBlockByNumber returned block 9,780,380 for latest, safe and finalized. There was no tag lag to measure. That is better than assuming a fixed number of confirmations, but it does not make inclusion instant: a transaction can only be in a block after a block is produced, and the sample contains gaps longer than nine minutes.

For a game, separate two questions:

  • Inclusion: did the transaction get into a block and return a receipt?
  • Finality: is the block at or below the chain's finalized tag?

Query the tags instead of inventing a confirmation count:

const finalized = await client.getBlock({ blockTag: "finalized" });
const receipt = await client.getTransactionReceipt({ hash });
if (receipt.blockNumber <= finalized.number) {
  // finality condition met
}

If your game needs a faster user-facing response, show the receipt as pending confirmation and reconcile against finalized before releasing an irreversible asset. Do not equate a 2-second target with a 2-second user-visible confirmation.

Logs: 5,000 blocks upstream, 100 on the free tier

eth_getLogs worked for 100 blocks and 1,000 blocks on the official endpoint. A 5,000-block request returned 5,956 logs. The next block of range failed with an exact error:

requested too many blocks from 9775380 to 9780380, maximum is set to 5000

So the official cap is 5,000 blocks, not a result-count cap. A 5,000-block query can still return a large response; add an address or topic filter when you do not need every event.

The SwiftNodes free tier is narrower: its current cap is 100 blocks per eth_getLogs request. That is a plan limit in front of the upstream, not a Beam protocol rule. A paid key can use the upstream's 5,000-block range. Chunk around the smaller of the two limits:

const MAX_RANGE = 100n; // SwiftNodes free tier
for (let from = start; from <= end; from += MAX_RANGE) {
  const to = from + MAX_RANGE - 1n > end ? end : from + MAX_RANGE - 1n;
  const logs = await client.request({
    method: "eth_getLogs",
    params: [{ fromBlock: `0x${from.toString(16)}`, toBlock: `0x${to.toString(16)}` }],
  });
  // store logs with blockNumber and logIndex before advancing the cursor
}

eth_getBlockReceipts is supported. The latest block returned one receipt with status: "0x1", gasUsed: "0x5208", and an empty log array. That is useful for a block-level indexer, but it does not remove the need to reconcile logs: a receipt's logs are the transaction-level record, while eth_getLogs is the filtered query surface. Store both the transaction hash and log index so a retry is idempotent.

Archive state and tracing

The official endpoint served historical state for an active address at 5,000,000 blocks back. For example, the address that sent the latest sampled transfer returned a non-zero balance at block 9,780,380 - 5,000,000, not a "missing trie node" error. That is evidence of archive state on that endpoint, not a guarantee that every provider keeps the same depth. Test the exact block you need before an indexer backfill.

Tracing is mixed:

  • debug_traceTransaction worked on a simple transfer and returned gas: 21000, failed: false and an empty structLogs array.
  • trace_block returned the method trace_block does not exist/is not available.
  • On SwiftNodes, debug_traceTransaction on the free tier returned: Trace and debug methods (debug_traceTransaction) need a paid plan, from Starter at $49/month.

If your debugging workflow depends on parity traces, test the specific method on the provider you plan to use. A generic "traces supported" claim is too broad for Beam.

WebSocket subscriptions

Both the SwiftNodes route and the official endpoint accepted eth_subscribe with newHeads. The subscription returned an ID immediately. In an eight-second test, no head arrived; that is consistent with the measured block gaps and does not mean the subscription failed. Subscribe, then use a heartbeat and a block-number poll as a fallback:

const ws = new WebSocket("wss://rpc.swiftnodes.io/ws/beam?key=YOUR_API_KEY");
ws.onopen = () => ws.send(JSON.stringify({
  jsonrpc: "2.0", id: 1, method: "eth_subscribe", params: ["newHeads"],
}));
ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.method === "eth_subscription") {
    const head = message.params.result;
    // Persist head.number; do not assume the next head is two seconds away.
  }
};

WebSocket heads tell you a block exists; they do not fill a gap after a disconnect. On reconnect, query the last stored block number and backfill with eth_getLogs in 100-block chunks on the free tier, or up to 5,000 blocks on a paid plan. The WebSocket guide covers reconnect patterns; the rate-limit guide covers the request budget.

Gaming-specific gotchas

BEAM is the gas token. A player cannot pay Beam gas with ETH or with a game token unless the game sponsors the transaction. Fund the account, or use the Beam account-abstraction and sponsored-gas path from the SDK, and keep a separate balance check before a transaction.

Do not infer block time from a game loop. The measured median was 9 seconds and the maximum gap was 573 seconds. If a game submits a transaction and then waits two seconds for a receipt, it will often wait longer. Poll the receipt with a bounded backoff, and show a pending state rather than failing the action.

Use block numbers for cursor state. Store blockNumber and logIndex; the timestamp is useful for display, but it is not a monotonic scheduling primitive.

Keep the fee config dynamic. The active eth_feeConfig and the genesis eth_getChainConfig disagreed on both gas limit and minimum base fee. If a dashboard hard-codes 8,000,000 gas or 10 gwei, it will misprice transactions on the live chain.

Do not assume all tracing methods exist. debug_traceTransaction worked; trace_block did not. Test the exact method, and remember that SwiftNodes gates trace/debug methods to paid plans.

Production checklist

  • Use https://rpc.swiftnodes.io/rpc/beam?key=YOUR_API_KEY for HTTP and wss://rpc.swiftnodes.io/ws/beam?key=YOUR_API_KEY for WebSocket.
  • Confirm chain ID 4337 before signing; do not accept a wallet's cached network label.
  • Read eth_feeConfig and the latest block's baseFeePerGas at startup.
  • Set a 1 wei minimum priority fee and a max fee above the current base fee.
  • Chunk eth_getLogs at 100 blocks on the free tier, 5,000 on a paid plan.
  • Store block numbers and log indexes for every indexed event.
  • Use eth_getBlockReceipts when you need a whole block's receipts, and reconcile receipt logs with filtered logs.
  • Treat finalized as the hard-finality tag, but do not assume a fixed wall-clock delay.
  • Test debug_traceTransaction before building a trace-dependent debugger.
  • Budget BEAM for gas; a game's sponsored-gas layer is separate from the RPC account.

FAQ

What is the Beam chain ID?

4337 (0x10f1). BEAM is the native gas token with 18 decimals. Beam is an Avalanche L1, not an Ethereum rollup, so gas is paid on Beam in BEAM.

What is the Beam RPC endpoint?

https://rpc.swiftnodes.io/rpc/beam?key=YOUR_API_KEY for HTTP and wss://rpc.swiftnodes.io/ws/beam?key=YOUR_API_KEY for WebSocket. The official endpoint is https://build.onbeam.com/rpc; the proxy slug is beam.

Why is Beam's block time not 2 seconds?

The active fee config has a 2-second target block rate. In our 1,000-block sample the median gap was 9 seconds, the average was 21.05 seconds, and the longest gap was 573 seconds. The chain produces blocks with activity; do not use a fixed 2-second timer for indexing or confirmations.

Why is the base fee 1,000 gwei?

The active eth_feeConfig reported a 1,000 gwei minimum base fee, and the sampled block headers carried 1,000 gwei. The genesis eth_getChainConfig and older documentation show different values. Read the live fee config or the block header rather than hard-coding a fee.

What is the eth_getLogs block range limit?

The official endpoint accepts at most 5,000 blocks per request. A 5,001-block request returned requested too many blocks from 9775380 to 9780380, maximum is set to 5000. The SwiftNodes free tier allows 100 blocks per request; a paid key can use the wider upstream range.

Does Beam support block receipts and tracing?

eth_getBlockReceipts returned a receipts array. debug_traceTransaction worked on a simple transfer; trace_block returned the method trace_block does not exist/is not available. SwiftNodes gates trace and debug methods to paid plans.

Does Beam have archive state?

The official endpoint returned a non-zero balance for an active address at 5,000,000 blocks back. Test the exact depth your indexer needs against your provider before relying on it.

How long does finality take?

In our snapshot, latest, safe and finalized all resolved to block 9,780,380, with no tag lag. Inclusion still depends on block production, so wait for a receipt and check the finalized tag instead of assuming a wall-clock delay.

Beam's EVM is easy to onboard. The block cadence and the fee-config split are the parts that break a copy-pasted Ethereum deployment. Measure the chain at runtime, store block-based state, and keep the log chunk small enough for the plan you actually use.

The Beam endpoint and measured method support are on the Beam RPC page. For free-tier limits and request sizing, see the rate-limit guide; for fee-history details, see eth_feeHistory. The SwiftNodes free tier includes 250,000 requests a month if you want to test this against your own game loop.

J
John Sullivan
Infrastructure Writer, SwiftNodes

John Sullivan covers RPC infrastructure, node operations, and multi-chain development at SwiftNodes — what it actually takes to keep endpoints fast, fresh, and reliable across EVM and non-EVM networks.

Related posts

Try SwiftNodes free — multi-chain RPC across 83 networks, flat-rate pricing, pay by card or crypto, no KYC. Get an API key in 30 seconds →