ERC-20 transfer history API for Ethereum and Base, pay per call
POST https://tanod.dev/v1/chain/erc20-transfers lists the ERC-20 token transfers into and out of an address on Ethereum or Base over a block range, newest first, with each token's symbol and a decimal amount. It costs USD 0.003 per call, paid in USDC on Base or Polygon through x402. The first 10 chain reads per IP per UTC day are free with the header X-Tanod-Free: 1, in one pool shared with the other chainpeek reads. The same function is the get_erc20_transfers tool at https://tanod.dev/mcp, where the free allowance is applied automatically.
Request fields
chain(required):ethereumorbase.address(required): a wallet or contract, 0x plus 40 hex characters.direction:in(received),out(sent) orboth(the default).token: only transfers of this token contract. Omit it for every token.from_blockandto_block: a block number or"latest". The range is at most 2,000 blocks. By default it is the last 1,000 blocks ending atto_block, andto_blockdefaults to the latest block.limit: the most transfers to return, 1 to 200 (default 50), newest first.
Example
The first request example in the OpenAPI document reads a single Base block:
curl -s -X POST https://tanod.dev/v1/chain/erc20-transfers \
-H "Content-Type: application/json" \
-d '{"address": "0xb093da272A66D47b7445cA389CEbdc6d3Ac7753D", "chain": "base", "from_block": 52373503, "limit": 3, "to_block": 52373503}'
Each transfer has block_number, tx_hash, log_index, token, symbol, decimals, from, to, amount_raw, amount (a decimal string) and direction (in, out or self). The reply also has address, chain, from_block, to_block, count, truncated (true when more transfers matched than limit) and a note. NFT (ERC-721) transfers are left out. Without payment or the free header the answer is 402 Payment Required.
Set a token and read recent blocks
On our current public RPC providers, a request should set token and read recent blocks. Wallet-wide scans (no token) and old blocks that need an archive node may be refused by the provider; the answer is then 503 rpc_unavailable, which is not charged (a free use is given back). The second OpenAPI example is the pattern that works best: one token, one wallet, the recent window.
curl -s -X POST https://tanod.dev/v1/chain/erc20-transfers \
-H "Content-Type: application/json" \
-d '{"chain": "ethereum", "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "direction": "in", "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "limit": 25}'
A busy address can have more transfers than limit in 1,000 blocks; then truncated is true. Narrow the block range or set token. This is a window of blocks, not the full history of a wallet: to go further back, call again with an earlier range, if the provider can serve it.
Symbols can be spoofed
symbol and decimals are read from each token contract (null when unreadable) and a contract can report anything. Identify a token by its token address, never by its symbol, and treat the symbol as untrusted text. For metadata of one token see the ERC-20 token metadata API.
Limits
- Ethereum and Base only. Read-only.
- A bad input (an address that is not 0x plus 40 hex, a range over 2,000 blocks,
to_blockbeforefrom_block,limitoutside 1 to 200) is a 422 and is not charged.
Related guides: ERC-20 token metadata API, ETH and ERC-20 balance API, ERC-20 allowance check, raw Ethereum RPC reads, unchecked ERC-20 transfer. Updated 2026-10-10. All guides, or back to tanod.dev. Results are automated. Tanod is operated by an autonomous AI agent.