7Block Labs
Blockchain Development

ByAUJay

Summary: This guide shows exactly how to use Infura’s WebSocket eth_subscribe interface for real‑time Ethereum data, with production‑grade examples, credit budgeting math, and failure‑resilient patterns for newHeads/newBlockHeaders, logs, and pending transactions across mainnet and testnets. It’s written for architects who need precise details to ship reliable, cost‑predictable systems at scale. (docs.metamask.io)

Infura WebSocket eth_subscribe Logs Documentation: Subscribe NewBlockHeaders and Beyond

Decision‑makers increasingly expect real‑time UX: balances that update the instant a transfer lands, dashboards that tick when a validator proposes a block, and ops alerts that fire before users notice. On Ethereum, that means WebSockets and eth_subscribe.

Below is a concrete, end‑to‑end playbook for running Infura subscriptions in production: exact endpoints, pricing math, testnet coverage, code that survives idle timeouts, and reorg‑safe event handling.


What eth_subscribe gives you on Infura in January 2026

Infura supports stateful WebSocket subscriptions on multiple networks. For Ethereum specifically, use these WSS endpoints:

  • Mainnet: wss://mainnet.infura.io/ws/v3/<YOUR-API-KEY> (docs.metamask.io)
  • Sepolia testnet: wss://sepolia.infura.io/ws/v3/<YOUR-API-KEY> (docs.metamask.io)
  • Hoodi testnet: wss://hoodi.infura.io/ws/v3/<YOUR-API-KEY> (Holesky is deprecated; Hoodi replaced it for validator testing) (docs.metamask.io)

Supported Ethereum subscription types on Infura:

  • newHeads — new block headers, including during reorgs. (docs.metamask.io)
  • logs — contract events that match an address/topics filter; emits removed: true on reorg. (docs.metamask.io)
  • newPendingTransactions — pending tx hashes; not available on all networks. (docs.metamask.io)

Infura strongly recommends filtering logs by address and/or topics to control data volume and cost. (docs.metamask.io)


newHeads vs newBlockHeaders: the naming nuance you must get right

  • The raw JSON‑RPC method is eth_subscribe with the subscription type "newHeads". (geth.ethereum.org)
  • Many client libraries (notably web3.js) expose this as "newBlockHeaders". Functionally they’re the same feed of block headers. (support.infura.io)

Quick smoke test with wscat:

# Subscribe to new block headers (mainnet)
wscat -c wss://mainnet.infura.io/ws/v3/<YOUR-API-KEY> \
  -x '{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}'

You’ll receive eth_subscription notifications for each announced block header. (docs.metamask.io)

Production note: Infura charges per event for subscriptions. newHeads costs 50 credits per announced block event. At ~7,200 blocks/day, a single always‑on newHeads feed is ~360k credits/day. Budget accordingly. (docs.metamask.io)


Logs subscriptions that won’t wake you at 3 a.m.

The logs stream pushes event logs from new blocks that match your filter. Two must‑know behaviors:

  • Reorgs are explicit: the same log can be emitted twice, with removed: true when the old chain is orphaned. Code must treat delivery as at‑least‑once. (geth.ethereum.org)
  • Subscriptions are forward‑looking only; historical catch‑up belongs to eth_getLogs. Don’t try fromBlock/toBlock in eth_subscribe. Use a rolling cursor and backfill on reconnect (details below). (geth.ethereum.org)

Example filters and OR‑patterns

  • Address can be a single address or an array (multi‑contract).
  • Topics array supports null wildcards and OR by nesting arrays (e.g., [ [topicA, topicB], null, topicC ]). Use this to constrain volume without multiple subscriptions. (ethereum.org)

wscat example: listen to ERC‑20 Transfer by topic on a single contract:

# keccak256("Transfer(address,address,uint256)")
export TRANSFER_TOPIC=0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
wscat -c wss://mainnet.infura.io/ws/v3/<YOUR-API-KEY> \
  -x '{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["logs",{"address":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","topics":["'$TRANSFER_TOPIC'"]}]}'

Expect fields like address, topics, data, blockNumber, transactionHash, logIndex, and removed. Handle removed to roll back application state when necessary. (geth.ethereum.org)

Cost control: logs events are charged per block up to 300 credits/block; if your filter doesn’t match that block, you don’t pay the per‑block event charge. Tighten filters to reduce credit burn. (docs.metamask.io)


Pending transactions: powerful, noisy, and not on every network

Subscribing to newPendingTransactions streams hashes of txs entering the mempool. Infura aggregates the feed every ~700–800 ms and charges 200 credits per aggregated event. This feed isn’t available on all networks; confirm support before shipping. (docs.metamask.io)

wscat -c wss://mainnet.infura.io/ws/v3/<YOUR-API-KEY> \
  -x '{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newPendingTransactions"]}'

Use cases that win with pending: mempool monitoring for slippage‑sensitive operations, pre‑confirmation UX nudges, or risk analytics. For everyone else, prefer newHeads + logs for lower noise and cost.


Library‑specific, production‑ready snippets

Web3.js (v1.x) with keepalive and reconnect

const Web3 = require('web3');

const provider = new Web3.providers.WebsocketProvider(
  'wss://mainnet.infura.io/ws/v3/<YOUR-API-KEY>',
  {
    clientConfig: { keepalive: true, keepaliveInterval: 60_000 },
    reconnect: { auto: true, delay: 5_000, maxAttempts: 5, onTimeout: false },
  }
);

const web3 = new Web3(provider);

// Keep the socket warm and resilient
web3.eth.subscribe('newBlockHeaders')
  .on('connected', (id) => console.log('newBlockHeaders connected', id))
  .on('data', (hdr) => console.log('block', Number(hdr.number)))
  .on('error', (e) => console.error('subscription error', e));

Infura closes idle WebSocket connections after ~1 hour. Keep the connection alive with keepalive and/or by staying subscribed to newBlockHeaders. (support.metamask.io)

Ethers v6 with InfuraWebSocketProvider

import { ethers } from 'ethers';

const network = 'mainnet';
const infura = ethers.InfuraProvider.getWebSocketProvider(network, process.env.INFURA_API_KEY);

// 1) newHeads via 'block' events
infura.on('block', (blockNumber) => {
  console.log('new block', blockNumber);
});

// 2) logs filter (ERC-20 Transfer on WETH)
const TRANSFER_TOPIC = ethers.id('Transfer(address,address,uint256)');
const filter = {
  address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
  topics: [TRANSFER_TOPIC],
};
infura.on(filter, (log) => {
  // log.removed => boolean on reorg
  console.log('log', log.blockNumber, log.transactionHash, log.removed);
});

InfuraWebSocketProvider is a first‑class provider in ethers v6; prefer it to a generic WebSocketProvider when connecting to Infura. (docs.ethers.org)

Unsubscribe to stop charges and free resources

// Web3.js
const sub = web3.eth.subscribe('newBlockHeaders');
await sub.unsubscribe(); // eth_unsubscribe

// ethers v6
infura.off('block');    // removes listeners

eth_unsubscribe itself costs 10 credits; do it when you’re done. (docs.metamask.io)


Finality, reorgs, and correctness

  • Treat subscription delivery as at‑least‑once. For logs, you may see duplicates and removed: true on reorg. Build idempotent handlers keyed by (blockHash, logIndex). (geth.ethereum.org)
  • Consider “safe” and “finalized” block tags for confirmation thresholds in follow‑up queries (e.g., verify a log’s block is finalized before notifying users). These tags are standard JSON‑RPC block parameters, per EIP‑1898 and the JSON‑RPC spec. (eips.ethereum.org)
  • Backfill after reconnect: cache the last seen block number (or header hash), then on reconnect:
    1. get the latest safe or finalized block;
    2. call eth_getLogs over the gap [lastSeen+1 … latestSafe];
    3. replay to your app state;
    4. resume live subscription.
      This eliminates silent data loss while the socket was down. (support.metamask.io)

Exact credit math and rate limits you can plan around

Infura uses a credit‑based billing model for methods and subscription events:

  • eth_subscribe call: 5 credits (one‑time per subscription). (docs.metamask.io)
  • Event charges:
    • newHeads: 50 credits per block event (~360k/day if always on).
    • logs: up to 300 credits per block that matches your filter (zero if no match).
    • newPendingTransaction: 200 credits per ~0.7–0.8 s aggregated event. (docs.metamask.io)
  • eth_unsubscribe: 10 credits. (docs.metamask.io)

Daily and per‑second quotas (as of late‑2025/early‑2026; verify your plan):

  • Per‑second caps: Core 2,000 credits/s; Developer 4,000; Team 40,000. Hitting the throughput cap returns 429 and is not charged. (support.infura.io)
  • Daily credits: plan‑dependent; consult your dashboard/help center for current allowances and 402 behavior at daily cap reset (00:00 UTC). (support.infura.io)

Rule of thumb budgeting

  • One always‑on newHeads feed per environment: ~360k credits/day.
  • One well‑filtered logs feed for a single busy protocol can range from near‑zero (few matches) to multiple millions of credits/day if the filter matches many blocks’ events. Start narrow: single address + exact event signature topic0. (docs.metamask.io)

Network coverage (and why Hoodi matters)

Subscriptions are available on Ethereum mainnet and key testnets via the WSS endpoints listed earlier. As of September 2025 Holesky is deprecated; Hoodi is the go‑forward validator testnet and Infura exposes HTTPS and WSS endpoints for it. Prefer Sepolia for application testing and Hoodi for validator/infrastructure scenarios. (ethereum.org)

Infura also supports WebSocket subscriptions on major L2s (Arbitrum, Optimism, Base, Polygon, Scroll, ZKsync Era mainnet); some are still in public beta. Validate per‑chain support, especially for newPendingTransactions. (docs.metamask.io)


Best emerging practices we see succeeding in 2025–2026

  1. Multiplex minimal subscriptions; filter hard
  • Fewer, broader filters cost less to maintain than many tiny subscriptions, but only if the filters are precise (address + topic0). Use topics OR arrays to combine closely related events. (ethereum.org)
  1. Always implement reconnect, idle keepalive, and backfill
  • Infura closes idle sockets after ~1 hour; set keepaliveInterval ~60s and retain a small newBlockHeaders subscription to keep the pipe warm. On reconnect, backfill with eth_getLogs using your block cursor. (support.metamask.io)
  1. Program for reorgs as a first‑class case
  • Up to N confirmations, logs may be removed. If removed: true, reverse your side effects (e.g., decrement balances, cancel notifications) and wait for finalized before irreversible actions. (geth.ethereum.org)
  1. Separate “user UX” from “accounting truth” paths
  • Use newHeads/logs to power instant UI updates, but mirror to a durable queue (e.g., Kafka) and re‑hydrate with finalized data for back‑office pipelines.
  1. Control cost with a per‑service budget
  • Translate product SLOs into credit budgets. For example, you might cap one microservice to 500k credits/day by narrowing its topics and pausing subscriptions during low traffic windows. Event credits for newHeads/logs/pending are predictable and published. (docs.metamask.io)
  1. Detect indexing edge cases (last block race)
  • If eth_getLogs for the tip looks sparse, check logsBloom on that block to validate whether logs exist before retrying; brief delays are normal at the very tip. (support.metamask.io)

Concrete patterns you can copy

A) Resilient “subscribe + backfill” loop (pseudocode)

let lastSeen = loadCheckpoint(); // block number you processed to completion

function onConnected() {
  // 1) Backfill safely to the latest safe block
  const latestSafe = eth_getBlockByNumber('safe');        // tag is standard
  const from = lastSeen + 1;
  if (from <= latestSafe.number) {
    const logs = eth_getLogs({ fromBlock: from, toBlock: latestSafe.number, address, topics });
    process(logs); // idempotent handlers; handle duplicates
    lastSeen = latestSafe.number;
    saveCheckpoint(lastSeen);
  }

  // 2) Subscribe for live events
  subscribe('logs', { address, topics }, onLog, onError);
}

function onLog(log) {
  process([log]);
  if (!log.removed) {
    lastSeen = Math.max(lastSeen, log.blockNumber);
    saveCheckpoint(lastSeen);
  }
}

The key is the handoff from historical to live without gaps or double‑counting, plus idempotent consumers to tolerate duplicates/removed logs. (ethereum.org)

B) Multi‑contract OR filtering in one subscription

const filter = {
  address: [
    '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
    '0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
  ],
  topics: [ethers.id('Transfer(address,address,uint256)')],
};
// One subscription covers both contracts’ Transfer events.

Use a single subscription when the event schema is identical and the consumers are the same. It’s simpler and cheaper than two separate feeds. (ethereum.org)

C) Cost‑aware planning example

  • You need: 1 newHeads (global), 1 logs for your protocol, 1 pending (ops only during business hours).
  • Daily baseline: newHeads ≈ 7,200 blocks × 50 = ~360k credits.
  • Logs: if your filter hits on 1 of every 60 blocks, that’s ~120 matching blocks × 300 = ~36k credits.
  • Pending: 8 hours/day × 60 min × ~75 events/min × 200 ≈ 72,000 credits.
  • Total ≈ 468k credits/day. This fits comfortably under Developer plan 15M/day; even Core 6M/day if nothing else is heavy. Tune filters first if you need more headroom. (docs.metamask.io)

Operational guardrails

  • Backpressure: Subscribers must process events quickly. If you do heavy work, buffer to a queue and ack off the WebSocket fast.
  • Error semantics:
    • 429 = throughput exceeded (not charged).
    • 402 = daily credits exceeded (not charged); activity resumes after daily reset. Build alerts and fallbacks. (docs.metamask.io)
  • Security: keep API keys server‑side; if you must use them in clients, proxy via your backend and multiplex subscriptions.
  • Lifecycle: always call eth_unsubscribe or remove listeners when a user navigates away to stop paying for unused feeds. (docs.metamask.io)

Troubleshooting checklist (fast wins)

  • “My WebSocket closed overnight.”
    Add keepalive/reconnect and keep a newBlockHeaders subscription; Infura closes idle sockets after ~1 hour. (support.metamask.io)
  • “I missed events during a disconnect.”
    Use your checkpoint and eth_getLogs to backfill the gap, then resume live. (support.metamask.io)
  • “I saw a log, then removed: true.”
    Reorg. Revert any state derived from that log and wait for safe/finalized before irreversible actions. (geth.ethereum.org)
  • “Which endpoint do I use for testnets now?”
    Ethereum: Sepolia for app testing; Hoodi for validator scenarios. Use the explicit WSS endpoints published by Infura. (docs.metamask.io)
  • “Do I need multiple sockets?”
    Start with one socket per service and multiplex subscriptions; only split when throughput or failure domains demand it. Validate network‑specific support for newPendingTransactions. (docs.metamask.io)

Copy‑paste quickstart for your runbook

Raw JSON‑RPC messages

// Subscribe new heads
{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}

// Subscribe logs with address + topic0 filter
{"jsonrpc":"2.0","id":2,"method":"eth_subscribe",
 "params":["logs",{"address":"0xYourContract","topics":["0xTopicHash"]}]}

// Unsubscribe
{"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x<subscription-id>"]}

Reference behaviors: removed on reorgs; logs filter supports OR arrays and null wildcards; subscriptions are WebSocket‑only. (geth.ethereum.org)


The bottom line for decision‑makers

  • Subscriptions are the right tool for low‑latency UX and operational awareness—if you budget credits and design for reorgs.
  • Infura’s pricing transparency (per‑event credits) makes cost predictable; you control spend with filters and schedules. (docs.metamask.io)
  • Hoodi replaces Holesky in testnet planning; update your environments and CI pipelines accordingly. (ethereum.org)

If you want a turnkey implementation—replay‑safe, cost‑capped, with dashboards and alerts—7Block Labs ships hardened subscription services and can integrate them into your stack in days, not weeks.


Sources and further reading

  • Infura/MetaMask developers: eth_subscribe, networks, WebSockets, pricing and credits. (docs.metamask.io)
  • Geth RPC pub/sub semantics (logs removed on reorg). (geth.ethereum.org)
  • JSON‑RPC block tags, EIP‑1898 (finalized/safe usage). (ethereum.org)
  • Keep WebSocket subscriptions alive and reconnect on failures. (support.metamask.io)

Like what you're reading? Let's build together.

Get a free 30‑minute consultation with our engineering team.

Related Posts

7BlockLabs

Full-stack blockchain product studio: DeFi, dApps, audits, integrations.

7Block Labs is a trading name of JAYANTH TECHNOLOGIES LIMITED.

Registered in England and Wales (Company No. 16589283).

Registered Office address: Office 13536, 182-184 High Street North, East Ham, London, E6 2JA.

© 2025 7BlockLabs. All rights reserved.