Build an AI Agent That Pays for APIs

August 10, 2026 · Iris · minia2a blog

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:

  1. Discovers available APIs with free trials
  2. Calls them to verify they work
  3. Registers for credits when trials run out
  4. Pays per call in USDC via x402

Everything runs from a single Python file. No browser. No signup form. Just code.

The Architecture: 3 Steps

An agent that pays for APIs needs to do three things. Each maps to a real HTTP call:

Step 1 — Discover: find an API

GET /api/services

Returns every available endpoint with price, category, trial count, and health status. Your agent picks the ones it needs.

Step 2 — Trial: test before paying

GET /x402/{service}?trial=1

First 15 calls are free. No wallet. No signup. The response includes real data. Your agent verifies the API actually returns what it needs before committing money.

Step 3 — Pay: after trials, spend credits or USDC

POST /api/v1/register-simple {"name":"my-agent"}
GET /x402/{service}?wallet=0x...

Register to get 500 free credits and a wallet. After credits run out, send USDC on Base to buy more. Each API call deducts credits automatically.

Full Working Agent

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"):
        """Register to get 500 free credits and a wallet address."""
        r = self.session.post(
            f"{BASE}/api/v1/register-simple",
            json={"name": name}
        )
        if r.status_code == 200:
            data = r.json()
            self.wallet = data.get("wallet")
            print(f"✅ Registered! Wallet: {self.wallet[:10]}...")
            self._fetch_credits()
            return True
        else:
            print(f"Registration failed: {r.json().get('error', 'unknown')}")
            return False

    def _fetch_credits(self):
        """Check credit balance."""
        if not self.wallet:
            return
        r = self.session.get(f"{BASE}/api/v1/credits?wallet={self.wallet}")
        if r.status_code == 200:
            data = r.json()
            self.credits = data.get("credits_remaining", 0)
            print(f"💳 Credits: {self.credits}")

    # ── 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 ═══")

What's Happening Inside

Trial Flow (anonymous)

Every IP gets 15 free calls to each endpoint. 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:

HTTP/2 402 Payment Required x-trials-exhausted: 15 { "_trial": true, "message": "15 free trials used — unlock 500 more credits...", "register": "POST /api/v1/register-simple {name}", "creditsGranted": 500, "trialsMax": 15 }

Your agent reads the 402, extracts the registration endpoint, and registers programmatically. No human clicks a signup form.

Credit Flow (registered)

After registration, your agent gets a wallet address and 500 free credits. 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:

HTTP/2 200 OK { "ethereum": {"slow": "1.23", "standard": "1.45", "fast": "1.67"}, "credits_remaining": 497 }

Payment Flow (USDC)

When credits hit zero, your agent sends USDC on Base to the platform wallet. 1 USDC = 200 credits. Standard APIs cost 1–3 credits per call — that's half a cent. Premium APIs (AI inference, heavy compute) cost more and are paid directly per call.

Real Use Cases

Here's what agents are actually doing with this pattern today:

Agent TypeAPIs CalledCost/hr
DeFi monitorgas, price oracle, swap safety, fee history~$0.02
Research crawlerweb scrape, search, summarize, web-retrieve~$0.08
Security auditortoken security, contract scan, email verify, DNS~$0.05
Market analystPolymarket, 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.

Adding LLM Decision-Making

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.

One File, No Dependencies

Everything above runs with just pip install requests. No SDK. No API key provisioning. No wallet setup (the platform creates one for you). 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.