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

# Bitcoin tooling

> Connect to a Chainstack Bitcoin node over JSON-RPC with curl, Python, and JavaScript on Mainnet, Testnet (testnet3), Signet Testnet, and Testnet4.

Get started with a [reliable Bitcoin RPC endpoint](https://chainstack.com/build-better-with-bitcoin/) to use the tools below.

Chainstack Bitcoin nodes serve the Bitcoin Core [JSON-RPC API](https://developer.bitcoin.org/reference/rpc/) over HTTPS. Send requests with curl or any HTTP client. The same calls work on Mainnet, Testnet (testnet3), Signet Testnet, and Testnet4.

## JSON-RPC API

To get basic network information from your Bitcoin node HTTPS endpoint, call `getblockchaininfo`:

<CodeGroup>
  ```bash cURL theme={"system"}
  curl YOUR_CHAINSTACK_ENDPOINT \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"1.0","id":1,"method":"getblockchaininfo","params":[]}'
  ```
</CodeGroup>

where YOUR\_CHAINSTACK\_ENDPOINT is your node HTTPS endpoint protected either with the key or password. See [node access details](/docs/manage-your-node#view-node-access-and-credentials).

To call methods from Postman, use the [Bitcoin RPC methods Postman collection](/reference/bitcoin-rpc-methods-postman-collection).

`bitcoin-cli` connects over plain HTTP only, so it cannot call a Chainstack HTTPS endpoint. Use curl or an HTTP client library.

## Python

Install [requests](https://pypi.org/project/requests/):

<CodeGroup>
  ```shell Shell theme={"system"}
  pip install requests
  ```
</CodeGroup>

<CodeGroup>
  ```python main.py theme={"system"}
  import requests

  ENDPOINT = "YOUR_CHAINSTACK_ENDPOINT"


  def rpc(method, params=None):
      response = requests.post(
          ENDPOINT,
          json={"jsonrpc": "1.0", "id": 1, "method": method, "params": params or []},
          timeout=30,
      )
      if "application/json" not in response.headers.get("Content-Type", ""):
          raise RuntimeError(f"HTTP {response.status_code}: {response.text.strip()}")
      body = response.json()
      if body.get("error"):
          raise RuntimeError(body["error"]["message"])
      return body["result"]


  info = rpc("getblockchaininfo")
  print("Chain:", info["chain"])
  print("Blocks:", info["blocks"])

  fee = rpc("estimatesmartfee", [6])
  if "feerate" in fee:
      print("Fee rate (BTC/kvB):", fee["feerate"])
  else:
      mempool = rpc("getmempoolinfo")
      print("No estimate for 6 blocks; minimum fee (BTC/kvB):", mempool["mempoolminfee"])
  ```
</CodeGroup>

On Mainnet, the `main.py` example prints `Chain: main`, the block height, and a fee rate for confirmation within 6 blocks. For a password-protected endpoint, put the credentials in the URL, as in `https://USERNAME:PASSWORD@HOSTNAME`, and requests sends them as HTTP basic authentication.

<Note>
  python-bitcoinlib 0.12.2 rejects HTTPS URLs in `bitcoin.rpc.Proxy` with `Unsupported URL scheme 'https'`, so send JSON-RPC to a Chainstack endpoint with an HTTP client such as requests.
</Note>

## JavaScript

Install [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) with its key-pair dependencies. The example needs Node.js 20 or later, which `ecpair` requires, and uses the built-in `fetch`.

<CodeGroup>
  ```shell Shell theme={"system"}
  npm install bitcoinjs-lib ecpair tiny-secp256k1
  ```
</CodeGroup>

<CodeGroup>
  ```javascript index.mjs theme={"system"}
  import * as bitcoin from "bitcoinjs-lib";
  import { ECPairFactory } from "ecpair";
  import * as ecc from "tiny-secp256k1";

  const ENDPOINT = "YOUR_CHAINSTACK_ENDPOINT";

  async function rpc(method, params = []) {
    const response = await fetch(ENDPOINT, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ jsonrpc: "1.0", id: 1, method, params }),
      signal: AbortSignal.timeout(30000),
    });
    if (!response.headers.get("content-type")?.includes("application/json")) {
      throw new Error(`HTTP ${response.status}: ${(await response.text()).trim()}`);
    }
    const body = await response.json();
    if (body.error) throw new Error(body.error.message);
    return body.result;
  }

  const { chain, blocks } = await rpc("getblockchaininfo");
  console.log("Chain:", chain, "Blocks:", blocks);

  const network = chain === "main" ? bitcoin.networks.bitcoin : bitcoin.networks.testnet;
  const ECPair = ECPairFactory(ecc);
  const keyPair = ECPair.makeRandom({ network });
  const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network });
  console.log("Address:", address);

  const result = await rpc("validateaddress", [address]);
  console.log("Valid on", chain + ":", result.isvalid);
  ```
</CodeGroup>

The `index.mjs` example picks the bitcoinjs-lib network from `chain`, generates a new native SegWit address for it, and checks the address with `validateaddress`. On Mainnet, it prints `Chain: main`, a `bc1q` address, and `Valid on main: true`.

The built-in `fetch` rejects a URL that includes credentials, with `TypeError: Request cannot be constructed from a URL that includes credentials`. For a password-protected endpoint, send the credentials in an `Authorization` header instead:

<CodeGroup>
  ```javascript auth.mjs theme={"system"}
  const ENDPOINT = "https://HOSTNAME";
  const AUTHORIZATION = "Basic " + Buffer.from("USERNAME:PASSWORD").toString("base64");

  const response = await fetch(ENDPOINT, {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: AUTHORIZATION },
    body: JSON.stringify({ jsonrpc: "1.0", id: 1, method: "getblockcount", params: [] }),
  });
  console.log("Blocks:", (await response.json()).result);
  ```
</CodeGroup>

## Testnet, Signet Testnet, and Testnet4

Testnet (testnet3), Signet Testnet, and Testnet4 endpoints take the same JSON-RPC calls as Bitcoin Mainnet, so the Python and JavaScript examples run on them unchanged. Before your application sends transactions, check that `chain` in `getblockchaininfo` is `test`, `signet`, or `testnet4`, because testnet addresses look the same on every testnet. For network parameters and how the testnets differ, see [Bitcoin testnets](/docs/bitcoin-testnets).

* bitcoinjs-lib has no separate Signet Testnet or Testnet4 network. `networks.testnet` produces `tb1` addresses that are valid on all three testnets, which is the network the `index.mjs` example selects for any `chain` other than `main`.
* On Testnet4, `estimatesmartfee` often has no estimate for short targets, so the `main.py` example falls back to the node's minimum mempool fee. See [Fee estimation on Testnet4](/docs/bitcoin-testnets#fee-estimation-on-testnet4).
