Skip to content

Quickstart

This guide gets you to a first successful 402 payment in about 5 minutes using the x402 rail on Base Sepolia testnet (no real funds required).

1. Install

pip install routeweiler

Python 3.11+ required.

2. Get a testnet wallet

You need a Base Sepolia wallet with some testnet USDC and ETH for gas.

# Generate a new wallet (or use an existing key)
python -c "from eth_account import Account; a = Account.create(); print(a.key.hex(), a.address)"

Fund it:

3. Write the code

import asyncio
import os
from eth_account import Account
from routeweiler import Routeweiler, Funding
from routeweiler.trace.sink_sqlite import TraceSink

async def main():
    # Load wallet from env var (never hard-code private keys)
    signer = Account.from_key(os.environ["WALLET_PK"])

    async with Routeweiler(
        funding=[Funding.base_sepolia_usdc(wallet=signer)],
        trace_sink=TraceSink.sqlite("rw.db"),  # optional — enables local trace log
    ) as client:
        response = await client.get("https://YOUR_X402_ENDPOINT")
        print(response.status_code, response.json())

asyncio.run(main())
export WALLET_PK=0x<your-private-key>
python main.py

4. What happens under the hood

GET https://YOUR_X402_ENDPOINT
  → 402 Payment Required (with PAYMENT-REQUIRED header)
      Routeweiler parses x402 challenge
      Signs ERC-3009 transferWithAuthorization
      Retries with PAYMENT-SIGNATURE header
  → 200 OK
      Trace event written to rw.db

The whole flow is synchronous from your code's perspective — await client.get(url) returns the 200 response as if the 402 never happened.

5. Inspect the trace

import sqlite3, json

conn = sqlite3.connect("rw.db")
rows = conn.execute("SELECT * FROM trace_events").fetchall()
for row in rows:
    print(json.loads(row[3]))  # payload column
conn.close()

Key fields in the payload:

{
  "selectedRail": "x402",
  "payment": {
    "proofType": "txid",
    "proofValue": "0xabc...def",
    "amountNative": 100,
    "amountNativeCurrency": "eip155:84532/erc20:0x036..."
  },
  "outcome": { "httpStatus": 200, "serviceDelivered": true }
}

Next steps

  • Add a budget envelope to cap how much your agent can spend per session — see Budgets.
  • Configure a policy to deny sketchy URLs or prefer specific rails — see Policy.
  • Try all four railsx402 | L402 | MPP-Tempo | MPP-SPT.