Payer quickstart — Python
The subetha package on PyPI is a payer-only reference client: it decodes
a 402 offer, decides under an operator-configured spending policy, signs,
pays, and keeps an audit trail. It touches no zk code and no BUSL code — its
only dependencies are httpx, eth-account, and eth-utils. Everything on
this page targets the local, non-production stack: loopback URLs, chain
31337, placeholder keys. The code mirrors the pinned package README; the flow
expectations describe what the pinned sources define — the
local end-to-end tutorial is where you actually run
them.
1. Install
Section titled “1. Install”pip install subethaRequires Python >= 3.11; Apache-2.0. The installable version at ship time is
0.2.0 (registry-verified 2026-07-27), and the package is experimental
(0.x) — the API may change in breaking ways between 0.x minor releases.
Note one documented source divergence: the package README at the pinned
commit still recommends the pin subetha>=0.1,<0.2, which excludes 0.2.0 —
treat 0.2.0 as current and that pin line as stale (details in the
Python API reference).
2. Configure the client with a spending policy
Section titled “2. Configure the client with a spending policy”The SpendingPolicy is the safety layer: it is operator-configured, never
agent-configured — hosts, network, token, and budget caps come from you (or
the SUBETHA_* environment), not from anything an agent says at runtime.
from subetha import SubethaClient, SpendingPolicy
client = SubethaClient( private_key="0x…", # placeholder — local anvil dev account only rpc_url="http://127.0.0.1:8545", # local anvil (chain 31337) mode="permit", # gasless default; or "self-transfer" policy=SpendingPolicy( allowed_hosts=["127.0.0.1"], # first line of defense — keep it tight allowed_network="eip155:31337", max_per_payment=100_000, # token base units max_total=5_000_000, # per network|token pair ), approve_above=10_000, # optional human-in-the-loop threshold approve_payment=lambda info: ask_human(info), # True = approve)Alternatively, SubethaClient.from_env() builds the client from the same
SUBETHA_* variables the TypeScript tools read:
# Placeholders — local dev values only; never commit a real key.export SUBETHA_PAYER_PK=0x…export SUBETHA_RPC_URL=http://127.0.0.1:8545export SUBETHA_ALLOWED_HOSTS=127.0.0.1export SUBETHA_MAX_PER_PAYMENT=100000export SUBETHA_MAX_TOTAL=5000000The full variable table (defaults included) is in the configuration reference.
3. Quote, pay, report
Section titled “3. Quote, pay, report”url = "http://127.0.0.1:4031/api/complete" # a locally running provider
# Quote: fetch the offer without paying — never needs the private key.q = client.quote(url, method="POST", body='{"prompt":"hi"}')print(q.offer.quoted, q.offer.fee, q.offer.approval_required)
# Pay: policy gate → 402 → offer selection → optional approval → sign → paid request.r = client.pay(url, method="POST", body='{"prompt":"hi"}')print(r.status, r.payment.amount, r.resource)
# Report: totals, payments, and attempts (including approval events).print(client.report())What a successful pay() means in flow terms: the first request drew a
402 offer, the client paid it, and the re-sent request came back
accepted — resource served, settlement acknowledged on the request path.
Finalization (the proof-gated mint) happens later, asynchronously, on the
facilitator side; no client response ever reports finalized
(accepted vs finalized).
Safety behavior you get for free
Section titled “Safety behavior you get for free”Guarantees stated by the pinned client module and README:
- Host allowlist before any request leaves — and redirects are refused, so a payment cannot be steered onto a host you did not allowlist.
- Budget accounting at the irreversibility point. A definitive settle rejection on the permit path releases the reservation (no funds moved); a paid request that then fails with an HTTP error keeps the spend counted, on the safe side.
- One payment at a time (internal lock); reserved
PAYMENT-*/X-PAYMENTheaders in your own header dict are rejected. - Redacted errors — every failure normalizes to
SubethaErrorwith secrets scrubbed from the message.
Next steps
Section titled “Next steps”- Local end-to-end tutorial — run the full 402 → payment → accepted → finalized flow against the local stack.
- Python API reference — every exported name, dataclass fields, and failure semantics.
- Provider quickstart — the service on the other side of the 402.