Skip to main content

Pay for x402 (HTTP 402) APIs privately over SubEtha's zERC20 rail — Python payer client

Project description

subetha (Python) — private x402 payments for Python agents

Pay for x402 (HTTP 402) paywalled APIs over SubEtha's zERC20 rail so that no on-chain trail links the payer to the payee: the burn itself is public (amounts stay transparent), but nothing on-chain connects it to the provider's treasury. This is the Python counterpart of the TypeScript agent tools (docs/AGENT-TOOLS.md): the same wire protocol (docs/PROTOCOL.md — this package is a reference implementation written against that spec), the same spending-policy semantics.

Status

Experimental (0.x). The API may change in breaking ways between 0.x minor releases while integrations (agent frameworks, plugins) shape it. If you depend on this package, pin a minor: subetha>=0.1,<0.2. Stability will be declared at 1.0. Changes are recorded in CHANGELOG.md.

Scope

This package is the payer side only: decode a 402 offer, decide under an operator-configured spending policy, sign, pay, report. That is a deliberate, stable boundary — it is also what the API-stability promise will cover at 1.0.

The provider, facilitator, and settlement layers are TypeScript and live in the same repository; there is no plan to port them. Paying touches no zk code and no BUSL code: this package depends only on httpx and eth-account.

Install

pip install subetha

For development (from a repository checkout):

uv pip install -e python/          # from the repository root
# or: pip install -e python/

Dependencies: httpx, eth-account (no web3.py). Python ≥ 3.11.

Use

from subetha import SubethaClient, SpendingPolicy

client = SubethaClient(
    private_key="0x…",                    # the paying account (permit: only signs, no gas)
    rpc_url="http://127.0.0.1:8545",
    mode="permit",                         # gasless (default); or "self-transfer"
    policy=SpendingPolicy(
        allowed_hosts=["127.0.0.1"],       # first line of defense — keep it tight
        max_per_payment=100_000,           # token base units
        max_total=5_000_000,
    ),
    approve_above=10_000,                                  # optional human-in-the-loop
    approve_payment=lambda info: ask_human(info),          # True = approve
)

q = client.quote("http://127.0.0.1:4031/api/complete", method="POST", body='{"prompt":"hi"}')
print(q.offer.quoted, q.offer.fee, q.offer.approval_required)

r = client.pay("http://127.0.0.1:4031/api/complete", method="POST", body='{"prompt":"hi"}')
print(r.status, r.payment.amount, r.resource)

print(client.report())                     # totals / payments / attempts (incl. approvals)

SubethaClient.from_env() reads the same SUBETHA_* variables as the TypeScript tools (SUBETHA_PAYER_PK, SUBETHA_RPC_URL, SUBETHA_MODE, SUBETHA_ALLOWED_HOSTS, SUBETHA_ALLOWED_NETWORK, SUBETHA_ALLOWED_TOKEN, SUBETHA_MAX_PER_PAYMENT, SUBETHA_MAX_TOTAL, SUBETHA_APPROVE_ABOVE, SUBETHA_APPROVAL_TIMEOUT_MS, SUBETHA_TIMEOUT_MS).

Semantics you can rely on

  • Policy before money: host allowlist before any request; per-payment / total caps and network/token pins at offer selection. Refusals raise SubethaError with the reason; nothing is paid.
  • Approval: payments quoted above approve_above call approve_payment (bounded by approval_timeout_s, default 120 s). Decline / timeout / a raising callback / no callback ⇒ refused. The approval is bound to the exact offer.
  • Accounting at the point of no return: a self-transfer burn is counted the moment it lands; a permit reservation is released only when settle is definitively rejected.
  • One payment at a time; errors are redacted (the key never appears in messages).
  • mode="permit" fail-fasts when the offer lacks the gasless path — it never silently degrades to a gas-paying transfer.

Key management & safety

This client signs with a raw private key. Treat that as the threat model:

  • At runtime the key exists decrypted in process memory. Encrypted config or secret stores protect the key at rest — anything that can read your agent process (or its crash dumps) can read the key. Plan accordingly.
  • Use a dedicated hot wallet. Fund the paying account with small amounts, top it up as needed, and keep it separate from any treasury or personal wallet. If it leaks, the loss is bounded by the balance and your max_total.
  • The spending policy is the operator's, not the agent's. Allowlist, caps, and approve_above are configuration the human sets; agents cannot raise their own limits. Keep allowed_hosts as tight as your deployment allows.
  • Redaction is scoped. This package keeps the key out of its own errors and reports; whatever you log around it (request dumps, env printouts) is your responsibility.
  • Report vulnerabilities privately via GitHub Security Advisories (see SECURITY.md).

LangChain (Python) example

No extra dependency in this package — wrap the client yourself:

from langchain_core.tools import tool
from subetha import SubethaClient, SubethaError

client = SubethaClient.from_env()

@tool
def subetha_x402_pay(url: str, method: str = "GET", body: str | None = None) -> str:
    """Pay for an x402 resource privately under the configured spending policy."""
    try:
        r = client.pay(url, method=method, body=body)
        return f"HTTP {r.status}; paid {r.payment.amount if r.payment else 0}; {r.resource[:2000]}"
    except SubethaError as e:
        return f"ERROR: {e}"   # let the agent read the refusal instead of crashing

Tests

uv run --project python pytest python/tests -q          # unit (no chain needed)
# cross-language live e2e (real stack + the TypeScript provider/facilitator):
PERMIT_BURNER_ADDRESS=0x… pnpm --filter @subetha/demo-official exec tsx ../../python/scripts/live-e2e.ts

License

Apache-2.0 (this package). Note that operating a SubEtha facilitator commercially / in production requires a separate use grant from the zERC20 team — see the License section of the repository README.

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

subetha-0.2.0.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

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

subetha-0.2.0-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

Details for the file subetha-0.2.0.tar.gz.

File metadata

  • Download URL: subetha-0.2.0.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for subetha-0.2.0.tar.gz
Algorithm Hash digest
SHA256 dd6368683e0f9329e7f43996128974dfa17267aa3afe4e82dcb3261ad4e33e49
MD5 1e7b4c3a51bc00845cba1aa0e8a8d5c5
BLAKE2b-256 4848a7c2f2160e2c50d6326d5c06fcfe86fa118690a65fab37ac9cb2acc7ba0f

See more details on using hashes here.

Provenance

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

Publisher: python-release.yml on peaceandwhisky/SubEtha

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

File details

Details for the file subetha-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: subetha-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 23.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for subetha-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9df5f92e4f503d9e76dd0a4250cee5945266f5f4339872edb1086c736c8fa2c4
MD5 8ce1f04d647041b8614b7188d592c60a
BLAKE2b-256 111df4cb053c8c662cf5f4bef8e3d3387a8d6ea7712315de7f0915289f276519

See more details on using hashes here.

Provenance

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

Publisher: python-release.yml on peaceandwhisky/SubEtha

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