Counterparties of a Wallet
The counterparties tool on the Bitquery MCP server lists everyone an address paid and was paid by, in one call. Every row carries the USD value each way, the transfer counts, the first and last time the two transacted, and the counterparty's labels. It can also total the whole flow by label type, for example "62% of the inflow came from exchanges, 3% from sanctioned addresses".
One tool serves five chains. You pick the chain with the chain argument: ethereum, tron, solana, bitcoin or zcash (transparent addresses only).
| Question | Use |
|---|---|
| Who did this wallet, or this set of wallets, pay and get paid by, in USD and with labels? | counterparties |
| What share of the inflow came from exchanges, bridges or risk entities? | counterparties with group_by: "label_type" |
| A quick look at one address's top flows | <chain>_address_flow_summary |
| The labels of addresses you already have | labels_for_addresses |
| A risk score for an address | address_risk_report |
Ask the agent
"List the counterparties of the CoW Protocol settlement contract 0x9008d19f58aabd9ed0d60971565aa8510560ab41 on Ethereum since yesterday, money in only, above $100,000."
"For this Tron wallet, what share of the USD it received in 2026 came from exchanges? Use counterparties with group_by label_type."
"Here are 20 Ethereum addresses that belong to one fund. Treat them as one set and list who they paid in USDT this quarter."
Inputs
| Input | Default | What it does |
|---|---|---|
chain | required | ethereum, tron, solana, bitcoin or zcash |
addresses | required | One address, or a set of up to 50 separated by spaces, commas or |. The set is treated as one wallet. Bitcoin and Zcash take one address per call. |
direction | both | in counts only who paid the set, out only who the set paid, both counts each way |
after_time / before_time | last 90 days | The window. Accepts YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 such as 2026-09-20T00:00:00Z, or 10-digit unix seconds. after_time is inclusive, before_time is exclusive. The answer echoes the resolved window. |
max_txs | 1000 | Bitcoin and Zcash only. How many of the address's newest spends and newest receipts to read, 1 to 2,000 per side. Raise it to look further back. |
group_by | address | address returns one row per counterparty. label_type totals the flow by each counterparty's primary label type. |
label_type | empty | Keep only counterparties with at least one label of these types, a comma-separated list such as cex-hot-wallet,cex-deposit-address |
exclude_label_type | empty | Drop counterparties with any label of these types |
token | empty (every token) | Restrict to one token: a contract address on Ethereum; a TRC-20 contract or a TRC-10 numeric asset id on Tron. Rejected on Solana, Bitcoin and Zcash. |
min_usd | 0 | Return only counterparties whose USD received plus sent is at least this much. The rest still count in below_cutoff. |
include_zero_value | 0 | 1 keeps zero-amount transfers. They are dropped by default because address poisoning plants look-alike addresses with 0-amount transfers. |
include_internal | 0 | Ethereum only. 1 also counts value moved by contract code inside a transaction (internal transfers). Rejected on the other chains. |
limit | 100 | How many counterparties to return, largest USD first, 1 to 1,000 |
cursor | empty | Paging token from next_cursor. group_by: "address" only. |
format | markdown | markdown returns a summary and a counterparty table to read. json returns the full object described below. |
How the chains differ
| Ethereum | Tron | Solana | Bitcoin | Zcash | |
|---|---|---|---|---|---|
| Address format | 0x + 40 hex characters | Base58, starts with T | Base58, 32 to 44 characters, case-sensitive | 1..., 3... or bc1... | Transparent t1... or t3... |
| Addresses per call | Up to 50, as one set | Up to 50, as one set | Up to 50, as one set | 1 | 1 |
| Window | after_time / before_time | after_time / before_time | after_time / before_time | The newest max_txs spends and receipts, inside after_time / before_time | The newest max_txs spends and receipts, inside after_time / before_time |
token filter | Token contract address | TRC-20 contract or TRC-10 asset id | Not available | Not available | Not available |
include_internal | Available | Rejected | Rejected | Rejected | Rejected |
| Labels | Labels from every EVM chain the counterparty is known on | Tron labels | Solana labels | Bitcoin labels | No address labels, so every counterparty falls in (none) |
An address that is not valid for the chain is skipped and listed in addresses_invalid; the call still runs for the others. Compare addresses_requested with addresses_used.
On Bitcoin and Zcash the counterparties come from the address's newest max_txs transactions on each side, so the window the answer echoes can start at the chain's first block. A busy address may have more history than max_txs reaches: raise it, up to 2,000.
Output: one row per counterparty
With group_by: "address" (the default), counterparties sort by usd_total (USD received plus sent), largest first.
| Field | Meaning |
|---|---|
counterparty | The other address |
usd_in, usd_out | USD the set received from it and sent to it, each transfer valued at its own time. null (never 0) when no transfer on that side could be priced. |
usd_total | usd_in plus usd_out. This is the ranking key, not a net balance: a pass-through counterparty counts on both sides. |
transfers_in, transfers_out | Transfer counts each way |
unpriced_transfers_in, unpriced_transfers_out | Transfers that were counted but could not be priced |
first_seen, last_seen | First and last transfer between them in the window |
label, label_value, label_chain | The primary label type, its value (for example the exchange's name) and the chain it was found on |
labels | Every label type the counterparty carries |
label_status | labeled when a primary label was picked; unlabeled when the counterparty carries only descriptive types such as high-transacting or signer; none when it has no labels |
token_count, tokens | How many tokens moved, and a per-token breakdown: token, amount_in, amount_out (in token units, as decimal strings), usd_in, usd_out, transfers_in, transfers_out |
set_members | When you pass more than one address: which of your addresses dealt with this counterparty |
The summary around the rows:
| Field | Meaning |
|---|---|
window | The resolved after_time and before_time |
addresses_requested, addresses_used, addresses_invalid | How many addresses you passed, how many were used, and which were skipped |
below_cutoff | usd and transfers of every counterparty past limit or under min_usd. The returned rows plus this bucket account for the whole flow. |
totals | usd_in, usd_out, usd_total, transfers_in, transfers_out, and the unpriced transfers split into no_price_transfers, suspect_transfers and stale_transfers |
self_transfers | Transfers between two of your own addresses. They are left out of the rows. |
share_base_usd_in, share_base_usd_out | The USD base the shares are computed over, after the label filters |
labeled_usd_share_in, labeled_usd_share_out | Share of that USD that went to or came from labelled counterparties |
pick_rule | The label-type priority used to pick each label (see below) |
partial | true when the call stopped early and returned only what finished. Narrow the window or the set. |
next_cursor | Present when more counterparties follow |
cost, timing_ms | Statements run, rows and bytes read, and time taken |
format: "markdown" returns the same summary and a shorter table: counterparty, label, label status, USD in and out, transfer counts, unpriced counts, token count, first and last seen. Ask for format: "json" when you need usd_total, the full labels list, tokens, set_members or cost.
Output: totals by label type
With group_by: "label_type" each counterparty counts once, under its primary label type, so the shares add up to 100%. Each row of types carries:
| Field | Meaning |
|---|---|
label_type | The label type. (none) collects counterparties with no usable label; (below_cutoff) collects the counterparties past limit. |
counterparties | How many counterparties fall in the type |
usd, share | USD received plus sent, and its share of share_base_usd |
share_in, share_out | The share of the inflow and of the outflow |
top_counterparties | The largest counterparties of the type, with their USD |
The summary carries share_base_usd, share_base_usd_in, share_base_usd_out, self_transfers, pick_rule, partial and cost. With label_type or exclude_label_type set, the tool filters first and then groups, so the shares are over the filtered total.
Which label is picked
A counterparty often has many labels. The primary label is the first type it has in this order:
- Risk:
sanctioned-entity,terrorist,hacker,ransomware,darknet-market,mixer,money-laundering,tainted,scam,phishing,issuer-blocked-usdt,issuer-blocked-usdc,risk-flag. - Exchanges and services:
cex-hot-wallet,cex-cold-wallet,cex-deposit-address,cex-withdrawal-address,cex-unknown-type,otc-desk,payment-processor,gambling,custodian,e-commerce,no-kyc,mining-pool,bridge. - DeFi and contracts:
dex-router,dex-aggregator,dex-pool,lending-protocol,staking-protocol,derivatives-protocol,yield-aggregator,asset-manager,market-maker,service,infrastructure,token-contract,contract. - Every other type, most common first.
Within one type the most trusted source wins, then the most sources, then the lowest value. These types describe an address but not who owns it, so they are never picked: account-abstraction-user, deployer, dormant, first-funder, governance, high-transacting, multisig, platform-user, proxy, safe-wallet, signer, tron-energy-delegate, username. The exact list in force comes back in pick_rule on every call.
USD values
- Each transfer is valued at its own time and the values are summed.
- A transfer in a token with no price is counted in the transfer totals and in
unpriced_transfers_*, and adds nothing to USD. Spam and look-alike tokens usually have no price, so a huge amount of one cannot push its sender to the top of the list. - A side with no priced transfer is
null, not 0. Shares are over priced USD only.
Paging
Pass next_cursor as cursor on the next call with the same chain, addresses, window and filters. Keep going until no next_cursor comes back. Each counterparty appears on one page only, so the pages joined together are the full list. Take the overall totals from the first page: on a later page, totals covers only the counterparties from that page on, which is the previous page's below_cutoff. Set after_time and before_time to fixed times when you page, so every page reads the same window.
Related
- Tracing on the MCP for multi-hop fund tracing and investigation prompts.
- Holders and balances for holder exports and wallet balance checks.
Ready to run this in production?
Get an API key and run these queries in minutes, or talk to us about plans and enterprise delivery.