L402 client for AI agents — pay Lightning invoices automatically to access paywalled APIs
Project description
bolthub
L402 client for AI agents. Automatically handles 402 Payment Required challenges, pays Lightning invoices, and retries requests with proof of payment.
Install
pip install bolthub
# Optional: Nostr Wallet Connect (NwcWallet.from_uri)
pip install 'bolthub[nwc]'
The only required runtime dependency is httpx. NWC support pulls in
websockets and cryptography via the nwc extra.
Quick Start
from bolthub import L402Client, LndWallet
wallet = LndWallet(
host="https://your-lnd-node:8080",
macaroon="0201036c6e...",
)
client = L402Client(wallet, budget_sats=10_000)
resp = client.get(
"https://acme.gw.bolthub.ai/v1/market-data",
params={"symbol": "BTC"},
)
data = resp.json()
Wallet Adapters
LND (recommended)
Full Lightning node. Self-host or use the bolthub Node Launcher, Umbrel, or Start9. Fastest payment path (<200ms) and full control.
from bolthub import LndWallet
wallet = LndWallet(
host="https://your-lnd-node:8080",
macaroon="admin-macaroon-hex",
timeout_seconds=30,
)
For agent deployments, use a scoped pay-only macaroon instead of admin.macaroon:
lncli bakemacaroon uri:/lnrpc.Lightning/SendPaymentSync \
uri:/lnrpc.Lightning/DecodePayReq \
--save_to=pay-only.macaroon
NWC (Nostr Wallet Connect)
Easiest to set up but slower (1-3s per payment). No node required. Get a free NWC connection from CoinOS or use Alby Hub, Zeus, or Primal.
Configure directly from the connection URI (requires the nwc extra,
pip install 'bolthub[nwc]'):
from bolthub import NwcWallet
wallet = NwcWallet.from_uri(
"nostr+walletconnect://<wallet_pubkey>?relay=wss://relay.example.com&secret=<hex>"
)
For the async client, use AsyncNwcWallet.from_uri(...). You can still pass your
own callback if you prefer to drive NWC yourself:
wallet = NwcWallet(pay_fn=lambda bolt11: my_nwc_pay(bolt11)) # returns preimage hex
LNbits
Supported if you already run LNbits. Multi-wallet accounts system; create a dedicated wallet for your agent.
from bolthub import LnbitsWallet
wallet = LnbitsWallet(
url="https://lnbits.example.com",
admin_key="your-admin-key",
)
Phoenixd
Supported if you already run Phoenixd for outbound payments.
from bolthub import PhoenixdWallet
wallet = PhoenixdWallet(
url="https://your-phoenixd:9740",
password="your-phoenixd-password",
timeout_seconds=35,
)
Custom Wallet
Implement the WalletAdapter protocol:
class MyWallet:
def pay_invoice(self, bolt11: str) -> str:
preimage = my_payment_logic(bolt11)
return preimage
Async
AsyncL402Client mirrors L402Client on httpx.AsyncClient. Existing
(synchronous) wallets work unchanged — they are run in a worker thread — or use
the async adapters (AsyncLndWallet, AsyncLnbitsWallet, AsyncPhoenixdWallet,
AsyncNwcWallet) for a fully non-blocking path.
from bolthub import AsyncL402Client, LndWallet
async def main():
async with AsyncL402Client(LndWallet(host=host, macaroon=mac), budget_sats=10_000) as client:
resp = await client.get("https://acme.gw.bolthub.ai/v1/market-data")
return resp.json()
Use with your own httpx client (L402Auth)
L402Auth plugs the L402 flow into a client you own, so you keep your transport,
pooling, and retries. It works with both httpx.Client and httpx.AsyncClient:
import httpx
from bolthub import L402Auth, LndWallet
auth = L402Auth(LndWallet(host=host, macaroon=mac), budget_sats=10_000)
with httpx.Client(auth=auth) as client:
resp = client.get("https://acme.gw.bolthub.ai/v1/market-data")
print(auth.total_spent)
Budget Guards
client = L402Client(
wallet,
max_per_request_sats=100, # reject invoices over 100 sats
budget_sats=10_000, # total spending cap
)
print(client.total_spent) # sats spent so far
print(client.remaining_budget) # sats remaining
The price of each invoice is determined from the response body (amountSats),
the BOLT11 invoice itself, or an optional price_header. If it still cannot be
determined, on_unknown_amount controls what happens — by default ("cap") the
client pays only up to max_per_request_sats and refuses outright if no ceiling
is set, so a price-less challenge is never paid blind. Use "refuse" to always
refuse, or "allow" for the legacy pay-blind behaviour.
Thread Safety
A single L402Client (or L402Auth) may be shared across threads. Budget
accounting is atomic, so total_spent is always exact and the budget is never
exceeded under concurrent requests; the lock is held only around the budget
check, not across the network or payment, so requests still run in parallel.
Session Persistence
By default sessions are kept in memory. Use FileSessionStore to persist
tokens across process restarts (stored in ~/.bolthub/sessions.json):
from bolthub import L402Client, LndWallet, FileSessionStore
client = L402Client(
LndWallet(host=host, macaroon=macaroon),
session_store=FileSessionStore(),
)
API Reference
| Export | Description |
|---|---|
L402Client |
HTTP client with automatic L402 challenge handling |
AsyncL402Client |
Async client on httpx.AsyncClient |
L402Auth |
httpx.Auth for plugging L402 into your own client |
LndWallet / AsyncLndWallet |
Wallet adapter for LND REST API |
LnbitsWallet / AsyncLnbitsWallet |
Wallet adapter for LNbits |
PhoenixdWallet / AsyncPhoenixdWallet |
Wallet adapter for Phoenixd |
NwcWallet / AsyncNwcWallet |
NWC wallet; from_uri(...) for NIP-47 (needs bolthub[nwc]) |
SyncWalletAdapter |
Run a sync wallet under the async client |
WalletAdapter / AsyncWalletAdapter |
Protocols for custom wallets |
FileSessionStore / InMemorySessionStore |
Session token storage |
SessionStore |
Protocol for custom session storage |
L402Error |
Base exception for L402 failures |
L402BudgetError |
Raised when budget limits are exceeded |
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bolthub-0.2.0.tar.gz.
File metadata
- Download URL: bolthub-0.2.0.tar.gz
- Upload date:
- Size: 30.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
857c6f74af8396da997ba58ee62dda1229b0a6e03da760ba8d8dbd1f61041c11
|
|
| MD5 |
c5f4701da10c3fb403a6aa523f5697bc
|
|
| BLAKE2b-256 |
46db4b6b2d63f66fd254d5c3f9ee59916fa50d07a5894889edab0807386ad276
|
Provenance
The following attestation bundles were made for bolthub-0.2.0.tar.gz:
Publisher:
publish.yml on signaltech-org/bolthub-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bolthub-0.2.0.tar.gz -
Subject digest:
857c6f74af8396da997ba58ee62dda1229b0a6e03da760ba8d8dbd1f61041c11 - Sigstore transparency entry: 1883291556
- Sigstore integration time:
-
Permalink:
signaltech-org/bolthub-sdk@44ca82efbd0f96c45817d44d36a85a5e80dbbe49 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/signaltech-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@44ca82efbd0f96c45817d44d36a85a5e80dbbe49 -
Trigger Event:
push
-
Statement type:
File details
Details for the file bolthub-0.2.0-py3-none-any.whl.
File metadata
- Download URL: bolthub-0.2.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c108b2173ef45805e70483812f00cb225dfc675ad46f02cfdff85c071e1b98b6
|
|
| MD5 |
c8184015d4042c9baed426f4015366cf
|
|
| BLAKE2b-256 |
4d625e701e5fdf1bbdf66f04c11862b7c34e0e4d55e401c40b19fd6bf955861d
|
Provenance
The following attestation bundles were made for bolthub-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on signaltech-org/bolthub-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bolthub-0.2.0-py3-none-any.whl -
Subject digest:
c108b2173ef45805e70483812f00cb225dfc675ad46f02cfdff85c071e1b98b6 - Sigstore transparency entry: 1883291702
- Sigstore integration time:
-
Permalink:
signaltech-org/bolthub-sdk@44ca82efbd0f96c45817d44d36a85a5e80dbbe49 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/signaltech-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@44ca82efbd0f96c45817d44d36a85a5e80dbbe49 -
Trigger Event:
push
-
Statement type: