Bitcoin has recently surged past $70,500, reaching new all-time highs. While many investors are jumping into the market, not everyone is ready—or able—to trade at these elevated levels. If you're more interested in understanding the underlying technology than speculating on price, generating your own Bitcoin keys and addresses using Python is a powerful way to learn.
In this guide, we’ll walk through how to use Python to generate Bitcoin private keys, public keys, and different types of wallet addresses—including legacy (starting with 1), P2SH-segwit (starting with 3), and native segwit (starting with bc1). You'll also learn how to work with popular libraries like bit, python-bitcoin-utils, and best practices for secure key generation.
Understanding Bitcoin Key Generation
At the core of every Bitcoin wallet lies a cryptographic key pair:
- Private Key: A secret number that allows you to spend or transfer funds.
- Public Key: Derived from the private key; used to generate your receiving address.
- Address: A hashed version of the public key, safe to share publicly.
These components form the foundation of Bitcoin’s security model based on elliptic curve cryptography (ECDSA).
👉 Generate secure Bitcoin keys and explore wallet tools today.
Generate a Legacy Bitcoin Address (Starts with "1")
Legacy addresses (P2PKH) begin with the digit 1. Here's how to create one using the bitcoin library:
from bitcoin import *
# Generate a random private key
my_private_key = random_key()
# Derive public key
my_public_key = privtopub(my_private_key)
# Create Bitcoin address
my_bitcoin_address = pubtoaddr(my_public_key)
print(f"Private Key: {my_private_key}")
print(f"Public Key: {my_public_key}")
print(f"Address: {my_bitcoin_address}")⚠️ Note: This will consistently produce addresses starting with1. Thebitcoinlibrary is simple but limited in supporting newer address formats.
You can also generate such keys offline using bitaddress.org by saving the site locally—ideal for enhanced security.
Generate a P2SH-SegWit Address (Starts with "3")
To get an address starting with 3, which uses Pay-to-Script-Hash (P2SH) wrapped SegWit format, we need a more modern library—like bit.
Install it via:
pip install bitHere’s the working code:
from bit import Key
# Generate a new private key
my_private_key = Key()
# Access corresponding public key
my_public_key = my_private_key.public_key
# Get SegWit-compatible P2SH address (starts with '3')
my_bitcoin_address = my_private_key.segwit_address
print(f"Private Key (WIF): {my_private_key}")
print(f"Public Key: {my_public_key.hex()}")
print(f"P2SH-SegWit Address: {my_bitcoin_address}")This outputs a compressed private key in Wallet Import Format (WIF), its public key, and a 3... address. This format offers lower fees and better scalability than legacy addresses.
Generate a Native SegWit Address (Starts with "bc1")
Native SegWit (Bech32) addresses start with bc1 and offer the best transaction efficiency and fee savings. We’ll use the python-bitcoin-utils library for full control.
Install it:
pip install python-bitcoin-utilsNow, configure for mainnet and generate a Bech32 address:
from bitcoinutils.setup import setup
from bitcoinutils.keys import PrivateKey, PublicKey
# Always set up the network first
setup("mainnet")
# Create a private key (for demo; never use fixed values in production!)
priv = PrivateKey(secret_exponent=1) # Use random values in real apps
print("Private Key (WIF):", priv.to_wif(compressed=True))
# Derive public key
pub = priv.get_public_key()
print("Public Key:", pub.to_hex(compressed=True))
# Generate native SegWit (Bech32) address
address = pub.get_segwit_address()
print("Bech32 Address:", address.to_string())
# Signing messages still works
message = "Hello, Bitcoin!"
signature = priv.sign_message(message)
print("Signature:", signature)
# Verify signature
if PublicKey.verify_message(address.to_string(), signature, message):
print("The signature is valid!")
else:
print("The signature is NOT valid!")🔒 Important: Avoid hardcoding secret_exponent=1. Always use cryptographically secure randomness in production.Note: When using get_segwit_address(), you’ll get a P2wpkhAddress object. It does not have a to_hash160() method—this is expected behavior. Instead, use address.to_bytes() or other built-in methods for hash extraction.
Testnet vs Regtest vs Mainnet: What’s the Difference?
Understanding Bitcoin networks is essential when developing or testing applications:
- Mainnet: The live Bitcoin blockchain where real transactions occur. Coins have monetary value.
- Testnet: A public test network mirroring mainnet rules. Used for development. Test BTC have no value.
- Regtest: A local private blockchain for offline testing. Ideal for automated tests and learning.
Address prefixes differ across networks:
- Mainnet:
1,3,bc1 - Testnet/Regtest:
m,n,2,tb1
Wallets detect network type automatically, but sending funds to a testnet address by mistake on mainnet results in permanent loss.
Bitcoin Core GUI (Bitcoin-Qt) visually distinguishes networks:
- Mainnet: Orange icon
- Testnet: Green icon
- Regtest: Blue icon
👉 Learn how different Bitcoin networks support secure development and testing.
Frequently Asked Questions (FAQ)
Q: Can I generate Bitcoin keys without internet access?
Yes. Libraries like bit and python-bitcoin-utils run entirely offline. For extra security, generate keys on an air-gapped machine.
Q: Is it safe to use hardcoded secret exponents like secret_exponent=1?
No. Hardcoded values are predictable and insecure. Use secure randomness:
import os
from bitcoinutils.keys import PrivateKey
rand_bytes = os.urandom(32)
priv = PrivateKey(secret_exponent=int.from_bytes(rand_bytes, 'big'))Q: Why can't I call .to_hash160() on a SegWit address?
Because native segwit (P2wpkhAddress) doesn’t expose that method directly. Hash160 is embedded in scriptPubKey logic. Use .to_script_pub_key() or consult advanced examples in the docs.
Q: What happens if I lose my private key?
You lose access to any funds sent to its associated address. There's no recovery mechanism—this is why backups and secure storage are critical.
Q: Can I import these generated keys into a wallet?
Yes. Most wallets support importing WIF-formatted private keys. Just ensure the wallet supports the corresponding address type (legacy, P2SH, or Bech32).
Q: Are these libraries actively maintained?
Yes:
bit: Actively updated, simple API.python-bitcoin-utils: Well-documented, ideal for learning and advanced scripting.
Final Thoughts
Generating Bitcoin keys and addresses programmatically gives you deeper insight into how wallets work under the hood. Whether you're building tools, experimenting with cryptography, or just curious about blockchain mechanics, Python offers accessible libraries to explore safely.
Remember: never expose private keys online, avoid hardcoded values, and always test on regtest or testnet before touching mainnet.
👉 Start exploring secure crypto development with trusted tools.
With proper practices, you can build robust applications while understanding the foundational elements of Bitcoin’s decentralized architecture.