Skip to main content

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

bolthub-0.2.0.tar.gz (30.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bolthub-0.2.0-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

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

Hashes for bolthub-0.2.0.tar.gz
Algorithm Hash digest
SHA256 857c6f74af8396da997ba58ee62dda1229b0a6e03da760ba8d8dbd1f61041c11
MD5 c5f4701da10c3fb403a6aa523f5697bc
BLAKE2b-256 46db4b6b2d63f66fd254d5c3f9ee59916fa50d07a5894889edab0807386ad276

See more details on using hashes here.

Provenance

The following attestation bundles were made for bolthub-0.2.0.tar.gz:

Publisher: publish.yml on signaltech-org/bolthub-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for bolthub-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c108b2173ef45805e70483812f00cb225dfc675ad46f02cfdff85c071e1b98b6
MD5 c8184015d4042c9baed426f4015366cf
BLAKE2b-256 4d625e701e5fdf1bbdf66f04c11862b7c34e0e4d55e401c40b19fd6bf955861d

See more details on using hashes here.

Provenance

The following attestation bundles were made for bolthub-0.2.0-py3-none-any.whl:

Publisher: publish.yml on signaltech-org/bolthub-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page