Menu
Bitcoin Intermediate 12 min read

Bitcoin Transactions

Complete guide to Bitcoin transaction structure for agents. Inputs, outputs, fees, signing, and broadcast.

transactions utxo signing

Bitcoin Transactions

A Bitcoin transaction is a signed data structure that transfers value from inputs (existing UTXOs) to outputs (new UTXOs). Understanding transaction structure is essential for agents that need to verify, create, or analyze payments.

Transaction Structure

┌─────────────────────────────────────────────────────────┐
│ Version (4 bytes)                                       │
├─────────────────────────────────────────────────────────┤
│ Input Count (varint)                                    │
├─────────────────────────────────────────────────────────┤
│ Inputs[]                                                │
│   ├── Previous TXID (32 bytes)                          │
│   ├── Previous Output Index (4 bytes)                   │
│   ├── ScriptSig Length (varint)                         │
│   ├── ScriptSig (variable)                              │
│   └── Sequence (4 bytes)                                │
├─────────────────────────────────────────────────────────┤
│ Output Count (varint)                                   │
├─────────────────────────────────────────────────────────┤
│ Outputs[]                                               │
│   ├── Value (8 bytes, satoshis)                         │
│   ├── ScriptPubKey Length (varint)                      │
│   └── ScriptPubKey (variable)                           │
├─────────────────────────────────────────────────────────┤
│ Locktime (4 bytes)                                      │
└─────────────────────────────────────────────────────────┘

JSON Representation

{
  "txid": "def456789...",
  "version": 2,
  "locktime": 0,
  "vin": [
    {
      "txid": "abc123...",
      "vout": 0,
      "prevout": {
        "value": 100000,
        "scriptpubkey": "0014...",
        "scriptpubkey_address": "bc1q..."
      },
      "scriptsig": "",
      "witness": ["304402...", "02a1b2..."],
      "sequence": 4294967293
    }
  ],
  "vout": [
    {
      "value": 50000,
      "scriptpubkey": "0014...",
      "scriptpubkey_address": "bc1q..."
    },
    {
      "value": 49000,
      "scriptpubkey": "0014...",
      "scriptpubkey_address": "bc1q..."
    }
  ],
  "size": 222,
  "vsize": 141,
  "weight": 561,
  "fee": 1000
}

Inputs (vin)

Each input references a previous unspent output and provides proof of authorization to spend it.

Input Fields

FieldSizeDescription
txid32 bytesTransaction containing the UTXO
vout4 bytesOutput index within that transaction
scriptSigvariableUnlock script (legacy)
witnessvariableWitness data (SegWit)
sequence4 bytesEnable RBF, relative timelocks

Sequence Values

ValueMeaning
0xffffffffFinal, no RBF
0xfffffffeFinal, locktime enabled
< 0xfffffffeRBF enabled (replaceable)

Outputs (vout)

Each output specifies an amount and spending conditions.

Output Fields

FieldSizeDescription
value8 bytesAmount in satoshis
scriptPubKeyvariableLock script (spending conditions)

Common ScriptPubKey Types

P2PKH:  OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
P2SH:   OP_HASH160 <scriptHash> OP_EQUAL
P2WPKH: OP_0 <pubKeyHash>
P2TR:   OP_1 <tweakedPubKey>

Fee Calculation

Fees are implicit—the difference between input and output values:

fee = sum(input_values) - sum(output_values)

Example

Inputs:  100,000 sats
Outputs: 50,000 + 49,000 = 99,000 sats
Fee:     100,000 - 99,000 = 1,000 sats

Fee Rate

Fees are priced per virtual byte (vB):

fee_rate = fee / vsize (sat/vB)

Current fee estimates via API:

curl https://mempool.space/api/v1/fees/recommended
{
  "fastestFee": 25,
  "halfHourFee": 20,
  "hourFee": 15,
  "economyFee": 10,
  "minimumFee": 5
}

Transaction Sizes

Virtual Size (vsize)

SegWit introduced “virtual bytes” for fee calculation:

vsize = (weight + 3) / 4
weight = base_size * 3 + total_size

Size Estimates by Type

Transaction Typevsize (approx)
P2WPKH → P2WPKH (1-in, 2-out)141 vB
P2TR → P2TR (1-in, 2-out)111 vB
P2PKH → P2PKH (1-in, 2-out)226 vB

Transaction States

Created → Signed → Broadcast → Mempool → Confirmed

Verification Checklist

  1. Unconfirmed (0 conf): In mempool, not in a block
  2. 1 confirmation: Included in most recent block
  3. 6 confirmations: Generally considered final
  4. Deep confirmation: 100+ blocks (coinbase maturity)

Query Transaction Status

# Get transaction details
curl https://mempool.space/api/tx/{txid}

# Check confirmation status
curl https://mempool.space/api/tx/{txid}/status
{
  "confirmed": true,
  "block_height": 830000,
  "block_hash": "000000000...",
  "block_time": 1706745600
}

Creating Transactions

Step-by-Step Process

  1. Select UTXOs to spend (inputs)
  2. Calculate total input value
  3. Define outputs (recipient + change)
  4. Estimate fee based on size and fee rate
  5. Set change output = inputs - payment - fee
  6. Sign inputs with private keys
  7. Broadcast to network

Pseudocode

def create_transaction(utxos, recipient, amount, fee_rate):
    # Select inputs
    selected = select_utxos(utxos, amount)
    total_input = sum(u.value for u in selected)

    # Estimate size
    estimated_size = estimate_vsize(len(selected), 2)  # 2 outputs
    fee = estimated_size * fee_rate

    # Calculate change
    change = total_input - amount - fee

    if change < 546:  # Dust threshold
        fee += change
        change = 0

    # Build transaction
    tx = Transaction()
    for utxo in selected:
        tx.add_input(utxo)

    tx.add_output(recipient, amount)
    if change > 0:
        tx.add_output(change_address, change)

    return tx

Replace-By-Fee (RBF)

Transactions can be replaced with higher-fee versions if:

  • Sequence number < 0xfffffffe
  • Replacement has higher fee AND higher fee rate

Signaling RBF

{
  "vin": [{
    "sequence": 4294967293  // 0xfffffffd - RBF enabled
  }]
}

Broadcasting

Via API

# Broadcast raw transaction hex
curl -X POST https://mempool.space/api/tx \
  -H "Content-Type: text/plain" \
  -d "0200000001..."

Response

Success returns the txid:

"abc123def456..."

Error returns description:

{
  "error": "bad-txns-inputs-missingorspent"
}

Agent Best Practices

  1. Always verify UTXOs exist before building transactions
  2. Use replace-by-fee for flexibility (sequence < max)
  3. Include change output unless dust
  4. Wait for confirmations based on value:
    • < $100: 1 confirmation
    • < $10,000: 3 confirmations
    • $10,000: 6 confirmations

  5. Monitor fee rates to avoid overpaying

Machine-Readable Summary

{
  "topic": "bitcoin-transactions",
  "components": ["inputs", "outputs", "locktime", "witness"],
  "fee_calculation": "sum(inputs) - sum(outputs)",
  "confirmation_thresholds": {
    "unconfirmed": 0,
    "tentative": 1,
    "standard": 3,
    "final": 6
  },
  "apis": {
    "broadcast": "POST https://mempool.space/api/tx",
    "status": "GET https://mempool.space/api/tx/{txid}/status"
  }
}