Skip to main content

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

QuestionUse
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 havelabels_for_addresses
A risk score for an addressaddress_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​

InputDefaultWhat it does
chainrequiredethereum, tron, solana, bitcoin or zcash
addressesrequiredOne 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.
directionbothin counts only who paid the set, out only who the set paid, both counts each way
after_time / before_timelast 90 daysThe 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_txs1000Bitcoin 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_byaddressaddress returns one row per counterparty. label_type totals the flow by each counterparty's primary label type.
label_typeemptyKeep only counterparties with at least one label of these types, a comma-separated list such as cex-hot-wallet,cex-deposit-address
exclude_label_typeemptyDrop counterparties with any label of these types
tokenempty (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_usd0Return only counterparties whose USD received plus sent is at least this much. The rest still count in below_cutoff.
include_zero_value01 keeps zero-amount transfers. They are dropped by default because address poisoning plants look-alike addresses with 0-amount transfers.
include_internal0Ethereum only. 1 also counts value moved by contract code inside a transaction (internal transfers). Rejected on the other chains.
limit100How many counterparties to return, largest USD first, 1 to 1,000
cursoremptyPaging token from next_cursor. group_by: "address" only.
formatmarkdownmarkdown returns a summary and a counterparty table to read. json returns the full object described below.

How the chains differ​

EthereumTronSolanaBitcoinZcash
Address format0x + 40 hex charactersBase58, starts with TBase58, 32 to 44 characters, case-sensitive1..., 3... or bc1...Transparent t1... or t3...
Addresses per callUp to 50, as one setUp to 50, as one setUp to 50, as one set11
Windowafter_time / before_timeafter_time / before_timeafter_time / before_timeThe newest max_txs spends and receipts, inside after_time / before_timeThe newest max_txs spends and receipts, inside after_time / before_time
token filterToken contract addressTRC-20 contract or TRC-10 asset idNot availableNot availableNot available
include_internalAvailableRejectedRejectedRejectedRejected
LabelsLabels from every EVM chain the counterparty is known onTron labelsSolana labelsBitcoin labelsNo 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.

FieldMeaning
counterpartyThe other address
usd_in, usd_outUSD 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_totalusd_in plus usd_out. This is the ranking key, not a net balance: a pass-through counterparty counts on both sides.
transfers_in, transfers_outTransfer counts each way
unpriced_transfers_in, unpriced_transfers_outTransfers that were counted but could not be priced
first_seen, last_seenFirst and last transfer between them in the window
label, label_value, label_chainThe primary label type, its value (for example the exchange's name) and the chain it was found on
labelsEvery label type the counterparty carries
label_statuslabeled 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, tokensHow 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_membersWhen you pass more than one address: which of your addresses dealt with this counterparty

The summary around the rows:

FieldMeaning
windowThe resolved after_time and before_time
addresses_requested, addresses_used, addresses_invalidHow many addresses you passed, how many were used, and which were skipped
below_cutoffusd and transfers of every counterparty past limit or under min_usd. The returned rows plus this bucket account for the whole flow.
totalsusd_in, usd_out, usd_total, transfers_in, transfers_out, and the unpriced transfers split into no_price_transfers, suspect_transfers and stale_transfers
self_transfersTransfers between two of your own addresses. They are left out of the rows.
share_base_usd_in, share_base_usd_outThe USD base the shares are computed over, after the label filters
labeled_usd_share_in, labeled_usd_share_outShare of that USD that went to or came from labelled counterparties
pick_ruleThe label-type priority used to pick each label (see below)
partialtrue when the call stopped early and returned only what finished. Narrow the window or the set.
next_cursorPresent when more counterparties follow
cost, timing_msStatements 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:

FieldMeaning
label_typeThe label type. (none) collects counterparties with no usable label; (below_cutoff) collects the counterparties past limit.
counterpartiesHow many counterparties fall in the type
usd, shareUSD received plus sent, and its share of share_base_usd
share_in, share_outThe share of the inflow and of the outflow
top_counterpartiesThe 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:

  1. Risk: sanctioned-entity, terrorist, hacker, ransomware, darknet-market, mixer, money-laundering, tainted, scam, phishing, issuer-blocked-usdt, issuer-blocked-usdc, risk-flag.
  2. 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.
  3. 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.
  4. 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.

Build with Bitquery

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.