Guide · Node.js · SDK · REST API

Bitcoin Address Validation in Node.js — P2PKH, SegWit & Taproot

What the four Bitcoin address formats are, how their checksums work, why regex falls short, and how to validate any mainnet address in a single API call.

1. What is a Bitcoin address?

A Bitcoin address is the encoded form of a scriptPubKey — the locking script that defines the conditions under which bitcoins can be spent. Instead of sharing a raw hex script, users share a shorter, checksummed string that wallets can decode and verify before broadcasting a transaction.

Over the years, the Bitcoin protocol has introduced several address formats. Each generation brought improvements in efficiency, security, or features — from the original Pay-to-Public-Key-Hash (P2PKH) addresses to the latest Taproot (P2TR) addresses introduced with the November 2021 soft fork.

All four formats remain valid on the mainnet today, so any robust validation solution must handle all of them — including their distinct encoding schemes and checksum algorithms.

ℹ️This guide covers mainnet addresses only. Testnet addresses (prefixed with tb1 or starting with m / n) use different version bytes and are not validated by this endpoint.

2. Bitcoin address types

There are four address types in active use on the Bitcoin mainnet. Each uses a different encoding scheme and serves a different purpose:

TypePrefixEncodingPurpose
P2PKH1...Base58CheckLegacy — pay to public key hash
P2SH3...Base58CheckScript hash — multi-sig, wrapped SegWit
P2WPKHbc1q...Bech32Native SegWit — lower fees
P2TRbc1p...Bech32mTaproot — privacy, scripting

Here is an example of each type:

P2PKH (Legacy)

1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

The genesis block coinbase address (Satoshi's address)

P2SH (Script Hash)

3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy

Common for multi-signature wallets and wrapped SegWit

P2WPKH (Native SegWit)

bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq

Lower transaction fees thanks to segregated witness

P2TR (Taproot)

bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3wf0qm

Latest format — improved privacy and scripting capabilities


3. The encoding algorithms

Bitcoin addresses use three different encoding schemes depending on the address type. Each scheme includes a checksum to catch typos before funds are sent to a wrong address.

Base58Check (P2PKH & P2SH)

Base58Check uses a 58-character alphabet that excludes visually ambiguous characters (0, O, I, l). The checksum is the first 4 bytes of a double SHA-256 hash of the payload (version byte + data). This catches any single-character typo with extremely high probability.

// Base58Check structure:
// [version byte] [20-byte hash] [4-byte checksum]
//
// Checksum = SHA256(SHA256(version + hash)).slice(0, 4)
//
// P2PKH version byte: 0x00  → addresses start with "1"
// P2SH  version byte: 0x05  → addresses start with "3"

Bech32 (P2WPKH — Native SegWit)

Introduced in BIP 173, Bech32 uses a BCH (Bose-Chaudhuri-Hocquenghem) error-detecting code with a 6-character checksum. The encoding has a human-readable part (bc for mainnet), a separator (1), and the data part. Bech32 is case-insensitive but must not mix cases.

// Bech32 structure:
// [human-readable part] [separator "1"] [data + 6-char checksum]
//
// bc1q...  → witness version 0 (SegWit v0)
// "bc" = mainnet, "tb" = testnet

Bech32m (P2TR — Taproot)

Introduced in BIP 350, Bech32m uses the same structure as Bech32 but with a different constant in the checksum calculation. This was done to fix a length extension mutation weakness in the original Bech32 encoding. Taproot addresses use witness version 1, which gives them the bc1p prefix.

// Bech32m vs Bech32:
// Same structure, different checksum constant:
//   Bech32  constant: 0x01  (witness v0)
//   Bech32m constant: 0x2bc830a3  (witness v1+)
//
// bc1p...  → witness version 1 (Taproot)

4. Why regex isn't enough

It is tempting to validate Bitcoin addresses with a regular expression. After all, they have distinct prefixes and predictable lengths. Here is why that approach fails:

Three different encoding schemes

P2PKH and P2SH use Base58 (case-sensitive, no 0/O/I/l). P2WPKH uses Bech32. P2TR uses Bech32m. A single regex cannot verify the checksum of all three — it can only check format, not integrity.

Checksum verification is essential

The whole point of address encoding is to catch typos. A regex that matches the pattern ^1[a-km-zA-HJ-NP-Z1-9]{25,34}$ will accept any string that looks like a P2PKH address — including ones with invalid checksums. Sending funds to such an address means permanent loss.

Testnet vs. mainnet confusion

Testnet addresses start with tb1, m, or n. A naive regex might accept them as valid mainnet addresses, or reject them without useful feedback.

Base58 ambiguity

Base58 encodes arbitrary-length data, so P2PKH addresses can be 25 to 34 characters long depending on leading zeros in the hash. A regex that enforces a fixed length will reject valid addresses. And Bech32/Bech32m are case-insensitive but must not mix upper and lower case — another constraint regex handles poorly.

⚠️An address that matches a regex pattern is not necessarily valid. Only full checksum verification can confirm that a Bitcoin address is safe to use as a payment destination.

5. The right solution

The IsValid BTC Address API handles all four address formats in a single GET request — decoding Base58Check, Bech32, and Bech32m, verifying the checksum, and returning the address type.

4
Formats
P2PKH, P2SH, P2WPKH, P2TR
Full
Checksum
SHA-256, BCH, Bech32m
Auto
Type detection
Returns the address type

Get your free API key at isvalid.dev — 100 calls per day, no credit card required.

Full parameter reference and response schema: BTC Address API docs →


6. Node.js code example

Using the @isvalid-dev/sdk package or the native fetch API (Node 18+).

// btcValidator.js
import { createClient } from '@isvalid-dev/sdk';

const iv = createClient({ apiKey: process.env.ISVALID_API_KEY });

// ── Validate a P2PKH (legacy) address ─────────────────────────────────────

const result = await iv.btcAddress('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa');

if (!result.valid) {
  console.log('Invalid Bitcoin address');
} else {
  console.log(`Valid ${result.type} address`);
  // → "Valid P2PKH address"
}

// ── Validate a SegWit address ─────────────────────────────────────────────

const segwit = await iv.btcAddress('bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq');
console.log(segwit);
// → { valid: true, type: "P2WPKH" }

// ── Validate a Taproot address ────────────────────────────────────────────

const taproot = await iv.btcAddress('bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3wf0qm');
console.log(taproot);
// → { valid: true, type: "P2TR" }

In an Express withdrawal handler you might use it like this:

// routes/withdraw.js (Express)
app.post('/withdraw', async (req, res) => {
  const { btcAddress, amount } = req.body;

  let check;
  try {
    check = await validateBtcAddress(btcAddress);
  } catch {
    return res.status(502).json({ error: 'Address validation service unavailable' });
  }

  if (!check.valid) {
    return res.status(400).json({ error: 'Invalid Bitcoin address' });
  }

  // Optionally restrict to certain address types
  if (check.type === 'P2PKH') {
    console.log('Legacy address — higher transaction fees expected');
  }

  // Proceed to build and broadcast the transaction
  const txId = await bitcoinService.send({ to: btcAddress, amount });
  res.json({ success: true, txId, addressType: check.type });
});
Validating the destination address before broadcasting a transaction is critical. Once a Bitcoin transaction is confirmed, it cannot be reversed. A checksum-verified address ensures funds are not lost to a typo.

7. cURL example

P2PKH (Legacy) address:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/btc-address?value=1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"

P2SH (Script Hash) address:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/btc-address?value=3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy"

P2WPKH (Native SegWit) address:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/btc-address?value=bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"

P2TR (Taproot) address:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/btc-address?value=bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3wf0qm"

Invalid address (bad checksum):

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/btc-address?value=1A1zP1eP5QGefi2DMPTfTL5SLmv7Divf00"

8. Understanding the response

Valid P2PKH address:

{
  "valid": true,
  "type": "P2PKH"
}

Valid P2WPKH (SegWit) address:

{
  "valid": true,
  "type": "P2WPKH"
}

Invalid address:

{
  "valid": false,
  "type": null
}
FieldTypeDescription
validbooleantrue when the address has a valid format, correct checksum, and is a recognised mainnet address type
typestring | nullDetected address type: P2PKH, P2SH, P2WPKH, or P2TR. null if the address is invalid

The type field lets you enforce policies — for example, only accepting SegWit or Taproot addresses for lower transaction fees, or warning users about legacy address overhead.


9. Edge cases

Testnet addresses are not validated

Bitcoin testnet addresses use different prefixes: tb1 for Bech32/Bech32m, and m or n for Base58Check. This endpoint only validates mainnet addresses. A testnet address will return valid: false.

P2SH can wrap SegWit

A P2SH address starting with 3 may internally contain a SegWit witness program (P2SH-P2WPKH). From the outside, the address looks like a regular P2SH address and is validated as such. The API reports the type as P2SH — it cannot distinguish between a native P2SH script and a wrapped SegWit script from the address alone.

Vanity addresses

Vanity addresses like 1BitcoinEaterAddressDontSendf59kuE are regular addresses generated by brute-forcing a specific prefix. They have valid checksums and will pass validation. Whether funds sent to them are recoverable depends on whether the private key is known — which is outside the scope of address validation.

Case sensitivity in Bech32 / Bech32m

Bech32 and Bech32m addresses are case-insensitive — both bc1q... and BC1Q... are valid. However, mixing cases within a single address (e.g., bc1Qar0...) is not allowed. The API correctly rejects mixed-case Bech32 addresses.


Summary

Validate all four address types: P2PKH, P2SH, P2WPKH, and P2TR
Full checksum verification — Base58Check, Bech32, and Bech32m
Automatic type detection returned in the response
Mainnet-only — testnet addresses are rejected
Do not rely on regex — it cannot verify checksums
Do not accept unvalidated addresses for withdrawals

See also

Validate Bitcoin addresses instantly

Free tier includes 100 API calls per day. No credit card required. Supports P2PKH, P2SH, P2WPKH (SegWit), and P2TR (Taproot) mainnet addresses.