🐍 Python₿ Crypto

Crypto Wallet Address Validation

Validate Bitcoin and Ethereum wallet addresses in Python. Detect address types (P2PKH, P2SH, bech32), verify EIP-55 checksums, and run parallel checks with asyncio.gather.

Also available in Node.js

1. Validate Bitcoin address

Use await iv.btc_address(value) (SDK) or GET /v0/btc-address?value=….

{
  "valid": true,
  "type": "P2WPKH"
}
typePrefixDescription
P2PKH1…Legacy — highest fees
P2SH3…Script hash — SegWit-wrapped
P2WPKHbc1q… (42)Native SegWit — lowest fees
P2WSHbc1q… (62)Native SegWit script hash
  1. Check valid
  2. Use type for fee estimation
  3. Warn users with P2PKH about higher fees

2. Validate Ethereum address

Use await iv.eth_address(value) (SDK) or GET /v0/eth-address?value=….

{
  "valid": true,
  "address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
  "isChecksumValid": true
}
  1. Check valid
  2. Check is_checksum_valid — EIP-55
  3. Normalise to checksummed form before storing
⚠️All-lowercase ETH addresses pass validation but are not checksummed. Normalise to EIP-55 mixed-case before storage to prevent duplicates.

3. Parallel validation with asyncio.gather

import asyncio
import re
from isvalid_sdk import IsValidConfig, create_client

config = IsValidConfig(api_key="YOUR_API_KEY")
iv = create_client(config)

def detect_chain(address: str) -> str:
    if re.match(r"^0x[0-9a-fA-F]{40}$", address):
        return "ETH"
    if re.match(r"^(1|3)[1-9A-HJ-NP-Za-km-z]{25,34}$", address):
        return "BTC"
    if re.match(r"^bc1[a-z0-9]{6,}$", address):
        return "BTC"
    return "unknown"

async def validate_wallet(chain: str, address: str):
    warnings = []
    if chain == "BTC":
        result = await iv.btc_address(address)
        if result.valid and result.type == "P2PKH":
            warnings.append("Legacy P2PKH — consider migrating to bech32 for lower fees")
        return {"chain": chain, "valid": result.valid, "type": result.type, "warnings": warnings}
    elif chain == "ETH":
        result = await iv.eth_address(address)
        if result.valid and not result.is_checksum_valid:
            warnings.append("Address lacks EIP-55 checksum casing — normalise before storage")
        return {"chain": chain, "valid": result.valid,
                "address": result.address, "checksumValid": result.is_checksum_valid,
                "warnings": warnings}
    else:
        raise ValueError(f"Unknown chain for address: {address}")

async def validate_both(btc: str, eth: str):
    btc_r, eth_r = await asyncio.gather(
        validate_wallet("BTC", btc),
        validate_wallet("ETH", eth),
    )
    return {"btc": btc_r, "eth": eth_r}

async def main():
    result = await validate_both(
        "1A1zP1eP5QGefi2DMPTfTL5SLmv7Divf",
        "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    )
    print(f"BTC valid: {result['btc']['valid']}, type: {result['btc']['type']}")
    print(f"ETH valid: {result['eth']['valid']}, checksum: {result['eth']['checksumValid']}")

asyncio.run(main())

4. Edge cases

Auto-detect chain from address format

import re

def detect_chain(address: str) -> str:
    if re.match(r"^0x[0-9a-fA-F]{40}$", address):
        return "ETH"
    if re.match(r"^(1|3)[1-9A-HJ-NP-Za-km-z]{25,34}$", address):
        return "BTC"
    if re.match(r"^bc1[a-z0-9]{6,}$", address):
        return "BTC"
    return "unknown"

ETH checksum normalisation

# Using eth-utils or web3.py for EIP-55 normalisation
from eth_utils import to_checksum_address

result = await iv.eth_address(user_input)
if result.valid and not result.is_checksum_valid:
    normalised = to_checksum_address(user_input.lower())
    # Store normalised form

BTC testnet rejection

ℹ️Testnet addresses (prefix m, n, or tb1) will fail validation against the mainnet endpoint. Reject any address that returns valid: False.

5. Summary checklist

Detect chain from address format before API call
Check BTC type for fee estimation
Warn users about legacy P2PKH higher fees
Validate ETH is_checksum_valid for EIP-55
Normalise ETH to checksum form before storage
Run BTC + ETH with asyncio.gather
Reject testnet addresses in production
Return 422 with field-level error on invalid addresses

See also

Ready to integrate?

Free tier — 1,000 requests/month. No credit card required.

Get your API key →