arc-agent-pay — Python SDK for AI agents that autonomously pay for API services using USDC nanopayments on Arc via the x402 protocol.
The core SDK has minimal dependencies. Install only what you need:
pip install arc-agent-pay
| Extra | Adds | Use when |
|---|---|---|
| [agent] | openai, python-dotenv | Building agents that synthesize with an LLM |
| [server] | fastapi, uvicorn, python-dotenv | Running your own x402-gated API server |
| [demo] | all of the above | Running the full research agent demo |
Create .env:
AGENT_PRIVATE_KEY=<your hex private key> # LLM synthesis — pick one: ARCAPIS_TOKEN_ID=<tokenId> # on-chain via arcapis.com (recommended) # OPENAI_API_KEY=sk-proj-... # or OpenAI directly
Run the demo agent:
uv run python -m demo.run "USDC payments on Arc network" uv run python -m demo.run "crypto whale activity" --budget 0.05 uv run python -m demo.run "AI agent economy 2026" --verbose
Or use the SDK directly:
from arc_agent_pay import PaymentClient, ServiceRegistry
from arc_agent_pay.models import Chain
from eth_account import Account
registry = ServiceRegistry()
services = registry.search("crypto prices")
account = Account.from_key("0x" + private_key)
async with PaymentClient(
account=account,
budget_usdc="0.05",
chain=Chain.ARC_TESTNET,
) as client:
response = await client.get(services[0].url) # 402 → pay → retry
data = response.json()
print(client.summary())Every API call goes through the x402 payment flow automatically:
1. Agent calls GET /prices 2. Server returns 402 Payment Required + PAYMENT-REQUIRED header 3. PaymentClient parses the header — price: 0.001 USDC, pay_to: 0x… 4. Signs EIP-3009 TransferWithAuthorization (off-chain, no gas yet) 5. Retries with X-402-Payment header 6. Server verifies signature → calls transferWithAuthorization on-chain 7. Arc Testnet confirms → tx hash returned in PAYMENT-RESPONSE header 8. 200 OK — agent gets the data Chain: Arc Testnet · chain ID 5042002 USDC: 0x3600000000000000000000000000000000000000 Protocol: EIP-3009 v2 (transferWithAuthorization)
Async httpx wrapper that intercepts HTTP 402 responses, signs EIP-3009 authorizations, and retries automatically. Enforces a session budget via BudgetGuard.
PaymentClient(
account: eth_account.Account,
budget_usdc: str = "0.10",
chain: Chain = Chain.ARC_TESTNET,
on_event: Callable[[str, dict], None] | None = None,
)| get(url, **kwargs) | Authenticated GET with auto-payment |
| post(url, **kwargs) | Authenticated POST with auto-payment |
| summary() | Returns payment audit dict |
In-memory registry of x402-gated API services. Search by keyword or tag.
ServiceRegistry()
| search(query, max_results=5) | Keyword/tag search → list[Service] |
| register(service) | Add a Service to the registry |
| all() | Return all registered services |
Tracks cumulative spend for a session and raises BudgetExhaustedError when the limit is reached.
BudgetGuard(budget_usdc: str)
| check_and_record(amount_usdc) | Raises if over budget, else records |
| remaining | Decimal — remaining USDC |
| spent | Decimal — total spent this session |
Two EOA wallets are needed, both funded with Arc Testnet USDC:
| Role | Env var | Purpose |
|---|---|---|
| Agent (payer) | AGENT_PRIVATE_KEY | Signs EIP-3009 authorizations off-chain |
| Server (facilitator) | SERVER_PRIVATE_KEY | Pays gas, submits transferWithAuthorization on-chain |
Generate a fresh EOA:
uv run python -c "
from eth_account import Account
import secrets
a = Account.from_key('0x'+secrets.token_hex(32))
print('key:', a.key.hex())
print('addr:', a.address)
"Fund from a Circle wallet:
circle wallet transfer <ADDRESS> --amount 5 --address 0x40a2f3926fb79b91b8012c8f1dc3a1c6e4ded2cc --chain ARC-TESTNET --testnet
Publicly deployed at web-production-175cc.up.railway.app
| Endpoint | Price | Data source |
|---|---|---|
| GET /prices | $0.001 USDC | Synthetic crypto prices |
| POST /research | $0.005 USDC | Synthetic research brief |
| GET /news | $0.002 USDC | Synthetic headlines |
| GET /whales | $0.010 USDC | Real — Arc Testnet Blockscout |