Skip to main content
Glama

etherscan-actions

CI License: MIT

Ethereum-only collector and RPC alternative built entirely on etherscan.io. It provides a Python SDK, a JSON CLI (escan), and a stdio MCP server. No API key or browser is needed: requests go to the same public pages and read-only UI JSON handlers a browser uses, over a Chrome-impersonating TLS client (curl_cffi).

This is not an Etherscan API client. See endpoint coverage for how each documented API endpoint, including every PRO and PRO+ endpoint, maps to public data.

Install

Requires Python 3.11+.

uv sync
uv run escan --help

Or install it straight from GitHub:

pip install git+https://github.com/billywgd/etherscan-actions

Related MCP server: Ethereum RPC MPC Server

Dedicated operations

These return an ActionResult with operation, target, source, chain_id (always 1), status, complete, data, per-request provenance (sources), pagination, unavailable_fields, warnings and errors.

Operation (API counterpart)

SDK

CLI

MCP

First funder (fundedby)

funded_by(address)

funded-by

etherscan_funded_by

Address metadata (getaddresstag)

metadata(address)

metadata

etherscan_metadata

ERC20 holdings (addresstokenbalance)

erc20_holdings(address)

erc20-holdings

etherscan_erc20_holdings

Token holder list (tokenholderlist)

token_holders(token)

token-holders

etherscan_token_holder_list

Transaction history (txlist, txlistinternal, tokentx, tokennfttx)

tx_history(address, kind=...)

tx-history --kind ...

etherscan_tx_history

Transaction details (eth_getTransactionByHash + receipt)

tx_info(hash)

tx-info

etherscan_tx_info

uv run escan funded-by 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
uv run escan metadata 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97
uv run escan erc20-holdings 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 --all --max-pages 5
uv run escan token-holders 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 --page 3
uv run escan tx-history 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 --kind erc20 --all --max-pages 5
uv run escan tx-info 0x53f109a88a180457eb8f558e73c02636f9a3ddb8d017f886a242dd2c4dc1a8e9
  • Funded By reads the address page's first-funder annotation (funder address, its public label and the funding transaction). It then reads that transaction for block, timeStamp and an exact wei value. The value is used only if the transaction's sender and recipient match the annotation. A contract creator is never substituted for a funder.

  • Metadata returns the public nametag, ENS name and labels. It is always partial: private/internal PRO+ fields are listed in unavailable_fields and never invented.

  • ERC20 holdings excludes ETH and returns full-precision balance decimal strings plus untruncated names and symbols. Price and value are kept as displayed. balance_raw and decimals are not public and stay unavailable.

  • Token holders returns address, full-precision balance and public label (null when unlabelled). Etherscan shows at most 1,000 holders. The cap and the reported total are kept in pagination, and capped results are never marked complete.

  • Transaction history returns one record per row, using Etherscan API field names: blockNumber, timeStamp, hash, from, to, value, isError, functionName and so on. It adds public labels (fromLabel, toLabel), direction and errorReason. ETH values are exact wei, read from full-precision tooltips. kind selects normal, internal, erc20 or nft. Token amounts are exact decimal strings; raw units are not public in the listing. Etherscan shows only the latest 10,000 records (5,000 for token transfers). pagination reports reported_total and public_record_cap, and capped histories are never complete.

  • Transaction details returns status and failure reason, block, timestamp, and from/to with labels. Value, fee, gas price, base/max/priority fees and the burnt fee are all exact wei. It also returns gas limit/used, type, nonce, position, raw input, methodId and the decoded function signature. ERC-20 tokenTransfers come with exact amounts, and logs include raw topics/data plus decoded parameters. For contract deployments, to is empty and contractAddress is set.

Listings resume with --page / start_page from pagination.next_page. Use --all / all_pages=True with a bounded --max-pages to walk them.

Ethereum RPC from Etherscan

escan rpc answers standard Ethereum JSON-RPC methods using only Etherscan, with no node provider or API key. Results use the usual RPC shapes (hex quantities, lowercase addresses). Fields Etherscan doesn't publish are null, never invented.

uv run escan rpc eth_getTransactionReceipt 0x53f109a88a180457eb8f558e73c02636f9a3ddb8d017f886a242dd2c4dc1a8e9
uv run escan rpc eth_getBalance 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
uv run escan rpc eth_call '{"to":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","data":"0x18160ddd"}'
uv run escan rpc escan_fundedBy 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97

Method

Source

Notes

eth_blockNumber, eth_call (incl. state overrides), eth_getCode

Etherscan's node

The node that powers etherscan.io's Read Contract tab. Only recent state (last 128 blocks).

eth_getBalance, eth_getStorageAt

Etherscan's node

Exact: an eth_call whose state override runs BALANCE / SLOAD. Same 128-block window.

eth_getTransactionByHash, eth_getTransactionReceipt

Transaction page (+ block page for blockHash)

Full input, exact fees, logs with block-level logIndex. v/r/s, logsBloom, cumulativeGasUsed are null.

eth_getBlockByNumber / ByHash, eth_getBlockTransactionCountBy*

Block page

Header fields including hash, parent, state/withdrawals roots, miner, gas, base fee, blob gas and extra data. Tags latest, safe, finalized and earliest resolve through the node. transactions is null: Etherscan serves a Cloudflare challenge on block transaction lists.

eth_chainId, net_version, web3_clientVersion

constant

escan_txInfo, escan_txHistory, escan_fundedBy, escan_metadata, escan_erc20Holdings, escan_tokenHolders

pages

The sleuthing operations above, through the same interface. Options go in a second parameter, e.g. {"kind": "erc20", "all_pages": true}.

Live verification status (0.4.0): the node-backed methods and the escan_* methods were verified against etherscan.io. eth_getTransactionByHash, eth_getTransactionReceipt, eth_getBlockByNumber and eth_getBlockByHash are tested only against saved copies of real pages: Cloudflare was challenging the test machine's IP when they shipped. pytest -m live covers them.

Not available from Etherscan without a browser: eth_getLogs, full block transaction lists, eth_getTransactionCount, eth_estimateGas, traces, and state older than 128 blocks. Sending transactions is out of scope.

Bulk

Batches read NDJSON: full JSON-RPC requests, or plain values with a METHOD. Results stream as NDJSON in completion order, each with its request id. --resume appends to a results file and skips ids it already answered, so an interrupted run continues where it stopped.

uv run escan rpc eth_getTransactionReceipt --batch hashes.txt --resume receipts.ndjson
uv run escan rpc escan_txHistory --batch addresses.txt '{"kind":"erc20","all_pages":true,"max_pages":20}' --resume history.ndjson
uv run escan rpc --batch requests.ndjson --workers 8 > results.ndjson
with EtherscanClient() as client:
    receipt = client.rpc("eth_getTransactionReceipt", [tx_hash])
    for response in client.rpc_batch(
        {"id": h, "method": "eth_getTransactionByHash", "params": [h]} for h in hashes
    ):
        ...

Throughput

Measured from one IP on 2026-10-06:

  • Etherscan's node: about 18 requests/s, with no errors at any concurrency. The default node rate is 15/s (node_rps).

  • Pages: short bursts of 5 to 10 requests/s succeed. But about 140 page requests within a minute got the IP challenged site-wide, and the block outlasted 20 minutes. The default page rate is therefore 1 request/s (--interval). Raising it risks a long block.

Finalized transaction and block pages are cached permanently, so repeated lookups and transactions in the same block cost no requests. --no-block-hash skips the block-page lookup that fills blockHash, halving page requests for transactions and receipts. After a challenge, page requests pause for a cooldown that doubles from 60 s up to 15 min. Batches wait it out (up to --max-wait) rather than extending the block.

Page and section collection

The generic collectors keep every public field, table, link, inline chart dataset and ABI on a page, along with section-level status, pagination and completeness.

uv run escan discover address 0xd8da6bf26964af9d7eed9e03e53415d37aa96045   # advertised sections
uv run escan address 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 --sections transactions,internals,erc20 --all --max-pages 3
uv run escan token 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 --summary
uv run escan tx 0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060
uv run escan contract 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
uv run escan history address 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 transactions --page 2
uv run escan holders 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
uv run escan page /gastracker
uv run escan cache-clear

--summary prints coverage and counts instead of the full record. --output FILE atomically writes the full JSON to a new file. --html keeps the original HTML.

from etherscan_actions import EtherscanClient

with EtherscanClient() as client:
    funding = client.funded_by("0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97")
    print(funding.data)  # fundingAddress, fundingAddressLabel, fundingTxn, block, timeStamp, value

    holders = client.token_holders(
        "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", all_pages=True, max_pages=4
    )
    print(len(holders.data), holders.pagination)

    token = client.token("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", sections=["holders"])
    print(token.summary())

MCP server

uv run etherscan-actions-mcp

Tools: etherscan_funded_by, etherscan_metadata, etherscan_erc20_holdings, etherscan_token_holder_list, etherscan_tx_history, etherscan_tx_info, etherscan_rpc, etherscan_address, etherscan_token, etherscan_transaction, etherscan_contract, etherscan_holders, etherscan_history, etherscan_discover, etherscan_page, etherscan_read_artifact.

If a response exceeds ETHERSCAN_ACTIONS_MCP_MAX_CHARS, the full result is written to a JSON artifact. The tool then returns its path, its SHA-256 and a coverage summary. Read the artifact in chunks with etherscan_read_artifact.

Configuration

SDK constructor options: profile, interval, timeout, proxy, cache, cache_dir, cache_ttl, node_rps. The CLI has matching flags. The CLI and MCP server also read:

Variable

Default

Purpose

ETHERSCAN_ACTIONS_PROFILE

chrome

curl_cffi impersonation profile

ETHERSCAN_ACTIONS_INTERVAL

1

Minimum seconds between requests

ETHERSCAN_ACTIONS_TIMEOUT

30

Request timeout in seconds

ETHERSCAN_ACTIONS_PROXY

none

Explicit HTTP proxy URL

ETHERSCAN_ACTIONS_CACHE_DIR

$XDG_CACHE_HOME/etherscan-actions

Page cache and MCP artifacts

ETHERSCAN_ACTIONS_MCP_MAX_CHARS

100000

Inline MCP response budget

Behaviour and guarantees

  • Read-only. Page requests are rate-limited, restricted to https://etherscan.io (other origins and off-origin redirects are rejected), and retried with bounded backoff on 429/5xx, honouring Retry-After. Node requests go only to node1.web3api.com (the node etherscan.io's own frontend uses), only for its three read methods, with an etherscan.io Origin as the frontend sends.

  • Successful responses are cached in SQLite (default TTL 300 s; --refresh bypasses it). Finalized transaction and block pages never expire. Challenges, error documents, login walls and not-found pages are never cached.

  • Failures are explicit: a Cloudflare challenge is blocked, a login wall is login_required, and a missing record is not_found. None of these is reported as empty data. complete covers only the requested public scope, so check it, along with section status and notices, before treating a listing as a full history.

Verification

uv run pytest -q          # unit + local-HTTPS end-to-end (SDK, CLI, stdio MCP)
uv run pytest -q -m live  # optional requests to etherscan.io
uv run ruff check .
uv build

Live results from 2026-10-06 are in the coverage doc.

Disclaimer

This is an independent project. It is not affiliated with or endorsed by Etherscan. It reads publicly visible pages at a deliberately slow default rate. You are responsible for following Etherscan's terms of service and for keeping your usage reasonable. If you need guaranteed, high-volume or commercial access, use the official Etherscan API.

Contributing and license

Contributions are welcome; see CONTRIBUTING.md. Report security issues as described in SECURITY.md. Released under the MIT License.

Related MCP Connectors

Related MCP Servers