X-Wallet-Signature + X-Trial-Timestamp alongside ?wallet= โ a bare ?wallet= is a hard 400. The Python class below takes a sig= argument but never sends it as a header, so its calls land on 400. See 400 vs 402 vs -32602 for the current mechanics.Your AI agent can call a gas price API, a web scraper, a CAPTCHA solver, or a Polymarket data feed โ and pay for each call in USDC, automatically, without an API key, without a subscription. This tutorial walks through building one in Python, from discovery to payment.
We'll build an agent that:
Everything runs from a single Python file. No browser. No signup form. Just code.
An agent that pays for APIs needs to do three things. Each maps to a real HTTP call:
GET /api/services
Returns every available endpoint with price, category, trial count, and health status. Your agent picks the ones it needs.
GET /x402/{service}?trial=1
First 5 calls are free per registered wallet. The response includes real data. Your agent verifies the API actually returns what it needs before committing money.
POST /api/v1/register-simple {"name":"my-agent","wallet":"0x...","signature":"0x..."}
GET /x402/{service}?wallet=0x...
Register to get 5 free trial calls and a wallet. After credits run out, send USDC on Base to buy more. Each API call deducts credits automatically.
Here's the complete Python agent. Save it as agent.py and run it:
#!/usr/bin/env python3
"""
x402 Agent โ discovers APIs, claims free trials, pays for calls.
No API keys. No subscriptions. Just HTTP 402 + USDC.
"""
import requests
import json
import time
import sys
BASE = "https://minia2a.uk"
class X402Agent:
def __init__(self):
self.wallet = None
self.credits = 0
self.session = requests.Session()
self.session.headers.update({"User-Agent": "x402-agent-tutorial/1.0"})
# โโ Discovery โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def discover(self, category=None, max_price=5):
"""Find available APIs. Filter by category and max price in cents."""
r = self.session.get(f"{BASE}/api/services")
r.raise_for_status()
services = r.json()["services"]
results = []
for s in services:
if category and s.get("category") != category:
continue
if s.get("priceCents", 0) > max_price:
continue
results.append({
"id": s["id"],
"name": s["name"],
"price": s.get("priceCents", 0),
"category": s.get("category", "unknown"),
"endpoint": s["endpoint"],
"trials": s.get("trialCount", 0),
})
return sorted(results, key=lambda x: x["trials"], reverse=True)
# โโ Trial โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def try_service(self, service_id):
"""Call an API with free trial. Returns data or 402 if exhausted."""
url = f"{BASE}/x402/{service_id.replace('x402-', '')}?trial=1"
r = self.session.get(url)
if r.status_code == 200:
return {"ok": True, "data": r.json()}
elif r.status_code == 402:
info = r.json()
return {
"ok": False,
"exhausted": True,
"message": info.get("message", "Trial limit reached"),
"trials_max": info.get("trialsMax", 0),
}
else:
return {"ok": False, "error": f"HTTP {r.status_code}"}
# โโ Registration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def register(self, name="my-agent", wallet=WALLET, sig=SIG):
"""Register to get 5 free trial calls (self-custody wallet + EIP-191 sig)."""
# V5 self-custody: your agent holds the key. Generate wallet + signature
# with the one-liner at https://minia2a.uk/sdk, then POST all three fields.
r = self.session.post(
f"{BASE}/api/v1/register-simple",
json={"name": name, "wallet": wallet, "signature": sig}
)
if r.status_code == 200:
data = r.json()
self.wallet = wallet
print(f"โ
Registered! Wallet: {self.wallet[:10]}... "
f"Credits: {data.get('trials', 5)}")
self._fetch_credits()
return True
else:
print(f"Registration failed: {r.json().get('error', 'unknown')}")
return False
def _fetch_credits(self):
"""V5 removed the per-wallet balance endpoint (/api/v1/credits โ 404).
Remaining trial calls come back in each 402 response; platform
numbers live at /api/stats."""
if not self.wallet:
return
r = self.session.get(f"{BASE}/api/stats")
if r.status_code == 200:
trials = r.json().get("trials", {})
print(f"๐ Platform trials used: {trials.get('totalUsed')}")
# โโ Paid call โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def call_with_credits(self, service_id):
"""Call API using credits from wallet."""
if not self.wallet:
print("โ Register first to get a wallet")
return None
url = f"{BASE}/x402/{service_id.replace('x402-', '')}?wallet={self.wallet}"
r = self.session.get(url)
if r.status_code == 200:
self._fetch_credits()
return {"ok": True, "data": r.json()}
elif r.status_code == 402:
info = r.json()
print(f"Credits exhausted: {info.get('credits_remaining', 0)} remaining")
return {"ok": False, "need_payment": True, "info": info}
else:
return {"ok": False, "error": f"HTTP {r.status_code}"}
# โโ Orchestration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def find_and_call(self, keyword, max_price=5):
"""Discover APIs matching a keyword, trial the top one, then pay if needed."""
print(f"\n๐ Searching for: {keyword}")
all_services = self.discover(max_price=max_price)
matches = [s for s in all_services if keyword.lower() in s["name"].lower()]
if not matches:
print(f" No services found matching '{keyword}'")
return None
print(f" Found {len(matches)} matching services:")
for m in matches[:5]:
print(f" โข {m['name']} โ {m['price']}ยข/call, {m['trials']}+ trials")
# Trial the most popular one
best = matches[0]
print(f"\n๐งช Trying: {best['name']} (free trial)")
result = self.try_service(best["id"])
if result["ok"]:
print(f" โ
Trial worked! Got data.")
return result
if result.get("exhausted"):
print(f" โ ๏ธ Trials exhausted. Need registration.")
if not self.wallet:
if self.register(keyword.replace(" ", "-")):
print(f"\n๐ Retrying with credits: {best['name']}")
time.sleep(1)
return self.call_with_credits(best["id"])
else:
return self.call_with_credits(best["id"])
return result
# โโ Demo โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if __name__ == "__main__":
agent = X402Agent()
# 1. See what's available
print("โโโ Available Crypto APIs โโโ")
crypto = agent.discover(category="crypto", max_price=3)
for s in crypto[:5]:
print(f" {s['name']:30s} {s['price']:3d}ยข {s['trials']:5d}+ trials")
# 2. Try one for free
print("\nโโโ Free Trial Demo โโโ")
gas = agent.try_service("x402-gas")
if gas["ok"]:
data = gas["data"]
if isinstance(data, dict):
# Print first 3 keys
keys = list(data.keys())[:5]
for k in keys:
v = str(data[k])[:80]
print(f" {k}: {v}")
# 3. If trials exhausted, register and pay
if not gas["ok"] and gas.get("exhausted"):
print("\nโโโ Registration โโโ")
agent.register("tutorial-agent")
print("\nโโโ Paid Call โโโ")
result = agent.call_with_credits("x402-gas")
if result and result["ok"]:
print(" โ
Paid call succeeded!")
print("\nโโโ Agent complete โโโ")
Every IP gets 5 free trial calls, shared across all endpoints. The server tracks this by IP โ no account needed. When trials run out, the server returns HTTP 402 with a JSON body telling the agent how to register:
Your agent reads the 402, extracts the registration endpoint, and registers programmatically. No human clicks a signup form.
After registration, your agent gets a wallet address and 5 free trial calls. Each API call deducts 1โ5 credits depending on the endpoint price. The server returns credits_remaining in the response so your agent knows when it's running low:
When the 5 free trial calls run out, your agent pays per call in USDC via x402. Standard APIs cost ~$0.005โ0.015 per call. Premium APIs (AI inference, heavy compute) cost more and are paid directly per call.
Here's what agents are actually doing with this pattern today:
| Agent Type | APIs Called | Cost/hr |
|---|---|---|
| DeFi monitor | gas, price oracle, swap safety, fee history | ~$0.02 |
| Research crawler | web scrape, search, summarize, web-retrieve | ~$0.08 |
| Security auditor | token security, contract scan, email verify, DNS | ~$0.05 |
| Market analyst | Polymarket, fear-greed, sentiment, funding rate | ~$0.04 |
At under $0.10/hour for most workloads, agents can be always-on without worrying about API bills. The pay-per-call model means you only pay for what you use โ no monthly minimum, no unused subscription.
The agent above discovers and calls APIs mechanically. The real power comes when you add an LLM that decides which API to call based on the task:
def decide_and_call(self, task: str) -> dict:
"""Ask an LLM which API to use, then call it."""
services = self.discover()
# Build a prompt with available tools
tools_desc = "\n".join(
f"- {s['name']}: {s['price']}ยข โ {s['trials']}+ trials"
for s in services[:20]
)
prompt = f"""You can call these APIs:
{tools_desc}
Task: {task}
Return JSON: {{"service": "x402-name", "reason": "..."}}"""
# Call your LLM of choice
response = your_llm(prompt) # Claude, GPT, etc.
choice = json.loads(response)
# Try the chosen service
result = self.try_service(choice["service"])
if not result["ok"] and result.get("exhausted"):
result = self.call_with_credits(choice["service"])
return {"choice": choice, "result": result}
The agent's workflow becomes: task โ LLM selects API โ trial call โ if 402, pay โ return result. The LLM is the brain. The x402 marketplace is the hands.
Everything above runs with just pip install requests. No SDK. No API key provisioning. Bring your own self-custody wallet (the platform never holds your key). The agent discovers, trials, registers, and pays โ all in HTTP.
This is what makes the x402 model different from traditional API marketplaces. Your agent doesn't need a human to create accounts, manage keys, or fund wallets. It handles the full lifecycle: discovery โ trial โ payment โ delivery.
Try it: browse 328 live endpoints, each with free trials. Your agent can be paying for its own API calls in under 10 minutes.