# EVM Wallet Scraper — Balances, Tokens, Transactions, Risk Flags (`yadroo/wallet-intel`) Actor

Wallet & contract intelligence for Ethereum, Base, Arbitrum, Optimism, Polygon, Gnosis, ZKsync, Scroll and more: balances, ERC-20 holdings with USD value (spam filtered), recent transactions with counterparties, token transfers, internal txs, NFTs, balance history, risk flags. ENS names accepted.

- **URL**: https://apify.com/yadroo/wallet-intel.md
- **Developed by:** [Samat Makatov](https://apify.com/yadroo) (community)
- **Categories:** Developer tools, AI, Automation
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 address analyzeds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Give it wallet or contract addresses (or ENS names) and get one clean JSON per address: native balance and USD value, ERC-20 holdings with prices (spam filtered), activity counters, recent transactions with direction and labelled counterparties, token transfers, internal transactions, NFTs, 90-day balance history and a deterministic list of risk flags. Works on **Ethereum, OP Mainnet, Gnosis, ZKsync, Scroll, Celo, Unichain, Soneium, Mode, Ink, World Chain** and two testnets via the public Blockscout API (Base, Arbitrum and Polygon are paused since 06.10.2026 — see **Limits & FAQ → Paused chains**). No API key, no proxy, no browser — and polite to the explorer: at most 2 requests in flight and 5 per second.

Built for AI agents and back-office automations that must answer "who is this address and what does it do?" in one call.

> **Need it tailored to your workflow?** Missing a field, a filter, another source or a different output format? [Open an issue](https://apify.com/yadroo/wallet-intel/issues) and describe what you need — we read every request and adapt the actor.

### Use cases

- **Counterparty due diligence** — before paying or accepting an invoice in crypto: balance, age, activity, explorer scam flag, exchange labels, proxy/EIP-7702 delegation.
- **KYT-lite monitoring** — start a daily run on your treasury or customer wallets (from your code, or a schedule whose input you update) with `sinceDate` = yesterday to get only new transactions and token transfers.
- **Payment reconciliation** — `includeTokenTransfers` + `tokenFilter` = USDC contract: "did wallet X receive ≥ 500 USDC since Monday?"
- **Portfolio snapshot** — `totalPortfolioUsd`, top tokens by value, spam removed, for a batch of wallets in one run (see **Limits & FAQ → Capacity** for how many fit).
- **Lead scoring / airdrop eligibility** — flag whales, fresh wallets, dormant wallets, NFT collections held.
- **Contract intel** — verified? proxy type, implementations, creator and creation tx, who calls it most (top counterparties).

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `addresses` | string\[] | — (required) | 0x addresses or ENS names (`vitalik.eth`). Letter case does not matter, `0X…` included. Each address is analysed and charged **once**: a repeat, an ENS name together with its 0x address, or the same address in another letter case is skipped and listed in `SUMMARY.duplicates`. Invalid values are listed in `SUMMARY.errors` (not charged). |
| `chain` | enum | `ethereum` | One key from **Reference → Chains** (`ethereum`, not `eth`): the platform refuses a value outside that list before the run starts. `base`, `arbitrum` and `polygon` are paused (**Limits & FAQ → Paused chains**). |
| `recentTxLimit` | int 0–200 | `10` | Recent **confirmed** native transactions per address (pages of 50). Mempool transactions are skipped and counted in `recentWindow.pendingTxs`. |
| `txDirection` | `all` / `from` / `to` | `all` | `from` = signed by the address (outgoing), `to` = incoming. |
| `sinceDate` | date | — | Keep only txs / token transfers / internal txs on or after this date (`YYYY-MM-DD` or ISO 8601, UTC). Paging stops early → cheaper runs. |
| `untilDate` | date | — | Upper bound (UTC); a bare date keeps that whole day. Lists are read newest first, so a date far in the past on a busy wallet may not be reached — see **Limits & FAQ**. |
| `includeTokens` | bool | `true` | ERC-20 balances with USD value, sorted by value. Auto-skipped for large contracts (`tokensSkipped:true`). |
| `tokensLimit` | int 1–500 | `50` | Max tokens kept per address. |
| `minTokenUsd` | number | `0` | Drop positions worth less than this. |
| `hideSpamTokens` | bool | `true` | Drop explorer-flagged scams and unpriced airdrop-lure tokens (URLs, "claim", "visit"…). Count in `spamTokenCount`. |
| `includeTokenTransfers` | bool | `false` | Recent ERC-20/721/1155 transfers with direction, counterparty, USD value, `likelySpam`. |
| `tokenTransfersLimit` | int 0–200 | `25` | |
| `tokenTransferType` | `all` / `ERC-20` / `ERC-721` / `ERC-1155` | `all` | |
| `tokenFilter` | string\[] | `[]` | Only transfers of these token contracts. |
| `includeInternalTransactions` | bool | `false` | Value-carrying internal calls (DEX payouts, bridge deliveries). |
| `internalTxLimit` | int 0–200 | `25` | |
| `includeNfts` | bool | `false` | NFT collections held (first 50) with counts and sample image. |
| `includeBalanceHistory` | bool | `false` | Daily native balance, last ~90 days (only days with changes). |
| `counterpartiesLimit` | int 0–50 | `10` | Size of `topCounterparties` (ranked by interactions within the fetched transactions). |
| `fields` | string\[] | `[]` | Keep only these top-level fields, in this order (`address`, `chain`, `found`, `fetchedAt` always kept). Letter case and snake_case do not matter; unknown names are left out with a note in the status and `SUMMARY.notes`; a list with no output field fails the run with the valid names. |
| `requestDelayMs` | int 0–5000 | `200` | Minimum gap between two explorer requests (at most 2 in flight). Values below 200 are raised to 200 — Blockscout allows 300 requests per minute per IP. |

All original inputs (`addresses`, `chain`, `recentTxLimit`, `includeTokens`) keep their meaning.

### Reference

#### Chains

| `chain` | Network | Chain ID | Native | Explorer |
|---|---|---|---|---|
| `ethereum` | Ethereum mainnet | 1 | ETH | eth.blockscout.com |
| `base` | Base (paused 06.10.2026) | 8453 | ETH | base.blockscout.com |
| `arbitrum` | Arbitrum One (paused 06.10.2026) | 42161 | ETH | arbitrum.blockscout.com |
| `optimism` | OP Mainnet | 10 | ETH | explorer.optimism.io |
| `polygon` | Polygon PoS (paused 06.10.2026) | 137 | POL | polygon.blockscout.com |
| `gnosis` | Gnosis Chain | 100 | xDAI | gnosis.blockscout.com |
| `zksync` | ZKsync Era | 324 | ETH | zksync.blockscout.com |
| `scroll` | Scroll | 534352 | ETH | scroll.blockscout.com |
| `celo` | Celo | 42220 | CELO | celo.blockscout.com |
| `unichain` | Unichain | 130 | ETH | unichain.blockscout.com |
| `soneium` | Soneium | 1868 | ETH | soneium.blockscout.com |
| `mode` | Mode | 34443 | ETH | explorer.mode.network |
| `ink` | Ink | 57073 | ETH | explorer.inkonchain.com |
| `worldchain` | World Chain | 480 | ETH | worldchain-mainnet.explorer.alchemy.com |
| `sepolia` | Ethereum Sepolia (testnet) | 11155111 | ETH | eth-sepolia.blockscout.com |
| `base-sepolia` | Base Sepolia (testnet) | 84532 | ETH | base-sepolia.blockscout.com |

Other Blockscout instances (e.g. Zora, Linea, Mantle) did not answer the public v2 API at the time of writing and are therefore not offered.

#### Flags (`flags[]`)

| Flag | Meaning |
|---|---|
| `not_found_on_chain` | Address unknown to the explorer on this chain. Such addresses are not dataset items (nothing to analyze, not charged) — they are listed in `SUMMARY.notFound`. |
| `flagged_scam_by_explorer` | Blockscout `is_scam` / non-ok reputation. |
| `unverified_contract` | Contract without verified source code. |
| `proxy_contract` | Upgradeable proxy (see `proxyType`, `implementations`). |
| `eoa_with_delegated_code_eip7702` | Wallet that delegated code via EIP-7702 (smart-account features). |
| `no_outgoing_transactions` / `fresh_wallet_under_5_txs` | Very young wallet. |
| `inactive_over_180d` / `dormant_over_1y` | No recent activity (wallets only). |
| `near_zero_balance` / `whale_over_1m_usd` | Portfolio size (native + tokens, USD). |
| `high_failed_tx_share` | ≥ 30 % of the fetched confirmed transactions failed (wallets only). |
| `labeled_exchange` | Explorer tag names a CEX. |
| `labeled_high_risk` | Tag mentions sanctions/OFAC/hack/exploit/phishing/drainer. |

Flags are a triage checklist, not a verdict.

#### Frequently used token contracts

| Token | Base | Ethereum |
|---|---|---|
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
| USDT | — | `0xdAC17F958D2ee523a2206206994597C13D831ec7` |
| WETH | `0x4200000000000000000000000000000000000006` | `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` |

### Examples

**1. Vet a counterparty before paying (Ethereum)**

```json
{ "addresses": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"], "chain": "ethereum", "recentTxLimit": 25, "includeTokens": true, "counterpartiesLimit": 10 }
```

**2. Daily monitor: what happened since yesterday on our treasury wallets (Ethereum)**

```json
{ "addresses": ["treasury.mycompany.eth", "0x…"], "chain": "ethereum", "recentTxLimit": 200, "sinceDate": "2026-09-12", "includeTokenTransfers": true, "tokenTransfersLimit": 200, "includeInternalTransactions": true, "includeTokens": false }
```

**3. Did the customer pay in USDC? (Ethereum)**

```json
{ "addresses": ["0xCustomerWallet…"], "chain": "ethereum", "recentTxLimit": 0, "includeTokens": false, "includeTokenTransfers": true, "tokenTransferType": "ERC-20", "tokenFilter": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], "sinceDate": "2026-09-08" }
```

**4. Portfolio snapshot for 100 wallets, only positions ≥ $50, compact output**

```json
{ "addresses": ["0x…", "0x…"], "chain": "optimism", "recentTxLimit": 0, "tokensLimit": 100, "minTokenUsd": 50, "fields": ["nativeBalance", "nativeUsd", "totalTokenUsd", "totalPortfolioUsd", "tokens", "flags"] }
```

**5. Contract intel: who calls this contract and is it upgradeable?**

```json
{ "addresses": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], "chain": "ethereum", "recentTxLimit": 100, "txDirection": "to", "includeTokens": false, "counterpartiesLimit": 25 }
```

### Output

One item per input address (trimmed real example, Base):

```json
{
  "query": "vitalik.eth",
  "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  "chain": "base", "chainId": 8453, "nativeSymbol": "ETH",
  "explorerUrl": "https://base.blockscout.com/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  "found": true, "degraded": false,
  "isContract": true, "isVerifiedContract": true, "proxyType": "eip7702",
  "implementations": [{ "address": "0x5A7FC11397E9a8AD41BF10bf13F22B0a63f96f6d", "name": "AmbireAccount7702" }],
  "ensName": "vitalik.eth", "tags": [], "isScam": false, "reputation": "ok",
  "nativeBalance": 3.1287, "nativeUsd": 7896.08, "nativePriceUsd": 2523.71,
  "txCount": 51, "txCountIsLowerBound": true, "tokenTransferCount": 51, "internalTxCount": 51,
  "tokenCount": 2575, "tokenCountShown": 50, "spamTokenCount": 89, "tokensSkipped": false, "tokenListPartial": false,
  "totalTokenUsd": 19145.92, "totalPortfolioUsd": 27042.0,
  "tokens": [{ "symbol": "TRUE", "name": "True", "contract": "0x21CF…", "decimals": 18, "balance": 1066680, "priceUsd": 0.0139, "usdValue": 14802.9, "holdersCount": 24836, "reputation": "ok" }],
  "lastSeen": "2026-08-10T13:00:47.000000Z",
  "recentWindow": { "txs": 10, "pendingTxs": 0, "direction": "all", "sentNative": 0.0, "receivedNative": 0.0, "feesNative": 0.00002, "failedTxs": 0, "uniqueCounterparties": 1 },
  "recentTransactions": [{ "hash": "0x1234…", "timestamp": "2026-08-10T13:00:47.000000Z", "direction": "out", "counterparty": "0x…", "counterpartyLabel": "PublicResolver", "valueNative": 0, "method": "setContenthash", "status": "ok", "feeNative": 0.0000277 }],
  "tokenTransfers": [], "internalTransactions": [], "nftCollections": [], "balanceHistory": [],
  "topCounterparties": [{ "address": "0x…", "label": "Deterministic Deployer", "txCount": 13, "sentNative": 0, "receivedNative": 0, "lastSeen": "2026-07-19T20:43:47.000000Z" }],
  "flags": ["eoa_with_delegated_code_eip7702"],
  "warnings": [],
  "source": "blockscout", "sourceUrl": "https://base.blockscout.com/api/v2/addresses/0xd8dA…", "fetchedAt": "2026-09-12T23:32:44.398Z"
}
```

| Field | Description |
|---|---|
| `query`, `address`, `ensName` | Input value, resolved checksum address, ENS primary name if any. One item per address and chain. |
| `chain`, `chainId`, `nativeSymbol`, `explorerUrl` | Chain key, EVM chain id, native coin symbol and the address page on the explorer. |
| `degraded` | `true` when the explorer's address endpoint was unavailable and the item was rebuilt from its other endpoints (see **Limits & FAQ → Explorer outages**). Facts that could not be read are `null`, and `warnings[]` says which. |
| `found` | Always `true` on dataset items. Unknown addresses, ENS names that resolve to nothing, invalid inputs, failed lookups and repeated addresses are not items (not charged): the `SUMMARY` record lists them under `notFound`, `errors` (with the reason) and `duplicates`, with their counts, and the status message sums it up. The run fails only if every address failed. |
| `isContract`, `isVerifiedContract`, `contractName`, `proxyType`, `implementations`, `creator`, `creationTxHash` | Contract facts. `proxyType: "eip7702"` = wallet with delegated code. |
| `tags`, `isScam`, `reputation` | Explorer labels (public tags, Kleros/OLI metadata) and scam status. |
| `nativeBalance`, `nativeUsd`, `nativePriceUsd` | Native coin balance and USD value. |
| `txCount`, `txCountIsLowerBound` | Transactions signed by the address. Blockscout computes counters lazily; when it returns 0 for an active address the actor reports `51` with `txCountIsLowerBound:true` ("50+"). |
| `tokenTransferCount`, `internalTxCount`, `gasUsed` | Activity counters. |
| `tokens[]`, `tokenCount`, `tokenCountShown`, `spamTokenCount`, `totalTokenUsd`, `totalPortfolioUsd` | ERC-20 holdings. `tokenListPartial:true` (and `tokenCountIsLowerBound:true`) when the full list timed out and the top-N-by-value fallback was used. |
| `firstSeen`, `lastSeen` | `lastSeen` = newest block time across the fetched confirmed transactions, token transfers and internal transactions; `firstSeen` comes from the transactions only and only when the whole history fits. |
| `recentWindow` | Summary of fetched transactions: counts, mempool transactions skipped (`pendingTxs`), native sent/received, fees, failures, unique counterparties. |
| `recentTransactions[]` | Confirmed transactions: `direction` in/out/self, `counterparty` + `counterpartyLabel`, value, method, status, fee, `createdContract`. |
| `tokenTransfers[]` | Token, amount, USD value, direction, counterparty, `likelySpam`. |
| `internalTransactions[]`, `nftCollections[]`, `nftCollectionCount`, `balanceHistory[]` | Optional extras (`nftCollectionCount` is null when `includeNfts` is off). |
| `topCounterparties[]` | Most frequent counterparties with labels and flows. |
| `flags[]` | See Reference. |
| `source`, `sourceUrl`, `fetchedAt` | `blockscout`, the explorer API record of the address, and when it was read (UTC). |
| `warnings[]` | Sub-requests that failed/timed out for this address, which fallback filled in for them (item is still delivered), and lists whose `sinceDate`/`untilDate` window the page cap did not reach. |

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~wallet-intel/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"addresses":["vitalik.eth"],"chain":"ethereum","recentTxLimit":25}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/wallet-intel').call({ addresses: ['vitalik.eth'], chain: 'ethereum' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(token)
run = client.actor("yadroo/wallet-intel").call(run_input={"addresses": ["vitalik.eth"], "chain": "ethereum"})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

MCP: add `https://mcp.apify.com` to Claude / Cursor / any MCP client and call the `yadroo/wallet-intel` tool with the same JSON input.

### Pricing

Pay per event: **$0.001 per run start + $0.005 per analyzed address**. Bronze −10 %, Silver −20 %, Gold and above −30 % on the address price; the start event is the same on every plan; platform usage is included. Only analyzed addresses are charged — invalid inputs, unknown addresses, failed lookups and repeated addresses are free and listed in `SUMMARY`. A typical due-diligence run on 10 addresses costs $0.051; a daily monitor over 100 wallets costs $0.501 per day. Batch addresses to amortize the start fee.

Your **maximum cost per run** is respected: the run stops before the address that would cross it and says so ("Stopped at your spending limit: N rows delivered"); addresses it did not reach are listed in `SUMMARY.notProcessedInputs`.

### Limits & FAQ

- **Rate limits** — Blockscout publishes 300 requests per minute per IP for its public API. The actor keeps at most 2 requests in flight per explorer and starts one at most every `requestDelayMs` (≥ 200 ms), however many lookups an address needs. An HTTP 429 pauses all requests to that explorer for its `Retry-After` (or an exponential backoff) and is never answered with extra fallback requests. If the explorer keeps answering 429 (6 in a row) or asks to wait more than 2 minutes, the run stops cleanly: addresses already saved stay saved, the rest are listed in `SUMMARY.notProcessedInputs`, and the status says "Stopped early: … rate limit" — start them again later. The actor runs without a proxy and does not rotate IPs.
- **Paused chains** — since 06.10.2026 the explorers of **Base, Arbitrum and Polygon** (`base.blockscout.com`, `arbitrum.blockscout.com`, `polygon.blockscout.com`) answer every API path with a browser challenge (HTTP 403, Cloudflare "Just a moment…") instead of JSON, from every server we tried (Apify's included). The actor does not try to get around it: a run on one of those chains stops on the first such answer and says so ("answers a bot challenge … pick another chain"), without sending more requests. The other 13 chains above are unaffected. Blockscout's own multichain API (`api.blockscout.com`) needs an API key, and no second Blockscout instance serves these three chains, so there is no other route for them right now; the chains stay in the list and start working again as soon as the explorer serves the API again.
- **Freshness** — live explorer data (seconds behind the chain). Prices are the explorer's (CoinGecko-backed), refreshed every few minutes.
- **Huge wallets/contracts** — full token lists of whales can time out; the actor falls back to a paginated, value-sorted list (`tokenListPartial:true`). Token balances are skipped for non-EOA contracts (`tokensSkipped:true`) because router/pool balance lists are enormous.
- **Pending transactions** — the explorer lists mempool transactions before the confirmed ones, and a busy wallet (an exchange hot wallet) can have dozens of them at any moment. They have no block time, so they are skipped: `recentTransactions` holds `recentTxLimit` confirmed transactions, and `recentWindow.pendingTxs` tells you how many mempool entries were passed over while collecting them. On the busiest contracts (USDT on Ethereum had 150 at once) the mempool entries can fill every page the limit reads; the item's `warnings` then say so instead of returning a silently empty list.
- **Counters** — Blockscout computes some counters lazily; see `txCountIsLowerBound`.
- **Date windows** — the explorer lists transactions newest first, and the actor reads at most `limit / 50 + 2` pages per list (4 pages for 100). With `untilDate` far in the past on a busy wallet (an exchange hot wallet makes hundreds of transactions an hour) those pages may all be newer than `untilDate`: the list is then empty and the item's `warnings` say the window was not reached — it does not mean "no activity". Move `untilDate` closer to today, or use `sinceDate` alone.
- **Capacity** — the default run timeout is 900 s, and the actor keeps the explorer's pace (≤ 5 requests per second, ≤ 2 in flight). Measured on 02.10.2026 on Ethereum: 150 addresses with the default settings took 6 min (2.4 s and about 5 requests per address; 2 s with `includeTokens: false`), so **about 300 addresses fit in one run**. Each extra list (token transfers, internal transactions, NFTs, balance history, or 200 transactions = 4 pages) adds requests, and a whale whose token list is slow can take up to a minute. Addresses that do not fit are listed in `SUMMARY.notProcessedInputs` — split larger batches across runs or raise the timeout.
- **Timeout** — a run near its timeout stops starting new addresses, keeps every address it saved and ends SUCCEEDED with "Stopped before the run timeout"; the addresses it did not reach are in `SUMMARY.notProcessedInputs`.
- **Explorer outages** — a single Blockscout instance can degrade on its own (on 28.09.2026 the Base instance answered `500`/timeout on `/addresses/{hash}` for hours while its other endpoints worked). The address is then not lost: the actor rebuilds the item from the search, stats, balance-history and v1 balance endpoints and marks it `degraded: true`. Such an item carries balance, USD value, contract/verification status, name, ENS and labels; transaction lists, token balances and counters can be empty, the balance can be up to a day old, and every gap is named in `warnings[]`. When nothing at all can be read, the address counts as failed (not an item, not charged).
- **Errors** — network/explorer failures on sub-requests never drop an address: the item is delivered with `warnings[]` (except while the explorer rate-limits the run — that address is then not saved and is listed with the rest, see **Rate limits**). Invalid inputs, addresses unknown to the explorer and lookups that fail entirely are not items and are not charged — they are listed in `SUMMARY` (`errors`, `notFound`) and the status message; the run fails only if every address failed. Invalid chains, dates or `fields` lists fail the run immediately with a message that says what to change (malformed dates are refused by Apify before the run starts).
- **Roadmap** — ERC-20 approval/allowance audit, per-token PnL, more Blockscout chains as they expose the v2 API.

***

Made by **Yadroo**. Siblings: [base-token-intel](https://apify.com/yadroo/base-token-intel) · [ens-resolver](https://apify.com/yadroo/ens-resolver) · [dexscreener-tokens](https://apify.com/yadroo/dexscreener-tokens) · [bitcoin-mempool](https://apify.com/yadroo/bitcoin-mempool) · [sanctions-screen](https://apify.com/yadroo/sanctions-screen) · [domain-intel](https://apify.com/yadroo/domain-intel)

# Actor input Schema

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

EVM addresses (0x…40 hex) or ENS names (vitalik.eth). Each address is analysed and charged once: repeats, an ENS name together with its 0x address, or the same address in another letter case are skipped and listed in SUMMARY.duplicates. Invalid values, unknown addresses and ENS names that resolve to nothing are not dataset items and are not charged; they are listed in SUMMARY (errors, notFound).

## `chain` (type: `string`):

Blockscout-hosted chain to query, one key from the list (README → Reference → Chains). The platform refuses any other value (for example eth or mainnet) before the run starts. Base, Arbitrum and Polygon are paused since 06.10.2026 — their explorer answers a browser challenge instead of data (README → Limits & FAQ).

## `recentTxLimit` (type: `integer`):

How many most recent transactions to include per address (0 = skip). Up to 200 (4 pages).

## `txDirection` (type: `string`):

`all` = both directions; `from` = only transactions signed by the address (outgoing); `to` = only incoming.

## `sinceDate` (type: `string`):

Keep only transactions / token transfers / internal txs on or after this date (YYYY-MM-DD or ISO 8601; UTC when no zone is given). Paging stops when older items are reached, which keeps runs cheap.

## `untilDate` (type: `string`):

Keep only items on or before this date (YYYY-MM-DD = the whole day, or ISO 8601; UTC when no zone is given). The explorer lists newest first and the actor reads at most limit / 50 + 2 pages, so on a busy wallet an untilDate far in the past can leave the window unreached; the item's `warnings` then say so.

## `includeTokens` (type: `boolean`):

Include ERC-20 token balances sorted by USD value.

## `tokensLimit` (type: `integer`):

Maximum number of tokens to keep per address (after spam/min-USD filtering). `tokenCount` still reports the full number held.

## `minTokenUsd` (type: `number`):

Drop token positions worth less than this (0 = keep everything, including unpriced tokens).

## `hideSpamTokens` (type: `boolean`):

Drop tokens the explorer flags as scams and unpriced tokens whose name/symbol looks like an airdrop lure (URLs, 'claim', 'visit', 'reward'…). Count of removed tokens is reported as `spamTokenCount`.

## `includeTokenTransfers` (type: `boolean`):

Include recent ERC-20/721/1155 transfers with direction, counterparty and USD value. Combine with `tokenFilter` to answer questions like 'did this wallet receive USDC in the last week?'.

## `tokenTransfersLimit` (type: `integer`):

Maximum token transfers per address (0–200).

## `tokenTransferType` (type: `string`):

Restrict token transfers to one standard.

## `tokenFilter` (type: `array`):

Token contract addresses (0x…) to keep in `tokenTransfers`. E.g. USDC on Base: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. Empty = all tokens.

## `includeInternalTransactions` (type: `boolean`):

Include value-carrying internal calls (contract → address ETH transfers, e.g. DEX payouts, bridge deliveries).

## `internalTxLimit` (type: `integer`):

Maximum internal transactions per address (0–200).

## `includeNfts` (type: `boolean`):

Include the list of ERC-721/1155 collections the address holds (first page, up to 50 collections) with counts and a sample image.

## `includeBalanceHistory` (type: `boolean`):

Include the daily native-coin balance series for the last ~90 days (only days where the balance changed).

## `counterpartiesLimit` (type: `integer`):

How many counterparties (ranked by number of interactions in the fetched transactions) to include in `topCounterparties`. 0 = skip.

## `fields` (type: `array`):

Keep only these top-level fields, in this order (address, chain, found and fetchedAt are always kept). Letter case and snake_case do not matter. Unknown names are left out with a note in the status message and SUMMARY.notes; a list without any output field fails the run with the list of valid names. Empty = full item. Example: \["nativeBalance","totalPortfolioUsd","flags"].

## `requestDelayMs` (type: `integer`):

Minimum gap between two requests to the explorer, however many lookups one address needs (at most 2 run at once). Values below 200 are raised to 200: Blockscout allows 300 requests per minute per IP. Raise it if a run stops on HTTP 429.

## Actor input object example

```json
{
  "addresses": [
    "vitalik.eth",
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
  ],
  "chain": "ethereum",
  "recentTxLimit": 10,
  "txDirection": "all",
  "includeTokens": true,
  "tokensLimit": 50,
  "minTokenUsd": 0,
  "hideSpamTokens": true,
  "includeTokenTransfers": false,
  "tokenTransfersLimit": 25,
  "tokenTransferType": "all",
  "tokenFilter": [],
  "includeInternalTransactions": false,
  "internalTxLimit": 25,
  "includeNfts": false,
  "includeBalanceHistory": false,
  "counterpartiesLimit": 10,
  "fields": [],
  "requestDelayMs": 200
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {
    "addresses": [
        "vitalik.eth",
        "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/wallet-intel").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 = { "addresses": [
        "vitalik.eth",
        "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("yadroo/wallet-intel").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 '{
  "addresses": [
    "vitalik.eth",
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
  ]
}' |
apify call yadroo/wallet-intel --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yadroo/wallet-intel"
        }
    }
}
```

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/baP6LDf6mUV6x0fd3/builds/kA4bNu8aTdBkcJ5QF/openapi.json
