<!-- Generated raw Markdown for /quickstarts/payer-python; product pin 638fa1e9ca7face19a442cef43754f0d627c7705. -->

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](/tutorial/local-flow/) is where you actually run
them.

## 1. Install

```bash
pip install subetha
```

Requires 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](/reference/python/)).

## 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.

```python
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:

```bash
# Placeholders — local dev values only; never commit a real key.
export SUBETHA_PAYER_PK=0x…
export SUBETHA_RPC_URL=http://127.0.0.1:8545
export SUBETHA_ALLOWED_HOSTS=127.0.0.1
export SUBETHA_MAX_PER_PAYMENT=100000
export SUBETHA_MAX_TOTAL=5000000
```

The full variable table (defaults included) is in the
[configuration reference](/reference/configuration/).

## 3. Quote, pay, report

```python
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](/concepts/accepted-vs-finalized/)).

## 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-PAYMENT` headers in your own header dict are rejected.
- **Redacted errors** — every failure normalizes to `SubethaError` with
  secrets scrubbed from the message.

## Next steps

- [Local end-to-end tutorial](/tutorial/local-flow/) — run the full
  402 → payment → accepted → finalized flow against the local stack.
- [Python API reference](/reference/python/) — every exported name, dataclass
  fields, and failure semantics.
- [Provider quickstart](/quickstarts/provider/) — the service on the other
  side of the 402.
