A hierarchical deterministic (HD) wallet generates every key and address it will ever need from a single starting value called a seed. Write down that seed once, and you can rebuild the entire wallet on any device. BIP-32 defined the idea in 2012, and nearly every modern Bitcoin wallet is built on it.
This guide shows how to create a Bitcoin HD wallet in Python. You will generate a seed phrase, derive legacy, SegWit, and Taproot addresses, and import an existing mnemonic to restore a wallet — all with a few lines of code and a maintained library.
What Is a Hierarchical Deterministic (HD) Wallet?

An HD wallet is a wallet whose keys all descend from one seed, arranged in a tree. Deterministic means the same seed always produces the same keys in the same order. Hierarchical means those keys branch into parent and child levels that you can organize into separate accounts.
Before HD wallets, software stored a fresh, unrelated private key for every address, so each new address meant another thing to back up. An HD wallet replaces that pile of keys with one secret. Wallets have worked this way since around 2013, which is why restoring a modern wallet usually means typing 12 or 24 words rather than importing a key file.
Seeds, Master Keys, and Child Keys
The process runs in one direction. A seed phrase is turned into a 512-bit seed, that seed is hashed with HMAC-SHA512 to produce a master private key and a chain code, and child keys are derived from there by feeding in an index number (BIP-32). Change the index, and you get a completely different child key.
Each key can have children of its own, which is the hierarchy part. A wallet uses different branches of the tree for different jobs — one branch for receiving addresses, another for change.
What Are Extended Keys (xprv and xpub)?
An extended key is a key bundled with its chain code, and together they can derive child keys. There are two kinds. An extended private key (xprv) can derive both private and public children. An extended public key (xpub) can derive only public children — addresses — without ever touching a private key.
That split is useful. A shop can load an xpub on its web server to generate a new receiving address per customer while the private keys stay offline. The trade-off is privacy: anyone holding your xpub can see every address in that branch, so an xpub protects your coins but not your anonymity.
Why Use an HD Wallet Instead of Single Keys?
The short answer: one backup covers everything, permanently. A few concrete benefits follow from that:
- One seed to back up. Those 12–24 words restore every address, including ones you create years later.
- A fresh address per payment. Reusing an address links your transactions on a public ledger. HD wallets make new addresses cheap, which helps privacy.
- Many coins from one phrase. The same seed can derive keys for Bitcoin, Ethereum, and other chains, each on its own branch.
- Watch-only setups. Share an xpub to monitor balances or hand out addresses without exposing spending keys.
Who Relies on HD Wallets?
Practically every current Bitcoin wallet is HD, from mobile apps to hardware devices like Ledger and Trezor. Exchanges and custodians use the tree structure to give each user a dedicated branch. Developers building wallets, payment processors, or block explorers lean on the same standards, so a seed created in one tool restores correctly in another. Merchants use xpubs to generate receiving addresses on a server that never stores a private key.
How BIP-39 Turns Entropy Into a Mnemonic Phrase
The readable word list comes from BIP-39. The wallet starts with random data — 128 to 256 bits of entropy — adds a short checksum, then slices the result into 11-bit chunks. Each chunk maps to one word in a fixed list of 2,048 words, producing a phrase of 12 to 24 words.
More entropy means more words:
| Entropy | Checksum | Words |
|---|---|---|
| 128 bits | 4 bits | 12 |
| 160 bits | 5 bits | 15 |
| 192 bits | 6 bits | 18 |
| 224 bits | 7 bits | 21 |
| 256 bits | 8 bits | 24 |
The words are not the seed themselves. They are stretched through the PBKDF2 function into the 512-bit seed the HD tree is built from. BIP-39 also allows an optional passphrase, sometimes called a 25th word, which produces a different seed from the same words — effectively a separate wallet hidden behind the same phrase.
Bitcoin Derivation Paths and Address Types
A derivation path is the address of a key inside the tree. Bitcoin paths follow BIP-44:
m / purpose' / coin_type' / account' / change / index
Each level narrows things down. The purpose number states which address standard you are using. coin_type is 0 for Bitcoin, a value fixed by SLIP-0044. account separates, say, savings from spending. change is 0 for receiving addresses and 1 for change. index counts addresses within a branch.
The apostrophe marks a hardened level. Hardened derivation cuts the link between a parent xpub and its children, so leaking one child key cannot expose its siblings. Account-level and higher branches are hardened for exactly this reason.
The purpose number also decides what your addresses look like:
| Standard | Purpose | Example path | Prefix | Address type |
|---|---|---|---|---|
| BIP-44 | 44' | m/44'/0'/0'/0/0 | 1... | Legacy (P2PKH) |
| BIP-49 | 49' | m/49'/0'/0'/0/0 | 3... | Nested SegWit (P2SH-P2WPKH) |
| BIP-84 | 84' | m/84'/0'/0'/0/0 | bc1q... | Native SegWit (P2WPKH) |
| BIP-86 | 86' | m/86'/0'/0'/0/0 | bc1p... | Taproot (P2TR) |
Native SegWit (BIP-84) is the common default for new wallets because of lower fees. Taproot (BIP-86) is the newest format.
How to Build a Bitcoin HD Wallet in Python
Several libraries handle HD derivation. This guide uses the hdwallet library, a production-stable package that implements BIP-32, BIP-44, BIP-49, BIP-84, and BIP-86 for Bitcoin and 200+ other coins (version 3.6.1 at the time of writing). bitcoinlib and bip_utils are solid alternatives if you prefer a different API.
Step 1: Install Python and the Library
Download Python 3.9 or newer from python.org, then install the crypto library with pip:
bash
pip install hdwallet
Step 2: Generate a Mnemonic and Seed
This creates a new 12-word English phrase and builds a native SegWit wallet from it:
python
from hdwallet import HDWallet
from hdwallet.mnemonics import (
BIP39Mnemonic, BIP39_MNEMONIC_WORDS, BIP39_MNEMONIC_LANGUAGES
)
from hdwallet.cryptocurrencies import Bitcoin
from hdwallet.hds import BIP84HD
from hdwallet.derivations import BIP84Derivation
# A fresh 12-word English mnemonic (BIP-39)
mnemonic = BIP39Mnemonic(
mnemonic=BIP39Mnemonic.from_words(
words=BIP39_MNEMONIC_WORDS.TWELVE,
language=BIP39_MNEMONIC_LANGUAGES.ENGLISH,
)
)
wallet = HDWallet(
cryptocurrency=Bitcoin,
hd=BIP84HD, # native SegWit (bc1q...)
network=Bitcoin.NETWORKS.MAINNET,
).from_mnemonic(mnemonic).from_derivation(
BIP84Derivation(coin_type=Bitcoin.COIN_TYPE, account=0, address=0)
)
print("Mnemonic:", wallet.mnemonic())
print("Path: ", wallet.path())
print("Address: ", wallet.address())
print("WIF: ", wallet.wif())
Running it prints a fresh mnemonic, the derivation path (m/84'/0'/0'/0/0), the first address (a bc1q... string), and the key in Wallet Import Format. Each run produces a different wallet, because the mnemonic is random.
Step 3: Derive Different Address Types
The address format depends on which standard you plug in. Swap BIP84HD and BIP84Derivation for BIP44HD and BIP44Derivation to get a legacy 1... address, or BIP49HD and BIP49Derivation for a nested-SegWit 3... address. The library supports Taproot (BIP-86) the same way. To generate the next receiving address in a branch, increment the index:
python
BIP84Derivation(coin_type=Bitcoin.COIN_TYPE, account=0, address=1)
Step 4: Import an Existing Mnemonic
Restoring a wallet means importing its mnemonic. Because derivation is deterministic, the same phrase and the same path always rebuild the same addresses:
python
from hdwallet import HDWallet
from hdwallet.mnemonics import BIP39Mnemonic
from hdwallet.cryptocurrencies import Bitcoin
from hdwallet.hds import BIP84HD
from hdwallet.derivations import BIP84Derivation
# A well-known BIP-39 test phrase — never send real funds to it
phrase = ("abandon abandon abandon abandon abandon abandon "
"abandon abandon abandon abandon abandon about")
wallet = HDWallet(
cryptocurrency=Bitcoin, hd=BIP84HD, network=Bitcoin.NETWORKS.MAINNET
).from_mnemonic(BIP39Mnemonic(mnemonic=phrase)).from_derivation(
BIP84Derivation(coin_type=Bitcoin.COIN_TYPE, account=0, address=0)
)
print(wallet.address()) # bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
This phrase is a public BIP-39 test vector, so it always derives bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu on the native SegWit path. Never send real funds to a test phrase — anyone can derive its keys. Replace it with your own words to restore a real wallet.
Connecting Your Wallet to the Bitcoin Network
Everything so far happens offline. Deriving keys and addresses needs no internet at all, which is what makes cold storage possible. But a usable wallet also has to read the chain and write to it — check a balance, list the unspent outputs (UTXOs) behind an address, and broadcast a signed transaction.
That requires a Bitcoin node. You can run your own, but a full node stores the entire chain — hundreds of gigabytes that only grows — and has to stay synced through every upgrade. The alternative is connecting to a node that someone else maintains, over its API.
This is where NOWNodes fits. Bitcoin node and API access gives your Python wallet an endpoint for standard JSON-RPC calls, so you can broadcast transactions without operating a node yourself. For address-level reads — balances, transaction history, UTXOs — the indexed Blockbook interface suits the job better than raw RPC, since it is built for exactly those lookups. You can also sanity-check any derived address in a Bitcoin block explorer before you use it. And because one seed can hold keys across many networks, the same accounts can reach several chains through one provider.
Security Notes for Python-Generated Wallets

A wallet is only as safe as its seed. A few rules matter more than the rest:
- Trust the library’s randomness. hdwallet and bitcoinlib draw entropy from your operating system’s secure generator. Never invent your own “random” source, and never reuse a phrase you found online.
- Keep the seed off the network. Don’t print it to logs, paste it into a chatbot, or commit it to a repository. Treat the file that holds it like cash.
- Test with testnet or tiny amounts first. Set the network to testnet while you confirm your derivation paths produce the addresses you expect.
- Guard extended keys. An xprv is as powerful as the seed itself, and an xpub exposes your whole address history.
Never share your seed phrase or extended private key. Anyone who has them controls every coin the wallet can ever hold.
Conclusion
A Bitcoin HD wallet turns one seed phrase into an unlimited, organized set of keys, and the standards behind it — BIP-32 for the tree, BIP-39 for the words, BIP-44 and its siblings for the paths — are stable and widely supported. In Python, a library like hdwallet reduces the whole thing to a few lines, whether you are generating a new wallet or restoring one from an existing mnemonic.
Generating keys is only half the job. To check balances and move funds, the wallet still has to reach the Bitcoin network — through your own node or a provider’s API — the bridge between the keys on your machine and the coins on the chain.
FAQ
Is it safe to generate a Bitcoin wallet with Python?
Yes, if you use a trusted library and keep the seed offline. The risk is almost never the math and almost always the handling — logging the phrase, running on a compromised machine, or sharing an extended key. For anything holding meaningful value, generate the wallet on an offline computer.
Which BIP standard should I use for a new Bitcoin wallet?
Native SegWit (BIP-84), which produces bc1q addresses, is the common default because it has the lowest transaction fees among the widely supported formats. Taproot (BIP-86), with bc1p addresses, is newer and improves privacy and efficiency but is not accepted everywhere yet. Legacy (BIP-44) remains the most universally compatible.
Can I restore my wallet in a different app from the same phrase?
Usually, as long as both apps use the same derivation path. The mnemonic alone isn’t always enough — a phrase created on a BIP-84 path won’t show the same addresses in an app that defaults to BIP-44. Match the path, and the addresses line up.
Can two people accidentally generate the same seed phrase?
For any practical purpose, no. A 12-word phrase encodes 128 bits of entropy, which is about 3.4 × 10³⁸ possibilities — vastly more than the number of grains of sand on Earth. A collision from proper random generation is not something you need to plan around.



