Data access · 9 min read
Following Bitcoin money through UTXOs: one declarative query
Bitcoin does not have accounts or contracts. Value lives in unspent outputs, and it moves when a transaction spends some outputs as inputs and creates new ones. Tracing where money went is therefore a matter of following outputs into the inputs of later transactions. SQD's Portal serves Bitcoin inputs and outputs as first-class data, in the same declarative request shape it uses for EVM logs and Solana instructions, so you do this without running a full node or writing a parser. The scope is flow evidence, who paid whom, not reconstructed address balances. Every transaction below is real, from block 954,236.
1. Flow evidence, not balances
One boundary up front, because it is the honest thing to state and it shapes everything below. This data tells you about movement: which address sent value into a transaction and which addresses it went to. It does not give you an address's current balance. A balance would mean summing every unspent output an address controls across all of history, which is a different and heavier reconstruction. What follows traces flows, not holdings.
That is still most of what fund tracing needs. Following value from one address to the next, transaction by transaction, is the core of Bitcoin forensics, and it is exactly what inputs and outputs give you. For the investigation workflow that chains these hops together, see compliance data for crypto.
2. Inputs and outputs in one shape
The request is the same declarative shape as every other chain, with Bitcoin's tables instead of logs: a window, the inputs and outputs tables, and a field selection. Bitcoin uses Bitcoin Core's own field names, so an input's sender is prevoutScriptPubKeyAddress (the address of the output being spent) and an output's recipient is scriptPubKeyAddress. This is the actual request, as a curl you can paste:
The response is newline-delimited JSON, one block per line, with the matching inputs and outputs attached. A single real output from that stream:
Each input carries prevoutScriptPubKeyAddress and prevoutValue (the address and amount of the output it spends); each output carries scriptPubKeyAddress, value in BTC, and scriptPubKeyType. Because a block returns inputs and outputs as flat arrays, every row also carries a transactionIndex: group inputs and outputs by it, and join to the txid in the transactions table, to rebuild each transaction's senders and recipients. The one input without these is the coinbase (transactionIndex 0): it mints new value rather than spending a prior output, so its prevoutScriptPubKeyAddress and prevoutValue come back null, and flow logic should skip that row. The Portal's Bitcoin MCP tools normalize these to sender, recipient, and type for an agent, and the Pipes SDK has a Bitcoin source that streams the identical query into your own database for a standing flow table.
3. Reading a payment and its change
A single real transaction shows the basic flow. One input funds two outputs: a payment to one address, and change back to the sending address. The change is recognizable because it returns to the exact address that funded the input.
4. Consolidation: many inputs, one output
The payment shape above is one of a handful that recur in flow analysis, and they all fall straight out of reading inputs against outputs. A consolidation spends several outputs controlled by one address into a single output, the pattern a wallet or exchange uses to sweep small UTXOs together. A real one from the same block, transaction 81acc1c4…a66635:
- 3 inputs, one address0.02813590 BTC
- 1 output, pubkeyhash (legacy)0.02812774 BTC
- Fee0.00000816 BTC
Three inputs, all from bc1q4f5u…xfpe5l, collapse into one legacy output, with 0.00000816 BTC left as fee. Reading the same inputs and outputs the other way, several outputs from one input, is a fan-out; the most informative non-payment shape, an output that carries data instead of value, is the subject of the next section.
5. Output types, including OP_RETURN
Every output carries a script type, which tells you the address format and, in one case, that the output carries data rather than value. Block 954,236 alone contains legacy, P2SH, SegWit, and Taproot outputs:
- pubkeyhash 1... Legacy P2PKH
- scripthash 3... P2SH
- witness_v0_keyhash bc1q... SegWit v0
- witness_v1_taproot bc1p... Taproot
- nulldata OP_RETURN Data, value 0
nulldata is the OP_RETURN case: a zero-value output that embeds data, used as a protocol marker, by Runes and similar metadata schemes, rather than to move coins.
A real OP_RETURN-bearing transaction in the same block makes it concrete. Transaction ee4f2a5b…06099f pays one Taproot output and attaches a single zero-value data marker, exactly as the stream returns them:
- witness_v1_taproot0.08811049 BTC
- nulldata (OP_RETURN)0.00000000 BTC
The nulldata output moves no coins; its scriptPubKeyAddress comes back null and its value is zero. It is a marker a protocol writes onchain, and reading it is just another output type in the same query, which is what makes this dataset as useful for protocol activity as for plain payments.
6. Why this is harder elsewhere
Most indexing frameworks and onchain MCP servers are built around the EVM account-and-log model, or around a single chain's curated endpoint. Bitcoin's UTXO model does not fit that shape, so the usual way to get raw inputs and outputs is to run a full node and write your own parser.
The harder thing is exposing Bitcoin's inputs and outputs as a declarative dataset in the same request shape as EVM and Solana, so one query model spans an account chain, an instruction chain, and a UTXO chain. That cross-model uniformity is the part that takes work, and it is what lets a single agent or pipeline follow value across all three.
For the EVM equivalent of following value inside a transaction, see internal transactions explained. For the one-query-shape idea across machines, see one query shape, every VM, and for how landing many chains together works, multi-chain indexing.
Frequently asked questions
How do you trace Bitcoin fund flows without running a node?
Does this give me an address balance?
What is the difference between an input and an output in a Bitcoin transaction?
How do you tell the change output from the payment?
What is an OP_RETURN output?
Related guides
Tracing funds or building payments tooling?
See how flow data feeds monitoring and payments on the compliance and wallets and payments pages.