> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.ampdb.io/api/mcp` to find what you need.

# Bitcoin RPC

The Bitcoin RPC provider connects Amp to Bitcoin. It reads blocks from the JSON-RPC interface of a Bitcoin Core node,
or of a hosted service that offers the same methods. [Raw datasets](/datasets/raw-datasets) of kind `bitcoin-rpc`
extract through it into four tables, blocks, transactions, and their inputs and outputs:
[Bitcoin RPC Tables](/datasets/tables/bitcoin-rpc) lists their columns.

## Prerequisites

* A running Amp: [Solo Mode](/running-amp/solo-mode) is the quickest.
* [`ampctl`](/clis/ampctl).
* A Bitcoin JSON-RPC endpoint that supports `getblockcount`, `getblockhash`, and `getblock` with verbosity `2`. To
  extract history, the node must not be pruned, since a pruned node deletes old blocks. It doesn't need a transaction
  index (`txindex`).

Export the endpoint's URL. Bitcoin Core authenticates with the `rpcuser` and `rpcpassword` from its configuration, or
the `user:password` pair in its `.cookie` file: put them in the URL.

```bash
export BITCOIN_RPC_URL=http://user:password@127.0.0.1:8332
```

`ampd` substitutes the variable when it extracts, not `ampctl` when you register, so set it in the environment `ampd`
runs in. When it's missing, `ampd` logs `environment variable substitution failed for provider, skipping`, and the job
fails with `No provider found for kind 'bitcoin-rpc' and network 'bitcoin-mainnet'`. See
[Environment variables](/providers#environment-variables).

## Configure the provider

A minimal configuration names the kind, the network, and the endpoint:

```toml [bitcoin_mainnet.toml]
kind = "bitcoin-rpc"
network = "bitcoin-mainnet"
url = "${BITCOIN_RPC_URL}"
```

* `kind` is always `bitcoin-rpc`.
* `network` must match the `network` of the datasets that extract through it, like `bitcoin-mainnet`, `testnet`, or
  `signet`.
* `url` is the endpoint, over HTTP or HTTPS. Credentials in it are sent with HTTP basic authentication.

A fuller one, for a hosted service that takes an API key in a header, adds limits and the number of confirmations.
Export each variable it refers to, or leave out the fields you don't use: Amp skips a provider with a variable that
isn't set.

```toml [bitcoin_mainnet.toml]
kind = "bitcoin-rpc"
network = "bitcoin-mainnet"
url = "${BITCOIN_RPC_URL}"
auth_header = "x-api-key"
auth_token = "${BITCOIN_RPC_TOKEN}"
rate_limit_per_minute = 600
concurrent_request_limit = 8
confirmations = 6
timeout_secs = 30
```

* `auth_token` is sent as `Authorization: Bearer <token>`, or in the header `auth_header` names.
* `rate_limit_per_minute` caps the requests Amp sends the endpoint. Set it to your endpoint's limit.
* `concurrent_request_limit` caps the requests in flight at once, `8` by default. Each block takes two.
* `confirmations` sets when Amp treats a block as final, `6` by default (see
  [Confirmations and finality](/providers/bitcoin-rpc#confirmations-and-finality)).
* `timeout_secs` is how long Amp waits for a request, `30` seconds by default.

The [configuration reference](/providers/configuration-reference#bitcoin-rpc) documents every field, with its type and
default.

## Create a dataset

:::steps
### Register the provider

Provider names take lowercase letters, digits, and underscores, and start with a letter or underscore.
[Providers](/providers) explains how registration works.

```bash
ampctl provider register bitcoin_mainnet ./bitcoin_mainnet.toml
```

### Generate the manifest

```bash
ampctl manifest generate --kind bitcoin-rpc --network bitcoin-mainnet -o bitcoin_mainnet.json
```

Add `--start-block` to skip the chain's early history (see [Start block](/datasets/raw-datasets#start-block)), and
`--finalized-blocks-only` to extract only confirmed blocks (see
[Confirmations and finality](/providers/bitcoin-rpc#confirmations-and-finality)).

### Register the dataset

```bash
ampctl dataset register acme/bitcoin_mainnet ./bitcoin_mainnet.json --tag 1.0.0
```

### Deploy it

```bash
ampctl dataset deploy acme/bitcoin_mainnet@1.0.0
```
:::

[Raw Datasets](/datasets/raw-datasets) covers the manifest and how extraction works.

## Tables

* [`blocks`](/datasets/tables/bitcoin-rpc#blocks): one row per block, with its hashes, time, difficulty, size, and
  transaction count.
* [`transactions`](/datasets/tables/bitcoin-rpc#transactions): one row per transaction, with its ID, size, and whether
  it's the block's coinbase transaction.
* [`inputs`](/datasets/tables/bitcoin-rpc#inputs): one row per input, naming the output it spends by `spent_txid` and
  `spent_vout`. Inputs don't carry amounts: join them to `outputs` for those. A coinbase input spends nothing, and
  holds its `coinbase` data instead.
* [`outputs`](/datasets/tables/bitcoin-rpc#outputs): one row per output, with its amount in satoshis (`value_sat`), its
  script, the script's type, and its address, when it has one.

## Confirmations and finality

A Bitcoin block is never final for certain: another chain can replace it until enough blocks are built on top. Each
block built on it is a confirmation, and makes that less likely.

By default, Amp extracts blocks up to the node's latest, and when a [reorg](/get-started/concepts/reorgs) replaces some,
extracts the new ones in their place. With [`--finalized-blocks-only`](/datasets/raw-datasets#finalized-blocks), it
extracts only blocks with at least `confirmations` blocks on top: with the default of `6`, the dataset trails the chain
by six blocks, about an hour, in exchange for blocks a reorg is very unlikely to replace. Without
`--finalized-blocks-only`, `confirmations` has no effect.

When several providers serve the same network, they must use the same `confirmations`.

## Limitations

* Amp reads each output's address from the `address` field Bitcoin Core returns from version 22. With older nodes,
  `script_pubkey_address` stays empty.
* `rate_limit_per_minute` and `concurrent_request_limit` apply to each provider on its own, not to all of them together.

## Errors

### `401 Unauthorized`

The node rejected the request's credentials. Check the user and password in the URL, or the token and header for a
hosted service.

### `HTTP status server error (500 Internal Server Error)`

The node answered a request with an error, which Amp doesn't show. The job's error names the block, like
`RPC call failed for block 0`. To see the node's error, ask it for that block yourself, the way Amp does:

:::terminal
```bash
hash=$(curl -s --data '{"jsonrpc":"1.0","id":1,"method":"getblockhash","params":[0]}' "$BITCOIN_RPC_URL" | jq -r .result)
curl -s --data "{\"jsonrpc\":\"1.0\",\"id\":1,\"method\":\"getblock\",\"params\":[\"$hash\",2]}" "$BITCOIN_RPC_URL"
```

```
{"result":null,"error":{"code":-1,"message":"Block not available (pruned data)"},"id":1}
```
:::

`Block not available (pruned data)` means the node is pruned, and deleted the block: use a node that keeps every
block, or a start block after what the node deleted. The node's `getblockchaininfo` gives the first block it still
has, as `pruneheight`.

### `provider pool has incompatible kinds, networks, or extraction settings`

Several providers serve the dataset's network with different `confirmations`. The job fails without retrying. Give
the providers the same value, or deploy again with `--provider` to use only one.
