Fetch testnet Bitcoin on regtest

Request testnet BTC from the Hiro faucet for local development and testing


const TESTNET_ADDRESS = 'bcrt1q728h29ejjttmkupwdkyu2x4zcmkuc3q29gvwaa';
try {
const response = await fetch(
`https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${TESTNET_ADDRESS}`,
{
method: 'POST',
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
console.log("Faucet response:", result);
if (result.success) {
console.log(`Transaction ID: ${result.txid}`);
console.log(`Amount sent: ${result.amount} sats`);
}
} catch (error) {
console.error("Faucet request failed:", error);
}

Use cases

  • Local development with Bitcoin transactions
  • Testing sBTC operations
  • Integration testing for cross-chain applications
  • Developing Bitcoin-aware smart contracts

Key concepts

The Hiro testnet faucet:

  • Rate limited: One request per address per hour
  • Amount: Sends 0.5 testnet BTC per request
  • Network: Works with Bitcoin testnet/regtest addresses
  • Format: Supports legacy, segwit, and taproot addresses

Generate a testnet address

import * as bitcoin from 'bitcoinjs-lib';
import * as ecc from 'tiny-secp256k1';
// Initialize ECC library
bitcoin.initEccLib(ecc);
// Define testnet network
const testnet = bitcoin.networks.testnet;
// Generate new key pair
const keyPair = bitcoin.ECPair.makeRandom({ network: testnet });
// Generate different address types
const { address: p2wpkh } = bitcoin.payments.p2wpkh({
pubkey: keyPair.publicKey,
network: testnet
});
const { address: p2tr } = bitcoin.payments.p2tr({
pubkey: keyPair.publicKey.slice(1),
network: testnet
});
console.log("Segwit address:", p2wpkh); // tb1q...
console.log("Taproot address:", p2tr); // tb1p...

Request with retry logic

async function requestBTCWithRetry(
address: string,
maxRetries = 3
): Promise<any> {
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(
`https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${address}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
}
);
if (response.ok) {
return await response.json();
}
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
console.log(`Rate limited. Retry after ${retryAfter}s`);
await delay(parseInt(retryAfter || '3600') * 1000);
continue;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`Attempt ${i + 1} failed, retrying...`);
await delay(2000 * (i + 1)); // Exponential backoff
}
}
}

Monitor transaction confirmation

async function waitForConfirmation(txid: string): Promise<void> {
console.log(`Waiting for transaction ${txid} to confirm...`);
while (true) {
try {
const response = await fetch(
`https://api.testnet.hiro.so/extended/v1/tx/${txid}`
);
if (response.ok) {
const tx = await response.json();
if (tx.tx_status === 'success') {
console.log(`Transaction confirmed in block ${tx.block_height}`);
break;
}
}
// Wait 10 seconds before checking again
await new Promise(resolve => setTimeout(resolve, 10000));
} catch (error) {
console.error("Error checking transaction:", error);
}
}
}
// Usage
const result = await requestBTCWithRetry(TESTNET_ADDRESS);
if (result.success) {
await waitForConfirmation(result.txid);
}
Local development

For local regtest environments, you'll need to run your own Bitcoin node and mine blocks. The Hiro faucet only works with the public testnet.

Setting up local regtest

# Start Bitcoin regtest node
bitcoind -regtest -rpcuser=user -rpcpassword=pass
# Generate new address
bitcoin-cli -regtest getnewaddress
# Mine blocks to that address
bitcoin-cli -regtest generatetoaddress 101 <address>
# Send BTC to your test address
bitcoin-cli -regtest sendtoaddress <test-address> 1

Integration with sBTC

// Request BTC for sBTC testing
async function prepareSBTCTest(btcAddress: string, stxAddress: string) {
// 1. Request testnet BTC
const btcResult = await fetch(
`https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${btcAddress}`,
{ method: 'POST', headers: { 'Content-Type': 'application/json' } }
).then(r => r.json());
// 2. Request testnet STX for fees
const stxResult = await fetch(
`https://api.testnet.hiro.so/extended/v1/faucets/stx?address=${stxAddress}`,
{ method: 'POST', headers: { 'Content-Type': 'application/json' } }
).then(r => r.json());
return {
btcTxid: btcResult.txid,
stxTxid: stxResult.txid,
btcAmount: btcResult.amount,
stxAmount: stxResult.amount
};
}