Skip to main content
Optimism produces Flashblocks — 250 ms pre-confirmation sub-blocks — but exposes them through the standard pending block tag, not through WebSocket subscriptions. To follow transactions in real time on Optimism, poll the pending block roughly every 250 ms and track the transactions that are new since the last poll. This guide does that with tested Python and JavaScript, then covers the pre-confirmed read methods.
For how Flashblocks works on Optimism, see Flashblocks on Optimism. For Base, which also exposes Flashblocks as WebSocket subscriptions, see Stream real-time transactions on Base with Flashblocks.

Prerequisites

  • An Optimism mainnet HTTPS endpoint. On Chainstack, copy it from your Optimism node’s Access and credentials tab — it looks like https://optimism-mainnet.core.chainstack.com/<key>. The examples use YOUR_OPTIMISM_HTTPS_ENDPOINT as a placeholder.
  • Python 3.8+ with the requests library (pip install requests), or Node.js 18+ (the fetch API is built in, so there are no dependencies to install).

Why Optimism uses the pending tag, not eth_subscribe

On Optimism, the Flashblocks subscriptions that exist on Base — eth_subscribe("newFlashblockTransactions"), newFlashblocks, and pendingLogs — are not exposed; calling them returns -32602 Invalid params. And like every OP Stack chain, Optimism has no public mempool, so eth_subscribe("newPendingTransactions") is effectively empty (see Mempool configurations). The supported real-time interface is the pending block tag, which reflects the latest Flashblock and advances every ~250 ms.

Track pre-confirmed transactions

Poll eth_getBlockByNumber("pending", false) every 250 ms and emit transaction hashes you have not seen before. Each poll returns the block the sequencer is currently building; within one block number the transaction list grows every ~250 ms as new Flashblocks arrive.
import time

import requests

HTTP = "YOUR_OPTIMISM_HTTPS_ENDPOINT"  # https://optimism-mainnet.core.chainstack.com/<key>


def rpc(method, params):
    response = requests.post(
        HTTP,
        json={"jsonrpc": "2.0", "id": 1, "method": method, "params": params},
        timeout=20,
    )
    return response.json()["result"]


seen = set()
while True:
    block = rpc("eth_getBlockByNumber", ["pending", False])  # False -> transactions are hashes
    for tx_hash in block["transactions"]:
        if tx_hash not in seen:
            seen.add(tx_hash)
            print(tx_hash)
    time.sleep(0.25)
You get a continuous flow of newly pre-confirmed transaction hashes:
0x2e8bb259f1f2e0744238e591155db3a075d24d84bd77645681b530b9807a5859
0x7a5a51b32639f5b2a10060c5fc63d90e738d3d2c2d7880c2ca99c7fe1f721737
0x918d6dda3c534f67aefa91ef517fc0a66af2d5d054f0e341b25ecdc568584830
The seen set de-duplicates hashes that repeat across polls of the same in-progress block. In a test against Optimism mainnet this emitted roughly 33 new transactions per second, tracking network activity.

How the pending block evolves

Each poll of the pending tag returns the same block number with more transactions and a different pre-confirmation hash, until the block seals and the number advances. Sampled every 250 ms:
#153440621 hash=0x7380844aa8d8 txs=41
#153440621 hash=0xd94ae98d59f2 txs=86
#153440621 hash=0x75486d38e01c txs=119
#153440622 hash=0xf35345a6e335 txs=24
#153440622 hash=0x55f66d179dc7 txs=45
Unlike Base, where the streamed block carries a zero hash until it seals, Optimism’s pending hash is non-zero and changes with each Flashblock — so track transactions by their own hash, not by the block hash.

Read pre-confirmed state

The pending tag also serves request-response reads against pre-confirmed state, returning answers in ~250 ms instead of waiting ~2 s for the next canonical block:
  • eth_getBlockByNumber, eth_call, eth_getBalance, eth_getTransactionCount, eth_getCode, eth_getStorageAt, and eth_estimateGas with the pending tag.
  • eth_getTransactionReceipt and eth_getTransactionByHash return the pre-confirmed receipt or transaction as soon as it lands in a Flashblock.
  • eth_sendRawTransactionSync submits a signed transaction and returns its pre-confirmed receipt in the same response.
curl -s -X POST YOUR_OPTIMISM_HTTPS_ENDPOINT \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["0x<tx hash>"]}'

Production considerations

  • Pre-confirmations are not final. The pending block can change until the 2-second canonical block seals, so for settlement confirm against latest or a finalized block.
  • Poll at ~250 ms. That matches the Flashblock cadence; polling much faster mostly returns the same state.
  • De-duplicate by transaction hash. The same transaction appears in every poll of the block it belongs to until that block seals.
  • newPendingTransactions will not help. Optimism has no public mempool, so the subscription is effectively empty — the pending tag is the real-time source.
Last modified on June 26, 2026