Your script has worked for years. It calls the Etherscan API for a token balance, or the old BscScan or BaseScan endpoint, and prints a number. Then one day the same call with chainid=8453 or chainid=56 and a free key stops returning a balance. Nothing in your code changed. The plan behind your key did.
This post covers what changed, which parts of the API still work for free, and how to keep reading contract data on Base and BNB Chain without buying a plan you only need for one call.
TL;DR
Etherscan API V2 uses one key and a chainid parameter. Per Etherscan’s
supported-chains page, Base, BNB
Smart Chain, OP Mainnet and Avalanche C-Chain are “Paid tier only”, while
Ethereum and Polygon still have a free tier. If you only need contract reads,
such as token balances or view functions, a single evmquery request does the
job on Base and BNB Chain on a free plan. If you need transaction lists, logs
or gas data, Etherscan’s paid plan or a Blockscout explorer is the right tool.
What changed
Two separate changes landed on the same scripts.
First, Etherscan retired API V1. Per Etherscan’s V2 migration docs, V1 was deprecated on 2025-08-15. V2 replaces the per-chain hosts with a single endpoint and a chainid query parameter, so one key can address many chains. That is also why the old BscScan API stopped being a separate service: BNB Chain’s own migration guide describes the move from BscScan to Etherscan V2.
Second, the free tier no longer spans every chain. The supported-chains page marks Base, BNB Smart Chain, OP Mainnet and Avalanche C-Chain as “Paid tier only”, and lists Ethereum and Polygon with a free tier. Paid plans start at $49 per month, per Etherscan’s API pricing page, which lists the Free plan at 3 calls per second.
One detail is easy to miss. The same page notes that source code and ABI endpoints are available on all chains for every plan, including the free one. So module=contract&action=getabi and getsourcecode should still answer on Base and BNB Chain. It is the other modules that need a paid plan there.
None of this is unreasonable. Running an indexed explorer API across dozens of chains costs real money, and a paid tier is a normal answer. The question is only whether your use case needs the paid tier at all.
Split by what you actually call
Most scripts that hit the Etherscan API fall into one of three groups. They have different answers.
Contract reads: balances and view functions
If your script calls module=account&action=tokenbalance, module=proxy&action=eth_call, or fetches an ABI with module=contract&action=getabi so it can then call a view function, you are reading live contract state. That is not explorer data. It is a state read, and any tool that can call a contract can do it.
Here is the Etherscan V2 version for a USDC balance on Base:
curl "https://api.etherscan.io/v2/api\?chainid=8453\&module=account\&action=tokenbalance\&contractaddress=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\&address=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\&tag=latest\&apikey=$ETHERSCAN_API_KEY"On a free key, a chainid=8453 request like this no longer returns a balance. On a paid key it returns the raw integer balance as a string, which you then divide by 10^6 yourself.
The same read with evmquery is one POST to https://api.evmquery.com/api/v1/query with an x-api-key header:
const res = await fetch("https://api.evmquery.com/api/v1/query", { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": process.env.EVMQUERY_API_KEY!, }, body: JSON.stringify({ chain: "evm_base", schema: { contracts: { usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" }, context: { wallet: "sol_address" }, }, context: { wallet: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" }, expression: "formatUnits(usdc.balanceOf(wallet), usdc.decimals())", }),});const { result } = await res.json();Three differences matter in practice:
- No ABI step. evmquery resolves the contract’s ABI for you, including through proxies, so there is no
getabicall before the read and no ABI file to keep in your repo. If you want background on why that step exists at all, see smart contract ABI explained. - Decoded output.
formatUnitsanddecimals()run inside the expression, so you get a human-readable number instead of a raw integer to scale yourself. - Chain is one field. Use
evm_basefor Base andevm_bnb_mainnetfor BNB Chain. The expression does not change. Ethereum isevm_ethereumand Polygon isevm_polygon.
Any view function on the contract works the same way, which replaces the eth_call proxy module and its hand-encoded data field. Reading a token’s total supply on BNB Chain is a one-line expression, usdt.totalSupply(), with chain set to evm_bnb_mainnet.
For many wallets, put a list<sol_address> in the context and use map. The multi-wallet balance scanner guide walks through that, and the Python version uses the same request shape. To try a read before writing code, open the ERC-20 inspector or browse the other free tools. If you are building this into an app, the developers page covers the rest of the workflow.
Transaction lists, logs, verified source and gas
This is where evmquery is the wrong tool. It reads current contract state. It does not serve history. There is no “all transactions for this address”, no event log search, and no gas tracker.
If your script uses module=account&action=txlist, module=logs&action=getLogs, module=gastracker, or reads verified source at scale, you have three honest options:
- Pay for Etherscan. The Lite plan at $49 per month is the direct path, and your code keeps working. If history is the core of what you do, this is usually the right answer.
- Use a Blockscout explorer. Blockscout instances expose a REST API for transactions, token transfers and logs on many chains, including Base. Our Blockscout comparison covers where it fits and where it stops.
- Use an RPC provider’s log APIs. Providers such as Alchemy offer
eth_getLogsand indexed history on top of RPC. The Alchemy comparison lists what they do well.
Pick by what you query, not by which one is cheapest on paper. A script that pulls one address’s transfers once a day has different needs from a dashboard that scans logs on every block.
Polling to catch a change
A third reason people poll the Etherscan API is to notice that something changed: a balance dropped under a threshold, a token’s supply moved, an owner list was edited. That is a cron job that calls tokenbalance every few minutes and compares the result with the last one.
That is what evmquery Monitors are for. A monitor runs an expression on a schedule against live chain state and sends an email or a webhook when the condition holds. You write the expression once, for example “balance of this wallet is below this amount”, and there is no cron job, no stored previous value and no polling script to host.
On the Free plan you get 3 monitors with 15-minute checks. Paid plans raise those limits. If your alert needs to react to a specific transaction or event log, a monitor is not the answer, because it checks state rather than history.
Which free tier you actually need
A short decision list:
- You read balances, supply, prices or other view functions on Base or BNB Chain: evmquery covers it on the Free plan, which is rate limited at 120 units per minute and 700 units per hour.
- You fetch ABIs or verified source on Base or BNB Chain: Etherscan’s own page says those endpoints stay available on the free plan.
- You need Ethereum or Polygon explorer data: the Etherscan free tier still covers them.
- You need transaction history or logs on Base or BNB Chain: pay for Etherscan, or use Blockscout.
- You need to be told when state changes: use a monitor.
Mixing is normal. Plenty of projects keep one Etherscan key for history and move only the state reads elsewhere, which keeps the paid plan focused on the part that needs it.
Next steps
- Get a free evmquery key and run the Base request above
- Multi-wallet ERC-20 balance scanning from TypeScript for lists of wallets in one request
- Blockscout alternative for contract reads for the history side of the API
- Monitors to replace the polling script with a scheduled check
- Smart contract ABI explained for what the ABI step was doing


