How to Read a Coinpath Graph
Components of the money flow graph
Coinpath® models a money flow as a directed acyclic multigraph (DAG multigraph).
- Nodes are entities. In a money flow they are addresses that send or receive tokens.
- Level is the causal order, or how many hops a node is from the initial address. A higher level means the transfer happened later and needed more hops to reach.
- Edges are relationships between nodes. In a money flow they are transfers between addresses.
- Multigraph means two nodes can be joined by several edges, because one address can pay another in several transactions.
- Edge labels carry the amount, the timestamp and other details of the transfer.

In the diagram:
- Address 1 is the originating address.
- It has two outbound transfers to Address 2 and one transfer to Address 3.
- Address 4 receives from Address 2 and Address 3, and sends to Address 2.
- Address 2 appears twice, on level 1 and on level 3.
Example from the Explorer
The Bitquery Explorer draws the same graph for an Ethereum address.

- Inbound and outbound depth are both set to 1, so only immediate USDT transfers appear.
- The address has three inbound transfers of 9.99972k, 21.9968k and 19.4366k USDT.
- There is one outbound transfer of 51.4306k USDT.
transaction versus transactions
The transaction and transactions fields group results differently. Run both examples to see the difference.
Group by transaction
query ($network: EthereumNetwork!, $address: String!, $inboundDepth: Int!, $limit: Int!, $currency: String!, $from: ISO8601DateTime, $till: ISO8601DateTime) {
ethereum(network: $network) {
inbound: coinpath(
initialAddress: {is: $address}
currency: {is: $currency}
depth: {lteq: $inboundDepth}
options: {direction: inbound, asc: "depth", desc: "amount", limitBy: {each: "depth", limit: $limit}}
date: {since: $from, till: $till}
) {
transaction {
hash
time {
time
}
value
}
sender {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
receiver {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
amount
currency {
symbol
}
depth
count
}
}
}
{
"inboundDepth": 1,
"outboundDepth": 1,
"limit": 10,
"offset": 0,
"network": "ethereum",
"address": "0xbd3afb0bb76683ecb4225f9dbc91f998713c3b01",
"currency": "ETH",
"from": "2023-09-20",
"till": "2023-09-27T23:59:59",
"dateFormat": "%Y-%m-%d"
}
The transaction {} field returns the full transaction object for each transfer, so the result has one row per transaction.
Group by sender and receiver
query ($network: EthereumNetwork!, $address: String!, $inboundDepth: Int!, $limit: Int!, $currency: String!, $from: ISO8601DateTime, $till: ISO8601DateTime) {
ethereum(network: $network) {
inbound: coinpath(
initialAddress: {is: $address}
currency: {is: $currency}
depth: {lteq: $inboundDepth}
options: {direction: inbound, asc: "depth", desc: "amount", limitBy: {each: "depth", limit: $limit}}
date: {since: $from, till: $till}
) {
transactions {
amount
height
timestamp
txHash
txValue
}
sender {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
receiver {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
amount
currency {
symbol
}
depth
count
}
}
}
{
"inboundDepth": 1,
"outboundDepth": 1,
"limit": 10,
"offset": 0,
"network": "ethereum",
"address": "0xbd3afb0bb76683ecb4225f9dbc91f998713c3b01",
"currency": "ETH",
"from": "2023-09-20",
"till": "2023-09-27T23:59:59",
"dateFormat": "%Y-%m-%d"
}
Here the rows are grouped by sender.address and receiver.address. Each row is one sender-receiver pair, and transactions {} lists the transactions that make up that pair.
Use transaction when you need every transaction. Use transactions when you need the most active senders and receivers or the flow between address pairs. This is the view the MoneyFlow chart uses for its edges.
initialAddress and finalAddress versus sender and receiver
initialAddress together with finalAddress traces a path across several hops. sender and receiver return direct transfers only.
Paths between two addresses
This query returns every outbound path, including loops, from 0xa910f92acdaf488fa6ef02174fb86208ad7722ba to 0x5265754ebfcfa800051df99ebaf6b4b41a5e0bb1 on Ethereum.
query ($network: EthereumNetwork!, $outboundDepth: Int!, $limit: Int!, $currency: String!) {
ethereum(network: $network) {
outbound: coinpath(
initialAddress: {is: "0xa910f92acdaf488fa6ef02174fb86208ad7722ba"}
finalAddress: {is: "0x5265754ebfcfa800051df99ebaf6b4b41a5e0bb1"}
currency: {is: $currency}
depth: {lteq: $outboundDepth}
options: {direction: outbound, asc: "depth", desc: "amount", limitBy: {each: "depth", limit: $limit}}
) {
sender {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
receiver {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
amount(in: USD)
currency {
symbol
name
}
depth
count
}
}
}
{
"inboundDepth": 3,
"outboundDepth": 3,
"limit": 100,
"offset": 0,
"network": "ethereum",
"currency": "ETH",
"dateFormat": "%Y-%m"
}
Direct transfers between two addresses
This query returns only the direct transfers from the first address to the second, whatever depth you set.
query ($network: EthereumNetwork!, $outboundDepth: Int!, $limit: Int!, $currency: String!) {
ethereum(network: $network) {
inbound: coinpath(
sender: {is: "0xa910f92acdaf488fa6ef02174fb86208ad7722ba"}
receiver: {is: "0x5265754ebfcfa800051df99ebaf6b4b41a5e0bb1"}
currency: {is: $currency}
depth: {lteq: $outboundDepth}
options: {direction: outbound, asc: "depth", desc: "amount", limitBy: {each: "depth", limit: $limit}}
) {
sender {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
receiver {
address
annotation
smartContract {
contractType
currency {
symbol
name
}
}
}
amount(in: USD)
currency {
symbol
name
}
depth
count
}
}
}
{
"inboundDepth": 3,
"outboundDepth": 3,
"limit": 10,
"offset": 0,
"network": "ethereum",
"currency": "ETH",
"dateFormat": "%Y-%m"
}
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.