> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moralis.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bitcoin Streams

> Stream real-time Bitcoin transaction data directly to your application with webhooks, no node infrastructure, no polling, no missed blocks.

## Overview

**Bitcoin Streams** extends Moralis Streams to the Bitcoin network, delivering real-time transaction data via webhooks. Moralis indexes Bitcoin automatically and pushes structured payloads to your endpoint whenever transactions match your criteria.

Webhooks typically arrive within seconds of a block being mined — no node, no polling, no missed blocks.

***

## Key Features

* **Real-time delivery:** end-to-end latency is typically under 10 seconds after a block is mined
* **Mempool notifications:** unconfirmed transactions fire a webhook the moment they hit the mempool — react before the next block is mined
* **Three-phase notifications:** matches can fire up to three times — first when the transaction is broadcast to the mempool (`block.hash: "mempool"`), again when it lands in a block (`confirmed: false`, near the chain tip), and finally after the 2-block confirmation depth (`confirmed: true`, reorg-safe)
* **All address formats supported:** P2PKH (legacy, `1...`), P2SH (SegWit-wrapped, `3...`), and Bech32 native SegWit (`bc1q...`)
* **Xpub support:** attach an extended public key to a stream and Moralis derives and monitors its addresses automatically — ideal for HD wallets
* **Flexible monitoring:** watch specific addresses, attach an xpub, or enable firehose mode with `allAddresses` to receive every Bitcoin transaction
* **Automatic retries:** Moralis retries webhook delivery until it receives an HTTP 200 response

***

## How It Works

1. A transaction is broadcast to the Bitcoin mempool. Moralis evaluates every mempool transaction against your registered addresses and xpubs
2. Matching mempool transactions fire a webhook with `confirmed: false` and the sentinel values `block.height: "0"` / `block.hash: "mempool"` — delivered once per stream
3. A new Bitcoin block is produced and Moralis evaluates every transaction in it against your registered addresses
4. Matching block transactions fire a webhook with `confirmed: false` and the real block `height` / `hash` — the block is close to the chain tip and could still be reorged
5. After 2 additional blocks have been mined on top, Moralis re-fetches the block and delivers a third webhook with `confirmed: true` — the transaction is now reorg-safe
6. Your service upserts on `txid` to reconcile the lifecycle, branching on `block.hash === "mempool"` for the mempool stage

***

## Webhook Payload Structure

Each Bitcoin webhook payload contains:

* **Block metadata** — hash, height, timestamp, difficulty, merkle root, transaction count
* **Transaction details** — `txid`, input/output counts, locktime, version, block reference
* **Outputs (`vout`)** — value in BTC (not satoshis), script type, recipient address
* **Inputs (`vin`)** — structural fields only; `address` and `value` fields remain `null`
* **Confirmation status** — `confirmed: false` for the near-tip delivery, `confirmed: true` once the block has cleared the 2-block confirmation depth

<Note>
  Output values are represented as **BTC decimals** (e.g. `3.13258866`). To convert to satoshis, multiply by `1e8`:

  ```js theme={null}
  const satoshis = Math.round(btc * 1e8);
  ```
</Note>

***

## Mempool Notifications

Bitcoin Streams emit webhook notifications for **unconfirmed transactions** as soon as they hit the mempool — no need to wait for block inclusion. The payload shape is identical to a confirmed-block webhook, with sentinel fields on the `block` object marking it as mempool:

| Field          | Mempool value | Confirmed-block value |
| -------------- | ------------- | --------------------- |
| `block.height` | `"0"`         | real block height     |
| `block.hash`   | `"mempool"`   | real block hash       |
| `confirmed`    | `false`       | `false` then `true`   |

Everything else (`transactions[]`, `vin`, `vout`, addresses, value, fee) is populated normally.

### Example payload

```json theme={null}
{
  "streamId": "cefa6529-e3ec-4ee8-8cbd-206d2e7ce64e",
  "chainId": "btc-mainnet",
  "confirmed": false,
  "block": {
    "height": "0",
    "hash": "mempool"
  },
  "retries": 0,
  "transactions": [ ]
}
```

### Detecting mempool in your handler

Branch on either sentinel field — `block.hash` is the most explicit:

```js theme={null}
const isMempool = payload.block.hash === "mempool";
```

### Behavior

* **Delivered at most once per stream** per matching mempool transaction
* **No follow-up webhook on confirmation from the mempool delivery itself** — the regular confirmed-block stream handles `confirmed: false` (in-block) and `confirmed: true` (reorg-safe) for the same `txid`
* **Mempool transactions are not guaranteed to confirm.** They can be evicted, replaced (RBF), or expire on low fee. Treat them as **pending**, not settled
* **Matching covers both sends and receives.** In mempool payloads, `vin` and `vout` addresses are both populated, so a watched address triggers a match whether it is sending or receiving
* **Xpub-derived addresses are fully supported** — addresses derived from an attached xpub match against the mempool the same way they do against confirmed blocks

### Getting started

No new endpoint, parameter, or schema. Existing Bitcoin streams pick up mempool notifications automatically. Just branch on `block.hash === "mempool"` (or `block.height === "0"`) in your handler to separate mempool events from confirmed-block events.

***

## Configuration Parameters

When creating a Bitcoin stream you specify:

| Parameter        | Description                                                 |
| ---------------- | ----------------------------------------------------------- |
| `webhookUrl`     | Public HTTPS endpoint that will receive payloads (required) |
| `network`        | Must be `["mainnet"]` as an array                           |
| `includeInputs`  | Include input structure in the payload                      |
| `includeOutputs` | Include output structure in the payload                     |
| `allAddresses`   | Firehose mode — deliver every Bitcoin transaction           |
| `description`    | Human-readable stream description                           |
| `tag`            | Identifier for routing / filtering on your side             |

***

## API Endpoints

All Bitcoin Streams endpoints require the `x-api-key` header.

### Streams

| Method   | Endpoint                                                               | Purpose                                                                                                               |
| -------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `PUT`    | `/streams/bitcoin`                                                     | [Create a stream](/streams/api-reference/bitcoin/streams/create-streams) with a webhook URL and configuration         |
| `POST`   | `/streams/bitcoin/{id}`                                                | [Update a stream's](/streams/api-reference/bitcoin/streams/update-stream) configuration                               |
| `POST`   | `/streams/bitcoin/{id}/status`                                         | [Pause or resume a stream](/streams/api-reference/bitcoin/streams/update-stream-status)                               |
| `GET`    | `/streams/bitcoin`                                                     | [List existing Bitcoin streams](/streams/api-reference/bitcoin/streams/get-streams)                                   |
| `GET`    | `/streams/bitcoin/{id}`                                                | [Get a stream by ID](/streams/api-reference/bitcoin/streams/get-stream-by-id)                                         |
| `DELETE` | `/streams/bitcoin/{id}`                                                | [Remove a stream](/streams/api-reference/bitcoin/streams/delete-stream)                                               |
| `POST`   | `/streams/bitcoin/{chainId}/block/{blockNumber}`                       | [Get webhook data for a historical block](/streams/api-reference/bitcoin/streams/get-webhook-data-by-block-number)    |
| `POST`   | `/streams/bitcoin/{chainId}/block-to-webhook/{blockNumber}/{streamId}` | [Replay a historical block](/streams/api-reference/bitcoin/streams/send-webhook-data-by-block-number) for backfilling |

### Addresses

| Method   | Endpoint                        | Purpose                                                                                                     |
| -------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `GET`    | `/streams/bitcoin/{id}/address` | [List addresses](/streams/api-reference/bitcoin/address/get-addresses-by-stream) attached to a stream       |
| `POST`   | `/streams/bitcoin/{id}/address` | [Add an address](/streams/api-reference/bitcoin/address/add-address-to-stream) to monitor (one per request) |
| `DELETE` | `/streams/bitcoin/{id}/address` | [Remove an address](/streams/api-reference/bitcoin/address/delete-address-from-stream) from a stream        |

### Xpub

| Method   | Endpoint                              | Purpose                                                                                          |
| -------- | ------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `GET`    | `/streams/bitcoin/{id}/xpub`          | [List xpubs](/streams/api-reference/bitcoin/xpub/get-xpubs) attached to a stream                 |
| `POST`   | `/streams/bitcoin/{id}/xpub`          | [Add an xpub](/streams/api-reference/bitcoin/xpub/add-xpub) to derive and monitor addresses from |
| `DELETE` | `/streams/bitcoin/{id}/xpub/{xpubId}` | [Remove an xpub](/streams/api-reference/bitcoin/xpub/delete-xpub) from a stream                  |

***

## Limitations

<Warning>
  Bitcoin Streams has a few behaviors that differ from EVM Streams — plan your consumer around them.
</Warning>

* **Input data is not populated.** Even with `includeInputs: true`, input `address` and `value` fields return `null`. You can reliably detect **inbound** transfers to watched addresses, not outflows.
* **`isCoinbase` is always `false`.** The `isCoinbase` field does not reliably identify coinbase transactions. Detect coinbase transactions using a heuristic: the transaction contains an `OP_RETURN` output **and** has exactly one input.
* **Values are BTC decimals.** Output values are not denominated in satoshis. Convert with `Math.round(btc * 1e8)` before feeding integer-based ledger systems.
* **Mainnet only.** The `network` parameter must be `["mainnet"]`.

***

## Processing Patterns

### Branching on mempool vs. confirmed block

A single `txid` can arrive up to three times: once from the mempool, once on block inclusion, and once on reorg-safe confirmation. Branch on the `block.hash` sentinel before doing any block-specific work:

```js theme={null}
if (payload.block.hash === "mempool") {
  // pending: tx broadcast but not yet mined
} else if (payload.confirmed === false) {
  // included in a block near the chain tip — could still reorg
} else {
  // reorg-safe — safe to persist
}
```

### Deduplication

Use the transaction id (`txid`) as your natural deduplication key. Upsert on each delivery, advancing your state machine through `mempool` → `in-block` → `confirmed` as each webhook arrives. Note that the mempool delivery can be **skipped entirely** for transactions Moralis only observes once they are already mined.

### Coinbase detection

Since `isCoinbase` is unreliable, detect coinbase transactions like this:

```js theme={null}
const isCoinbase =
  tx.vin.length === 1 &&
  tx.vout.some((out) => out.scriptPubKey?.type === "nulldata"); // OP_RETURN
```

### Value conversion

Store both BTC and satoshi values so downstream services can pick whichever representation fits:

```js theme={null}
const btc = vout.value;
const satoshis = Math.round(btc * 1e8);
```

***

## Common Use Cases

* **Exchange deposits** — surface deposits the moment they hit the mempool (`block.hash === "mempool"`), mark them `pending` on block inclusion (`confirmed: false`), and `confirmed` on the reorg-safe delivery (`confirmed: true`)
* **Pending balances** — show users a pending balance the instant a tx is broadcast, before any block has been mined
* **Payment UX** — trigger downstream flows on payment broadcast instead of waiting for confirmation
* **Mempool analytics** — RBF detection, fee analytics, and MEV / arbitrage signals against unconfirmed activity
* **Cold wallet surveillance** — alert on any inbound transfer to an offline storage address
* **Mining pool revenue** — watch payout addresses for both standard payments and coinbase block rewards
* **Treasury monitoring** — real-time accounting for corporate or DAO-held BTC addresses

***

## Setup Checklist

1. Generate an API key from [admin.moralis.com](https://admin.moralis.com)
2. Deploy a publicly accessible HTTPS webhook endpoint
3. Create the stream via `PUT /streams/bitcoin`
4. Attach the wallets you want to monitor — either by [registering individual addresses](/streams/api-reference/bitcoin/address/add-address-to-stream) (`POST /streams/bitcoin/{id}/address`) or by [adding an xpub](/streams/api-reference/bitcoin/xpub/add-xpub) (`POST /streams/bitcoin/{id}/xpub`) to auto-derive and watch its addresses
5. Implement the verification handler — respond to the empty-body test `POST` with HTTP `200`
6. Build upsert logic keyed on `txid` to handle the `confirmed: false` → `confirmed: true` transition
7. Always return HTTP `200` from your handler, even when your own processing errors — Moralis retries on non-200 responses
