Skip to main content

Auto-paying L402 HTTP client — APIs behind Lightning paywalls just work

Project description

l402-requests

Auto-paying L402 HTTP client for Python. APIs behind Lightning paywalls just work.

l402-requests wraps httpx and automatically handles HTTP 402 responses by paying Lightning invoices and retrying with L402 credentials. It's a drop-in HTTP client where any API behind an L402 paywall "just works."

Install

pip install l402-requests

Quick Start

import l402_requests

# Any L402-protected API just works — invoice is paid automatically
response = l402_requests.get("https://api.example.com/paid-resource")
print(response.json())

That's it. The library detects your wallet from environment variables, pays the Lightning invoice when it gets a 402 response, and retries with L402 credentials.

Wallet Configuration

Set environment variables for your preferred wallet. The library auto-detects in this order:

Priority Wallet Environment Variables Preimage Support
1 LND LND_REST_HOST, LND_MACAROON_HEX Yes
2 NWC NWC_CONNECTION_STRING Yes (CoinOS, CLINK)
3 Strike STRIKE_API_KEY Yes
4 OpenNode OPENNODE_API_KEY Limited

Recommended: Strike (0% fee, full preimage support, no infrastructure required).

Strike (Recommended)

export STRIKE_API_KEY="your-strike-api-key"

LND

export LND_REST_HOST="https://localhost:8080"
export LND_MACAROON_HEX="your-admin-macaroon-hex"
export LND_TLS_CERT_PATH="/path/to/tls.cert"  # optional

NWC (Nostr Wallet Connect)

pip install l402-requests[nwc]
export NWC_CONNECTION_STRING="nostr+walletconnect://pubkey?relay=wss://relay&secret=hex"

OpenNode

export OPENNODE_API_KEY="your-opennode-key"

Note: OpenNode does not return payment preimages, which limits L402 functionality. For full L402 support, use Strike, LND, or a compatible NWC wallet.

Budget Controls

Safety first — budgets are enabled by default to prevent accidental overspending:

from l402_requests import L402Client, BudgetController

# Custom budget limits
client = L402Client(
    budget=BudgetController(
        max_sats_per_request=500,     # Max per single payment (default: 1000)
        max_sats_per_hour=5000,       # Hourly rolling limit (default: 10000)
        max_sats_per_day=25000,       # Daily rolling limit (default: 50000)
        allowed_domains={"api.example.com"},  # Optional domain allowlist
    )
)

# Disable budgets entirely (not recommended)
client = L402Client(budget=None)

If a payment would exceed any limit, BudgetExceededError is raised before the payment is attempted.

Explicit Wallet

from l402_requests import L402Client, StrikeWallet

client = L402Client(
    wallet=StrikeWallet(api_key="your-key"),
)
response = client.get("https://api.example.com/paid-resource")

Async Support

from l402_requests import AsyncL402Client

async with AsyncL402Client() as client:
    response = await client.get("https://api.example.com/paid-resource")
    print(response.json())

Spending Introspection

Track every payment made during a session:

from l402_requests import L402Client

client = L402Client()
client.get("https://api.example.com/data")
client.get("https://api.example.com/more-data")

# Inspect spending
print(f"Total spent: {client.spending_log.total_spent()} sats")
print(f"Last hour: {client.spending_log.spent_last_hour()} sats")
print(f"By domain: {client.spending_log.by_domain()}")

# Export as JSON
print(client.spending_log.to_json())

How It Works

  1. Your code makes an HTTP request via L402Client
  2. If the server returns 200, the response is returned as-is
  3. If the server returns 402 with an L402 challenge:
    • The WWW-Authenticate: L402 macaroon="...", invoice="..." header is parsed
    • The BOLT11 invoice amount is checked against your budget
    • The invoice is paid via your configured Lightning wallet
    • The request is retried with Authorization: L402 {macaroon}:{preimage}
  4. Credentials are cached so subsequent requests to the same endpoint don't require re-payment

Two-Step L402 Flows (Commerce)

Some servers intentionally use a two-step L402 flow where payment and claim are separate endpoints. This is common for physical goods — it separates payment from fulfillment and allows the claim URL to be shared with a gift recipient.

For example, the Lightning Enable Store returns a 402 on POST /checkout, and after payment you claim the order at POST /claim with the L402 credential.

In these cases, l402-requests pays the invoice automatically. Use the spending_log to retrieve the preimage, then make the claim request:

from l402_requests import L402Client, BudgetController

client = L402Client(budget=BudgetController(max_sats_per_request=50000))
checkout = client.post("https://store.lightningenable.com/api/store/checkout",
    json={"items": [{"productId": 2, "quantity": 1, "size": "L", "color": "Black"}]})

# Payment was made — retrieve credentials from the spending log
record = client.spending_log.records[-1]
print(f"Paid {record.amount_sats} sats, preimage: {record.preimage}")

See the full documentation for the complete store purchasing example.

What is L402?

L402 (formerly LSAT) is a protocol for monetizing APIs with Lightning Network micropayments. Instead of API keys or subscriptions, servers return HTTP 402 ("Payment Required") with a Lightning invoice. Once paid, the client receives a credential (macaroon + payment preimage) that grants access.

Learn more: docs.lightningenable.com

License

MIT — see LICENSE.

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

l402_requests-0.1.0.tar.gz (22.4 kB view details)

Uploaded Source

Built Distribution

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

l402_requests-0.1.0-py3-none-any.whl (23.4 kB view details)

Uploaded Python 3

File details

Details for the file l402_requests-0.1.0.tar.gz.

File metadata

  • Download URL: l402_requests-0.1.0.tar.gz
  • Upload date:
  • Size: 22.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for l402_requests-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3adb9597191c1bba265677b32a6f0ba3ff0394ca02b8bfe3379ecf9af152f1f5
MD5 35a0d86c6f7d8936f94254d6e19959db
BLAKE2b-256 1630db8ce8288e9d906881bf28148caa57e868b2b7e0fcca57ba68cbd9092e50

See more details on using hashes here.

File details

Details for the file l402_requests-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: l402_requests-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 23.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for l402_requests-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7805b9806b4b98ecfbd4cc2e2b25f633a81838f6cb20a449aa75599c179b62d3
MD5 7bde788c3297c4f38e60307f068c3dc1
BLAKE2b-256 b1b7f51cbd81197c01652060311eca8458162d2277eeef2fa07368f89e6db9d3

See more details on using hashes here.

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