tx402
The spend-governance layer for x402, in Python. Spend caps and budgets enforced before any
key is signed, deterministic multi-chain routing, and a drop-in httpx wrapper for autonomous agents.
Feature-complete and at behavioural parity with the TypeScript SDK — both execute the same 88 language-neutral conformance fixtures.
What it does
An AI agent is a loop with a private key. Making one payment is easy — the hard part is trusting the
ten-thousandth, unattended: a per-request cap says nothing about the total, and a budget checked
after you've signed is a receipt, not a control. tx402 wraps a normal HTTP client and handles the
402 Payment Required handshake, with your spend policy as the first thing it enforces:
from tx402 import Policy, Tx402Client
client = Tx402Client(
evm_signer=evm_signer,
solana_signer=solana_signer,
policy=Policy(
max_per_request="0.50 USDC",
max_per_hour="10.00 USDC",
allowed_networks=["eip155:8453", "solana:mainnet"],
),
)
response = client.post(url, json={"prompt": "Hello"})
Under the hood, on a 402, it decodes the challenge strictly, enforces your spend policy
before any key is touched, deterministically ranks every route the merchant offered —
scored by balance, expected buyer fee, and local endpoint health — verifies the chain
identity on the RPC endpoint that serves the balance, reserves the spend atomically, asks
your signer for exactly one bounded authorization with a fresh nonce, and retries once with
it. A merchant that re-challenges gets a freshly parsed challenge and a new signature, up to
max_paid_attempts.
Both Tx402Client and AsyncTx402Client are provided, and both support Base (EIP-3009) and
Solana (SPL exact transfer).
Design commitments
- Non-custodial. The core API accepts signer abstractions, never raw private key strings. Private keys never leave your process, and are never logged or transmitted.
- Policy before signature. Budget caps, domain allowlists, and network allowlists are evaluated and the spend is reserved before a signer is ever invoked.
- Integer money. All amounts are integer atomic units.
floatmoney inputs are rejected, not coerced — a cap that rounds is not a cap. - No backend. No tx402-operated service, no telemetry, no phone-home. The only network calls are to the merchant you asked for and the RPC endpoints you configured.
- Same-chain only. It pays on a network the merchant offered and you can sign for. It will not
bridge or swap behind your back; if you can't pay, you get a typed
InsufficientLiquidityError. - The buyer never talks to a facilitator.
/verifyand/settleare the merchant's calls, not yours.
Sharing one budget across processes
Policy(max_per_hour=…) is enforced against a SpendStore. The default is in-memory and
therefore per-process; a fleet of agents that must share one hourly cap points at a durable one.
The Redis adapter ships in the box (pip install "tx402[redis]"):
from redis import Redis
from tx402 import Tx402Client
from tx402.stores.redis import RedisSpendStore
store = RedisSpendStore(Redis.from_url("redis://localhost:6379/0"))
client = Tx402Client(spend_store=store, ...) # every agent shares one authoritative cap
A gateway-backed store (tx402.stores.gateway, over an HTTP wire protocol) and a Cloudflare
Durable Object are the other reference backends; examples/python/fleet.py
is a runnable end-to-end fleet that shares one Redis budget across processes.
Writing your own? Implement the whole v2 SpendStore contract — a kind string, a
capabilities with atomic_global_freeze, reserve, the ref-based commit / release /
expose (each takes a ReservationRef, not a bare id), get_budget_state, list_exposed, and
is_frozen — then verify it with the shipped conformance suite before you construct a client
around it:
from tx402.spend_store_contract import check_spend_store
check_spend_store(
lambda: MyStore()
) # raises SpendStoreContractError on the first violation
check_spend_store runs the whole contract, including twenty concurrent reservations against a
five-unit cap, because the rule an adapter is most likely to break is that reserve must be
atomic. policy_scope is the normalized merchant host (normalize_policy_host(url)), so two
processes calling one merchant share one cap. See tx402.ledger.SpendStore for the full contract.
Install
pip install tx402 # core: protocol codec + HTTP transport
pip install "tx402[evm]" # + Base / EVM signing
pip install "tx402[svm]" # + Solana signing
pip install "tx402[all]" # everything
Requires Python 3.10+.
Parity with the TypeScript SDK
The TypeScript SDK (tx402 on npm) is the reference
implementation. Both are validated against the same language-neutral conformance fixtures, and a
behavioural change lands in both languages together or not at all.
License
Apache-2.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tx402-0.2.0.tar.gz.
File metadata
- Download URL: tx402-0.2.0.tar.gz
- Upload date:
- Size: 485.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2b3eb0feb1a063ce41e27fd4ed2e36e7ac0bf48789183d718fd6b154bc715ac
|
|
| MD5 |
b6ca3bb91b28fe4f40d38b5eecb9e998
|
|
| BLAKE2b-256 |
9c415fda7e3ffefd94f1b2021be8cf73180ccd14bcb908a1aadfd3eb5715b337
|
Provenance
The following attestation bundles were made for tx402-0.2.0.tar.gz:
Publisher:
release.yml on neogeeks/tx402
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tx402-0.2.0.tar.gz -
Subject digest:
d2b3eb0feb1a063ce41e27fd4ed2e36e7ac0bf48789183d718fd6b154bc715ac - Sigstore transparency entry: 2439494725
- Sigstore integration time:
-
Permalink:
neogeeks/tx402@25362265bdd3bcdd0bd8f0bfe5a5efa87d3da8f3 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/neogeeks
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@25362265bdd3bcdd0bd8f0bfe5a5efa87d3da8f3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tx402-0.2.0-py3-none-any.whl.
File metadata
- Download URL: tx402-0.2.0-py3-none-any.whl
- Upload date:
- Size: 177.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f40323449f09fff731070ed48a4eab61b81e33bff72796005c477804adc64497
|
|
| MD5 |
e3466a398660d986dba2fe5fc6358c86
|
|
| BLAKE2b-256 |
95065ffdcd7f0110b1735aa0d4bc2d09b0a027cbec3ab6ec77daac1cd37e2582
|
Provenance
The following attestation bundles were made for tx402-0.2.0-py3-none-any.whl:
Publisher:
release.yml on neogeeks/tx402
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tx402-0.2.0-py3-none-any.whl -
Subject digest:
f40323449f09fff731070ed48a4eab61b81e33bff72796005c477804adc64497 - Sigstore transparency entry: 2439494791
- Sigstore integration time:
-
Permalink:
neogeeks/tx402@25362265bdd3bcdd0bd8f0bfe5a5efa87d3da8f3 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/neogeeks
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@25362265bdd3bcdd0bd8f0bfe5a5efa87d3da8f3 -
Trigger Event:
push
-
Statement type: