On Monad’s public endpoints, eth_getLogs accepts at most 101 blocks per request (toBlock minus fromBlock of at most 100) on rpc.monad.xyz and the Monad Foundation endpoints, while other public providers allow more: 1,000 blocks on rpc3.monad.xyz and 20,000 on rpc2.monad.xyz with an address filter. eth_call and eth_estimateGas are capped at 150,000,000 gas on every endpoint we measured, and batches are limited or refused outright. We measured all of this on 24 October 2026, between 12:05 and 12:40 UTC.
This article lists the numbers per host, shows what the errors look like, gives you a tested eth_getLogs pager and covers block time, finality and Monad’s own WebSocket subscriptions. Where Monad’s documentation and our measurements disagree, both values are shown. For the endpoint URL, chain ID and wallet setup, see the Monad network page.
Public endpoints change without notice, so treat every number below as a dated snapshot and re-measure before you build on it.
The public endpoints we measured
Monad’s Network Information page lists five mainnet endpoints. We use the short names below.
rpc:rpc.monad.xyz(QuickNode per the docs)rpc1:rpc1.monad.xyz(Alchemy per the docs)rpc2:rpc2.monad.xyz(Goldsky Edge)rpc3:rpc3.monad.xyz(Ankr)mf:rpc-mainnet.monadinfra.com(Monad Foundation)
One oddity: rpc1 is documented as an Alchemy endpoint, but in our tests it behaved like the Foundation edge. Its response headers matched mf, it rejected batches the same way and it capped eth_getLogs at the same span. Name the hosts, not the providers, in your own notes.
Measured limits per public endpoint
| Endpoint | eth_getLogs blocks per request | eth_call and eth_estimateGas gas | Batch | State depth |
|---|---|---|---|---|
rpc | 101 | 150,000,000 | 50 items per second | about 8 days |
rpc1 | 101 | 150,000,000 | any batch refused | full history |
rpc2 | 20,000 with an address filter | not measured | 150 items passed | full history |
rpc3 | 1,000 filtered, 727 unfiltered | 150,000,000 | 10 items | about 32 hours |
mf | 101 | 150,000,000 | any batch refused | full history |
How to read it:
- Blocks per request are counted inclusively, so 101 blocks is a span (
toBlockminusfromBlock) of 100. Onrpc,rpc1,mfand the testnet endpoint, 101 blocks counted inclusively pass and 102 fail. Onrpcthe same result held with a topic filter, an address filter and no filter at all. Onrpcan unfiltered 101-block call returned 8,965 logs, so we saw no log-count cap there. - Gas is the largest
gasvalue that succeeded. 150,000,000 passed and 150,000,001 failed on all four hosts. That equals the block gas limit, which was 150,000,000 in all 1,001 blocks we sampled. - Batch on
rpcis a per-second item quota: a batch of 50 passed, and in a batch of 51 only 50 items succeeded.rpc1andmfreturned the plain textRestricted JSON RPC methodfor any array, even one item, while single requests worked. - State depth is how far back
eth_getBalanceworked, found by binary search: 2,316,645 blocks onrpc, 384,991 blocks onrpc3, and onrpc1,rpc2andmfstate read back identically at blocks 30,000,000, 60,000,000 and 100,000,000, which we take as full history.
The single-request rate limit is not in the table because we could not measure it cleanly without hammering shared infrastructure. Eighty parallel eth_chainId calls on rpc all succeeded, but our own client throttled itself. On rpc3, the 16th consecutive 10-item batch drew HTTP 429 with code -32090 and the message Too many requests, reason: call rate limit exhausted, retry in 10s, while a single 11-item batch is refused at once with 413 and code -32062. For the general mechanics of limits and backoff, read RPC rate limits explained.
Where Monad’s docs and our measurements disagree
Monad documents its limits on the JSON-RPC overview and the Network Information page. Neither page shows a date on screen; their page metadata gives last-modified dates of 22 October and 4 September 2026. Here is each mismatch, with the docs value first and our measurement second.
- Gas cap for
eth_call. Docs: 200M gas onrpc,rpc1andmf, 1B onrpc3. Measured on 24 October 2026: 150,000,000 on all four. Above 200,000,000 the message differs (gas limit too highonrpc,user-specified gas exceeds provider limiton the others), but the real ceiling is the block gas limit. - Request rate and batches on
rpc. Docs: 25 requests per second, batch of 100. Measured: 50 batch items per second, with the 51st item in a batch failing. We did not measure the docs’ 25 rps for single calls. - Batches on
rpc2andmf. Docs:rpc2batch of 10,mfbatch of 1. Measured: a 150-item batch fully succeeded onrpc2, andmfrefused every array. eth_getLogsonrpc1. Docs: 1,000 blocks and 10,000 logs, whichever is more constraining. Measured: the same 101-block limit (span 100) asrpc.- Block time. One docs page says “2.5 blocks every second” (400 ms), while others say 300 ms. We measured 302 ms. More in the next section.
debug_traceCall. Docs: it returns-32602 Invalid paramsif you omit the trace options. Measured: it worked without options onrpcandrpc1and returned a call-tracer result, although the docs also saydebug_methods are disabled onrpc1. Onrpc3the method is disabled.newPendingTransactions. Docs: not supported. Measured: see the WebSocket section.- State depth. Docs: roughly 40,000 blocks on a 2 TB SSD. Measured: about 2.3 million blocks on
rpc, about 385,000 onrpc3and full history onrpc1,rpc2andmf.
Do not plan around the optimistic number. When docs and a live call disagree, the live call is what your code will hit.
What the errors look like and how to page eth_getLogs
Error codes for the same problem differ by host, so do not key your handling on the code alone. Here is the range error per endpoint:
rpcand the testnet endpoint:-32614,eth_getLogs is limited to a 100 rangerpc1andmf:-32602,block range too largerpc3:-32062,Block range is too large, and for a busy unfiltered range-32000,response exceeds size limitrpc2:-32012,getLogs request exceeded max allowed range
-32602 is also the generic “Invalid params” code, so a range error on rpc1 looks like a malformed request unless you read the message. Other errors you will meet:
-32007,50/second request limit reached: an item in an over-quota batch onrpc-32062,Batch size too largewithHTTP 413: more than 10 items onrpc3-32000,gas limit too high:gasabove the cap onrpc
The reliable fix for a range error is to page the range. The script below walks a block range in windows of 100 blocks, runs four windows at a time and merges the results in order. It uses viem, which ships a Monad chain definition. Swap RPC_URL for your own endpoint and set MAX_BLOCKS to whatever your provider allows.
import { createPublicClient, http, parseAbiItem } from 'viem';
import { monad } from 'viem/chains';
const RPC_URL = process.env.RPC_URL ?? 'https://rpc.monad.xyz'; // swap for your own endpoint
const MAX_BLOCKS = Number(process.env.MAX_BLOCKS ?? 100); // blocks per eth_getLogs call
const WMON = '0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A';
const transfer = parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)');
const client = createPublicClient({ chain: monad, transport: http(RPC_URL, { retryCount: 3, retryDelay: 500 }) });
// Walk [from, to] in windows of MAX_BLOCKS blocks (inclusive), a few at a time.
async function getLogsChunked({ from, to, concurrency = 4, ...filter }) {
const windows = [];
for (let start = from; start <= to; start += BigInt(MAX_BLOCKS)) {
const end = start + BigInt(MAX_BLOCKS) - 1n;
windows.push([start, end < to ? end : to]);
}
const out = new Array(windows.length);
let next = 0;
await Promise.all(Array.from({ length: concurrency }, async () => {
while (next < windows.length) {
const i = next++;
const [fromBlock, toBlock] = windows[i];
out[i] = await client.getLogs({ ...filter, fromBlock, toBlock });
}
}));
return { logs: out.flat(), calls: windows.length };
}
const chainId = await client.getChainId();
const head = await client.getBlockNumber();
const to = head - 2n; // stay behind the tip
const from = to - 999n; // 1,000 blocks = about 5 minutes on Monad
const t0 = Date.now();
const { logs, calls } = await getLogsChunked({ from, to, address: WMON, event: transfer });
console.log({ chainId, head, from, to, calls, logs: logs.length, ms: Date.now() - t0 });
// What the same 1,000-block request does without chunking:
try {
await client.getLogs({ address: WMON, event: transfer, fromBlock: from, toBlock: to });
} catch (e) {
console.log('single 1,000-block call ->', e.details ?? e.shortMessage);
}
We ran it with viem 2.57.11 on Node v24.19.0 at 12:34 UTC against https://rpc.monad.xyz:
{ chainId: 143, head: 116184782n, from: 116183781n, to: 116184780n, calls: 10, logs: 161, ms: 499 }
single 1,000-block call -> eth_getLogs is limited to a 100 range
Ten calls of 100 blocks returned 161 WMON Transfer logs, and the single 1,000-block call failed with the range error. Running the same script with only a URL change and MAX_BLOCKS=1000 against rpc3.monad.xyz used one call and also returned 161 logs for its own window.
To check that paging loses nothing, we fetched one fixed window of 1,000 blocks twice: as ten chunks of 100 on rpc and as a single call on rpc3. Both returned 161 logs, and comparing them by transaction hash and log index showed them identical.
Monad produces about 3.3 blocks per second, so a day is roughly 288,000 blocks. At 100 blocks per call that is about 2,880 calls per day per filter. The script ends its range two blocks behind the head, which is about where the finalized tag sits (see the finality section below). Reorg handling, deduplication and storage are covered in the production eth_getLogs pipeline guide.
Block time and finality
Monad’s docs give a 300 ms block time, 600 ms finality (2 slots) and 300 ms speculative finality (1 slot), on the current facts page. Our measurements agree with 300 ms:
- 302 ms mean over 1,000 consecutive blocks on
rpc(302 seconds of timestamps) - 305 ms mean over 200 blocks
- 302 ms mean arrival gap on a live
newHeadsstream, 98 headers in 30 seconds with no gaps - 300 ms on the testnet over 200 blocks
Block timestamps are whole seconds, so a single gap between two blocks is 0 or 1 second. Only averages over many blocks mean anything. The one docs page that says “2.5 blocks every second” is the real-time data sources page, which contradicts the 300 ms stated elsewhere on the same site. Use 300 ms.
Finality shows up in the block tags. Monad maps latest to the Proposed state, safe to Voted and finalized to Finalized, and pending behaves the same as latest. In 12 of 12 samples on rpc, latest minus safe was 1 block and latest minus finalized was 2 blocks. Two blocks at about 300 ms each is the documented 600 ms.
Which tag to read depends on the job:
latestis fastest but backed by speculative execution, so the data may never become finalsafesuits a UI that wants a stronger signal with little delayfinalizedsuits anything that moves money or writes to a database you cannot roll back
Real-time data over WebSocket
Monad extends the standard Ethereum subscriptions. We tested wss:// on rpc, rpc1, rpc3, mf and the testnet endpoint, and all four subscription types below returned a subscription id on each of them:
newHeads: a header when a block is Proposed and speculatively executedlogs: matching logs at the same pointmonadNewHeads: the same headers, plusblockIdandcommitState, once per state changemonadLogs: the same for logs
wss://rpc2.monad.xyz opened and then closed with code 1006 within about 0.3 seconds, and the docs list no WebSocket URL for it. syncing and an invented type both returned -32602 Invalid params while the valid types worked on the same socket, so the error is a real answer and not a dead connection.
The four commit states
monadNewHeads sends up to four messages per block, one for each commit state: Proposed, Voted, Finalized and Verified. We measured 65 of each in a 20-second run. Seen from our test client, a block took on average:
- 293 ms from Proposed to Voted
- 555 ms from Proposed to Finalized (minimum 387 ms, maximum 712 ms)
- 1,463 ms from Proposed to Verified
That is 124 full lifecycles out of 134 blocks seen in a 40-second run on rpc. The figures include network delay from our test machine to the endpoint, so your numbers will differ. Monad’s docs say Voted may be skipped when consensus runs ahead of execution; we saw it skipped 0 times in those 124. They also say an abandoned block gets no explicit event and the docs call a competing proposal for the same height very rare. We saw none in 134 blocks. If you act on a Proposed block, you must handle the case that it never finalizes.
Here is the sample we tested. It subscribes to both newHeads and monadNewHeads and groups the states by blockId. Install the one dependency first with npm i ws.
import WebSocket from 'ws';
const WS_URL = process.env.WS_URL ?? 'wss://rpc.monad.xyz'; // swap for your own endpoint
const RUN_SECONDS = Number(process.env.RUN_SECONDS ?? 10);
const ws = new WebSocket(WS_URL);
const subs = new Map(); // subscription id -> name
const lifecycle = new Map(); // blockId -> { number, states: [commitState...] }
let heads = 0;
ws.on('open', () => {
ws.send(JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_subscribe', params: ['newHeads'] }));
ws.send(JSON.stringify({ jsonrpc: '2.0', id: 2, method: 'eth_subscribe', params: ['monadNewHeads'] }));
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.id) { // subscribe replies
if (msg.error) console.error('subscribe failed:', msg.id, msg.error);
else subs.set(msg.result, msg.id === 1 ? 'newHeads' : 'monadNewHeads');
return;
}
const { subscription, result } = msg.params;
if (subs.get(subscription) === 'newHeads') {
heads++;
} else {
const entry = lifecycle.get(result.blockId) ?? { number: parseInt(result.number, 16), states: [] };
entry.states.push(result.commitState);
lifecycle.set(result.blockId, entry);
}
});
setTimeout(() => {
ws.close();
console.log(`newHeads: ${heads} headers in ${RUN_SECONDS}s`);
const done = [...lifecycle.values()].filter((b) => b.states.includes('Finalized'));
console.log(`monadNewHeads: ${lifecycle.size} blocks seen, ${done.length} reached Finalized`);
for (const b of done.slice(0, 3)) console.log(' block', b.number, b.states.join(' -> '));
}, RUN_SECONDS * 1000);
Output from wss://rpc.monad.xyz at 12:35 UTC:
newHeads: 31 headers in 10s
monadNewHeads: 36 blocks seen, 32 reached Finalized
block 116184893 Proposed -> Voted -> Finalized -> Verified
block 116184892 Finalized -> Verified
block 116184894 Proposed -> Voted -> Finalized -> Verified
Block 116184892 shows only two states because the subscription joined mid-lifecycle. A Finalized message for a block arrives about when the next block is Proposed, and a Verified message refers to a block four heights behind the newest Proposed one. We did not test reconnect logic or ping handling, so add your own before production, as covered in WebSocket vs polling, measured.
Do not subscribe to every log
An unfiltered logs subscription delivered roughly 200 to 230 messages per second on mainnet in our runs, and an unfiltered monadLogs subscription roughly 770 to 900 per second. The rate follows chain activity, so treat these as orders of magnitude. A logs subscription filtered to one address (WMON) delivered 2 logs in 15 seconds while newHeads on the same socket kept up with every block.
Always filter by address and topic. On BoltRPC every pushed WebSocket message bills once, so an unfiltered log stream at hundreds of messages per second is hundreds of billed pushes per second.
newPendingTransactions
Monad’s docs say syncing and newPendingTransactions are not supported, and there is no global mempool. On rpc1, rpc3 and mf the subscribe call returned -32602 Invalid params, as documented. On rpc and the testnet endpoint it did something stranger: the subscribe call returned a subscription id, then the server closed the socket after 1.8 to 3.3 seconds with code 1001 and the reason upstream went away. No pending transaction ever arrived. A control socket subscribed to newHeads only stayed open for 30 seconds and delivered 97 headers. Do not build on pending transactions on Monad.
Methods that are not there
Some methods that exist on other EVM chains do not exist here. Each result below was checked against a control call (eth_chainId) that succeeded on the same host:
eth_getProofreturns-32601 Method not foundonrpc,rpc1andrpc3eth_newFilter,eth_newBlockFilterandeth_getFilterChangesreturn-32601onrpcandrpc1, and-32075 Method disabledonrpc3trace_blockreturns-32601onrpcandrpc1txpool_contentandeth_blobBaseFeereturn-32601(onrpc3,txpool_contentreturns-32075)
eth_getBlockReceipts works on rpc. Since filter methods are missing, poll eth_getLogs in windows or use a filtered logs subscription instead of installing a filter.
History depth differs by host
Monad’s docs say full nodes do not serve arbitrary old state, only recent state. How recent is a host decision. Reading a balance at an old block worked back about 8 days on rpc, about 32 hours on rpc3 and full history on rpc1, rpc2 and mf. The failure message is Block requested not found. Request might be querying historical state that is not available. If possible, reformulate query to point to more recent blocks, with code -32602. Block lookups are a different matter: eth_getBlockByNumber a million blocks back worked on all three hosts. Treat these depths as a snapshot from 12:32 UTC on 24 October 2026.
Running this on BoltRPC
BoltRPC has Monad (chain ID 143) live with a native WebSocket, so subscriptions are served by the node and not synthesised. Debug methods are available and trace methods are not. We repeated the key measurements through a BoltRPC key on 24 October 2026:
eth_getLogson WMONTransferevents returned 151 logs for 1,000 blocks in 0.6 seconds and 3,615 logs for 20,000 blocks in 2.2 seconds. The 100-block limit of the Foundation endpoints does not apply.eth_callwithgasset to 150,000,001 is refused withuser-specified gas exceeds provider limit, the same 150,000,000 cap as every public endpoint.monadNewHeadsworks over the WebSocket endpoint: a 4-second run delivered Proposed, Voted and Finalized updates.eth_getProofreturns-32601 Method not found, as onrpc.monad.xyz.
The general docs describe eth_getLogs on the gateway like this: no hard block-range cap, a wide range is split into chunks and fanned out across the chain’s nodes server-side, then merged into one answer. A single request returns at most 250,000 logs; past that it is refused with an error that names a narrower range that fits. Each request has a 60-second deadline. In our own test on Ethereum the same day, the cap that refused a busy query was stated in bytes (query returned more than 268435456 bytes) rather than logs, and the error still named a narrower block range. Test your widest Monad filter on your own key.
The URLs follow the usual pattern:
https://endpoints.boltrpc.io/rpc/YOUR_API_KEY/monad
wss://endpoints.boltrpc.io/ws/YOUR_API_KEY/monad
Both scripts above read the endpoint from RPC_URL and WS_URL, so you can point them at BoltRPC without editing the code. Start with MAX_BLOCKS at 1,000 and raise it while the call still succeeds. We did not test monadLogs through BoltRPC; monadNewHeads worked as described above. The BoltRPC docs do not state whether Monad has archive access, so ask before you plan around deep history. Request unit costs are on the pricing page.
FAQ
What is the eth_getLogs block range limit on Monad?
On the public endpoints we measured on 24 October 2026, rpc.monad.xyz, rpc1.monad.xyz and rpc-mainnet.monadinfra.com accept at most 101 blocks per request (a span of 100), rpc3.monad.xyz accepts 1,000 blocks with a filter and rpc2.monad.xyz accepted 20,000 blocks with an address filter. Page larger ranges in windows of 100 blocks.
What is the maximum gas for eth_call on Monad?
150,000,000 gas on the four public endpoints we measured (rpc, rpc1, rpc3 and mf), for both eth_call and eth_estimateGas. That equals the block gas limit. Monad’s docs list 200M or 1B for some of these endpoints, but 150,000,001 failed everywhere we tried it.
What is the Monad RPC rate limit?
It depends on the host. Monad’s docs list 25 requests per second on rpc.monad.xyz, 15 on rpc1.monad.xyz, 20 on the Foundation endpoint and 300 per 10 seconds on rpc2 and rpc3. We measured a limit of 50 batch items per second on rpc, a 10-item batch limit on rpc3 and no batches at all on rpc1 and the Foundation endpoint. We did not measure single-call rates.
Does Monad support newPendingTransactions?
No. Monad’s docs list it as unsupported, and rpc1, rpc3 and the Foundation endpoint return -32602. On rpc.monad.xyz the subscribe call returns an id, then the server closes the socket with code 1001 after 2 to 3 seconds and sends nothing.
What is monadNewHeads?
It is a Monad-specific WebSocket subscription that sends each block header once per commit state (Proposed, Voted, Finalized and Verified) with a blockId and a commitState field. On rpc.monad.xyz a block went from Proposed to Finalized in 555 ms on average as seen from our client.
Test your Monad limits on your own key
Run the two scripts against your BoltRPC endpoint and see which filters and subscriptions pass. The free trial lasts 14 days.
Start your free 14-day trial: dashboard.boltrpc.io/signup
