# Ethereum Chain Scraper: Gas, Blocks & Balances (`arman-bd/ethereum-rpc-scraper`) Actor

Query Ethereum through public JSON-RPC endpoints: gas price, block data, account balances and chain height. No API key, no third-party indexer.

- **URL**: https://apify.com/arman-bd/ethereum-rpc-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Business, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 result scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Ethereum Chain Scraper: Gas, Blocks & Balances

![Ethereum Chain Scraper: Gas price, chain head, block headers and account balances, decoded from public JSON-RPC nodes with BigInt precision](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/ethereum-rpc-scraper.jpg)

**Ethereum Chain Scraper** reads Ethereum straight from public JSON-RPC nodes: current gas price and base fee, chain head, full block headers, and account balances with nonces. Works on **mainnet, Sepolia, Base and Arbitrum One**.

No credentials to manage. No third-party index sitting between you and the chain, this talks to the same JSON-RPC interface your wallet does, and every number it emits is decoded from the node's own hex.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/ethereum-rpc-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/ethereum-rpc-scraper.md
```

### What you get

One dataset row per query. Fields are emitted per method, so a `gasPrice` row carries fee fields and a `getBalance` row carries balance fields.

| Output field | Appears on | Meaning |
|---|---|---|
| `method` | all | The JSON-RPC method behind the row, e.g. `eth_getBalance` |
| `network`, `chainId`, `rpcEndpoint` | all | Which chain, and the node that actually answered |
| `blockNumber` | gasPrice, blockNumber, getBlock | Block height as a decimal integer |
| `gasPriceWei`, `gasPriceGwei` | gasPrice | Current gas price, decoded from hex |
| `baseFeePerGasWei`, `baseFeePerGasGwei` | gasPrice, getBlock | EIP-1559 base fee for the block |
| `nextBlockBaseFeePerGasWei/Gwei` | gasPrice | The **next** block's base fee, what you'll actually pay if you send now |
| `maxPriorityFeePerGasWei/Gwei` | gasPrice | Suggested priority tip |
| `address`, `blockTag` | getBalance | The account queried and the point in history |
| `balanceWei`, `balanceEth`, `currency` | getBalance | Exact balance, see the precision note below |
| `nonce` | getBalance | Transaction count for the account |
| `blockHash`, `parentHash` | getBlock | Block identity |
| `timestamp`, `timestampUnix` | getBlock | ISO-8601 and raw Unix seconds |
| `transactionCount` | getBlock | Number of transactions in the block |
| `miner` | getBlock | Fee recipient / proposer address |
| `gasUsed`, `gasLimit`, `blockSizeBytes` | getBlock | Block utilisation |
| `scrapedAt` | all | Run timestamp |

A `RUN_SUMMARY` record in the key-value store holds per-run counts, the queries used, per-query failures, which endpoints were used and which ones were rejected on the way there.

### Common use cases

**1. Monitor gas to time transactions.** Schedule this every few minutes; `nextBlockBaseFeePerGasGwei` is the number to alert on.

```json
{
 "queries": ["gasPrice"],
 "network": "mainnet"
}
```

**2. Track treasury wallet balances.** Reading at `finalized` rather than `latest` gives you a figure that cannot be reorged away.

```json
{
 "queries": ["getBalance"],
 "addresses": [
 "0x00000000219ab540356cBB839Cbe05303d7705Fa",
 "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
 ],
 "blockNumbers": ["finalized"],
 "network": "mainnet"
}
```

**3. Build on-chain alerts without an indexer.** Chain head plus block headers is enough to detect stalls, unusual block sizes or a change in proposer.

```json
{
 "queries": ["blockNumber", "getBlock"],
 "blockNumbers": ["latest", "finalized"],
 "network": "base"
}
```

### Quick start

Simplest possible run:

```json
{
 "queries": ["gasPrice", "blockNumber"]
}
```

Everything at once on mainnet:

```json
{
 "queries": ["gasPrice", "blockNumber", "getBalance", "getBlock"],
 "addresses": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"],
 "blockNumbers": ["latest", "18000000"],
 "network": "mainnet"
}
```

Through your own node:

```json
{
 "queries": ["gasPrice"],
 "network": "mainnet",
 "rpcEndpoint": "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `queries` | array | `["gasPrice","blockNumber"]` | **Required.** Any of `gasPrice`, `blockNumber`, `getBalance`, `getBlock`. |
| `addresses` | array | `[]` | `0x` + 40 hex characters. **Required when `getBalance` is selected**, the run fails fast with a clear message otherwise. |
| `blockNumbers` | array | `["latest"]` | Decimal height, hex height, or one of `latest`, `finalized`, `safe`, `earliest`, `pending`. |
| `network` | string | `mainnet` | `mainnet`, `sepolia`, `base`, `arbitrum`. |
| `rpcEndpoint` | string | - | Optional. Full https URL of your own node; tried first, public nodes remain as fallbacks. |

**Which combinations make sense.** `blockNumbers` does double duty: it lists the blocks `getBlock` fetches, *and* it sets the point in history at which `getBalance` reads. So `getBalance` with `blockNumbers: ["latest","finalized"]` and two addresses gives four rows, a small matrix, so keep an eye on it. `gasPrice` and `blockNumber` ignore both `addresses` and `blockNumbers` and always return exactly one row each, at the chain head.

### Output example

`gasPrice` on mainnet:

```json
{
 "method": "eth_gasPrice",
 "network": "mainnet",
 "chainId": 1,
 "rpcEndpoint": "https://ethereum-rpc.publicnode.com",
 "blockNumber": 25695686,
 "gasPriceWei": "192636783",
 "gasPriceGwei": "0.192636783",
 "baseFeePerGasWei": "192536783",
 "baseFeePerGasGwei": "0.192536783",
 "nextBlockBaseFeePerGasWei": "194809698",
 "nextBlockBaseFeePerGasGwei": "0.194809698",
 "maxPriorityFeePerGasWei": "100000",
 "maxPriorityFeePerGasGwei": "0.0001",
 "scrapedAt": "2026-08-06T11:43:53.191Z"
}
```

`getBalance` for the beacon deposit contract, note the 26-digit wei value survives intact:

```json
{
 "method": "eth_getBalance",
 "network": "mainnet",
 "chainId": 1,
 "rpcEndpoint": "https://ethereum-rpc.publicnode.com",
 "address": "0x00000000219ab540356cbb839cbe05303d7705fa",
 "blockTag": "latest",
 "balanceWei": "89299316879836548086007430",
 "balanceEth": "89299316.87983654808600743",
 "currency": "ETH",
 "nonce": 1,
 "scrapedAt": "2026-08-06T11:43:53.289Z"
}
```

`getBlock`:

```json
{
 "method": "eth_getBlockByNumber",
 "network": "mainnet",
 "chainId": 1,
 "rpcEndpoint": "https://ethereum-rpc.publicnode.com",
 "blockTag": "latest",
 "blockNumber": 25695686,
 "blockHash": "0x5072bea4d9c5cb8907c67b9a465d0b3e06767e73d5727e3a562c7f6669412fb6",
 "parentHash": "0x80460ad541a0f9e93a7658e02534b4d2798672406ee818abc0bdf94345ebfa95",
 "timestamp": "2026-08-06T11:43:47.000Z",
 "timestampUnix": 1786016627,
 "transactionCount": 509,
 "miner": "0x396343362be2a4da1ce0c1c210945346fb82aa49",
 "gasUsed": "32833224",
 "gasLimit": "60000000",
 "baseFeePerGasWei": "192536783",
 "baseFeePerGasGwei": "0.192536783",
 "blockSizeBytes": 247593,
 "scrapedAt": "2026-08-06T11:43:53.346Z"
}
```

`RUN_SUMMARY`:

```json
{
 "network": "mainnet",
 "chainId": 1,
 "rpcEndpoint": "https://ethereum-rpc.publicnode.com",
 "endpointsUsed": ["https://ethereum-rpc.publicnode.com"],
 "endpointFailures": [
 { "endpoint": "https://cloudflare-eth.com", "error": "RPC error -32046: Cannot fulfill request" }
 ],
 "queriesRequested": ["gasPrice"],
 "queriesFailed": 0,
 "failures": [],
 "recordsSaved": 1,
 "filters": {
 "queries": ["gasPrice"],
 "addresses": [],
 "blockNumbers": ["latest"],
 "network": "mainnet",
 "rpcEndpoint": null
 },
 "finishedAt": "2026-08-06T11:44:59.062Z"
}
```

### Limits and behaviour

- **Hex is decoded with BigInt, never with `Number`.** A mainnet balance in wei routinely exceeds `Number.MAX_SAFE_INTEGER`, the beacon deposit contract's is a 26-digit integer, and parsing that as a float silently corrupts the low-order digits. Every wei value is a `BigInt` internally and leaves as a **decimal string**, so `balanceWei` and `gasPriceWei` are exact. `balanceEth` and the `*Gwei` fields are also strings, formatted by integer division rather than floating-point maths. Only small counters, block number, nonce, transaction count, timestamp, become JSON numbers, and even those are range-checked first. Parse them with a decimal library, not `parseFloat`.
- **Calls are batched, at a size the node will actually take.** Requests go out as JSON-RPC batch arrays, up to 25 per POST, and responses are re-aligned by request `id` because nodes are free to answer out of order. Four queries against two addresses and two blocks is a handful of HTTP requests, not a dozen. Some free public nodes cap batch arrays well below 25 and reject an oversized array wholesale rather than trimming it, so the ceiling is measured once per endpoint right after the health check and the run splits to fit it.
- **Endpoints fail over automatically, in two ways.** Before anything is read, candidates are probed for chain ID *and* head height, and the first healthy one wins. Then, if the chosen node later refuses an entire batch, the Actor moves to the next endpoint once and retries. Everything rejected on the way is listed in `RUN_SUMMARY.endpointFailures`, so you can see exactly which public node was down.
- **Public nodes rate-limit.** They are shared infrastructure. For heavy or high-frequency use, put your own Alchemy/Infura/QuickNode URL in `rpcEndpoint`. It is tried first and the public nodes stay as a safety net.
- **Historical state is often pruned.** Most public nodes keep only recent state, so `getBalance` at an old block may be rejected even though `getBlock` at the same height works fine. The failure is recorded per query in `RUN_SUMMARY.failures` and the rest of the run continues. Use an archive node via `rpcEndpoint` if you need deep history.
- **A chain-ID mismatch is a warning, not a stop.** If a custom `rpcEndpoint` reports a different chain than the `network` you picked, the run continues but logs a loud warning, a self-hosted node on a fork is a legitimate thing to want.
- **One failed query never aborts the run.** Per-query failures land in `RUN_SUMMARY.failures`; the Actor only errors out if no query produced a single row.
- **Read-only.** No transactions are signed or sent, no private keys exist anywhere in this Actor, and every method it calls is a read.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~ethereum-rpc-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "queries": ["gasPrice", "getBalance"],
 "addresses": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"],
 "network": "mainnet"
 }'
```

### JavaScript example

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/ethereum-rpc-scraper').call({
 queries: ['gasPrice', 'getBalance'],
 addresses: ['0x00000000219ab540356cBB839Cbe05303d7705Fa'],
 blockNumbers: ['finalized'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items) {
 if (row.method === 'eth_gasPrice') console.log(`gas: ${row.gasPriceGwei} gwei`);
 // BigInt, not Number, the wei value is far past float precision.
 if (row.method === 'eth_getBalance') console.log(`${row.address}: ${BigInt(row.balanceWei)} wei`);
}
```

### FAQ

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Do I need an Alchemy or Infura account?** No, the built-in public endpoints work without one. Supply your own URL in `rpcEndpoint` if you want higher limits or archive-node history.

**What happens if a node is unavailable?** It is skipped during the health probe, or failed over mid-run, and recorded in `RUN_SUMMARY.endpointFailures`. The run continues on the next endpoint. Only if every endpoint for the network is down does the run fail.

**Can I schedule it?** Yes, that is the main use. Gas monitoring and balance tracking are both designed for recurring runs.

**Why are balances strings instead of numbers?** Because they are exact. Wei values exceed JavaScript's safe integer range, and JSON numbers would round them. Convert with `BigInt(row.balanceWei)`.

**Why is `balanceEth` also a string?** Same reason. It is produced by integer division and formatted digit by digit, so it never loses precision. Feed it to a decimal library rather than `parseFloat` if the low digits matter.

**Can it read ERC-20 token balances or call contracts?** Not in this version. It covers native balances, gas, chain height and block headers. Token balances need `eth_call` with ABI encoding.

**Can it fetch transactions or receipts?** Not yet, `getBlock` returns the header plus a transaction count, not the transaction bodies.

**Which network does `balanceEth` apply to?** All four supported networks use ETH as their native currency, and the `currency` field states it explicitly on every balance row.

**Can I integrate it with something else?** Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.

# Actor input Schema

## `queries` (type: `array`):

Which reads to perform. gasPrice returns the current gas price, base fee and next-block base fee. blockNumber returns the chain head. getBalance needs 'addresses'. getBlock needs 'blockNumbers'. Each produces its own dataset rows.

## `addresses` (type: `array`):

Wallet or contract addresses for the getBalance query, as 0x plus 40 hex characters. Copy them from Etherscan or your wallet. Ignored unless getBalance is selected.

## `blockNumbers` (type: `array`):

Blocks to read for getBlock, and the point in history at which balances are read for getBalance. Accepts a decimal height (18000000), a hex height (0x112a880), or a tag: latest, finalized, safe, earliest, pending. Note that most public nodes prune state, so a historical balance may be rejected.

## `network` (type: `string`):

Which chain to read. Each has its own list of verified public endpoints and the Actor fails over between them automatically.

## `rpcEndpoint` (type: `string`):

Optional. A full https URL of your own JSON-RPC node. Alchemy, Infura, QuickNode or self-hosted. It is tried first and the built-in public endpoints stay as fallbacks. Leave empty to use the public nodes only.

## Actor input object example

```json
{
  "queries": [
    "gasPrice",
    "blockNumber",
    "getBalance"
  ],
  "addresses": [
    "0x00000000219ab540356cBB839Cbe05303d7705Fa",
    "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
  ],
  "blockNumbers": [
    "latest",
    "finalized",
    "18000000"
  ],
  "network": "mainnet",
  "rpcEndpoint": "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "gasPrice",
        "blockNumber",
        "getBalance"
    ],
    "addresses": [
        "0x00000000219ab540356cBB839Cbe05303d7705Fa"
    ],
    "blockNumbers": [
        "latest"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/ethereum-rpc-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": [
        "gasPrice",
        "blockNumber",
        "getBalance",
    ],
    "addresses": ["0x00000000219ab540356cBB839Cbe05303d7705Fa"],
    "blockNumbers": ["latest"],
}

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/ethereum-rpc-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "gasPrice",
    "blockNumber",
    "getBalance"
  ],
  "addresses": [
    "0x00000000219ab540356cBB839Cbe05303d7705Fa"
  ],
  "blockNumbers": [
    "latest"
  ]
}' |
apify call arman-bd/ethereum-rpc-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/ethereum-rpc-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XglHBeZPr84gyYGsn/builds/PwPiSCBB6k56y4emc/openapi.json
