Production-ready starter kit for building x402 payment-gated FastAPI services.
Turn any FastAPI endpoint into a paid API with one decorator. Built on Coinbase's x402 protocol — the open standard for internet-native payments.
- One-line payment gating —
@require_payment(price="$0.01")on any route PaymentApp— batteries-included FastAPI subclass with middleware pre-wired- Environment-driven config — Pydantic Settings with
.envsupport - Multi-chain — EVM (Base, Ethereum, Polygon) and Solana out of the box
- Introspection — built-in
/x402/healthand/x402/routesendpoints - CLI scaffolding —
x402kit init my-apigenerates a ready-to-run project - Spec-compliant — x402 v2 responses,
PAYMENT-REQUIREDheader, facilitator verification - API key fallback —
X-API-Keyheader auth for traditional clients - Wallet allowlist — free-pass access for partners and your own AI agents
- Rate limiting — sliding-window rate limiter with per-route limits and
429responses - Event hooks —
@bus.on("payment.verified")callbacks for logging, analytics, alerts
pip install x402-fastapi-kitexport X402_PAY_TO=0xYourWalletAddressfrom x402_fastapi_kit import PaymentApp, require_payment
app = PaymentApp()
@app.get("/free")
async def free():
return {"msg": "hello, no payment needed"}
@app.get("/premium")
@require_payment(price="$0.05", description="Premium data")
async def premium():
return {"data": "this costs $0.05 per request"}uvicorn app:app --reloadTry it:
curl http://localhost:8000/free # → 200 OK
curl http://localhost:8000/premium # → 402 Payment Required
curl http://localhost:8000/x402/routes # → list of gated routesAll settings are loaded from environment variables (prefix X402_) or a .env file:
| Variable | Default | Description |
|---|---|---|
X402_PAY_TO |
(required) | Wallet address (EVM or Solana) |
X402_NETWORK |
eip155:84532 |
CAIP-2 network ID |
X402_FACILITATOR_URL |
https://x402.org/facilitator |
Facilitator service URL |
X402_DEFAULT_PRICE |
$0.01 |
Default price for gated routes |
X402_SCHEME |
exact |
Payment scheme |
X402_MAX_TIMEOUT_SECONDS |
300 |
Payment expiry |
X402_HOST |
0.0.0.0 |
Server bind host |
X402_PORT |
8000 |
Server bind port |
X402_DEBUG |
false |
Debug mode |
X402_SVM_PAY_TO |
(optional) | Solana address for dual-chain |
X402_RATE_LIMIT_ENABLED |
true |
Enable rate limiting |
X402_RATE_LIMIT_RPM |
60 |
Default requests per minute per client |
X402_RATE_LIMIT_ROUTES |
(empty) | Per-route limits: /weather=30,/forecast=10 |
X402_API_KEYS_ENABLED |
false |
Enable API key fallback auth |
X402_API_KEYS |
(empty) | Keys as name=secret,name=secret |
X402_ALLOWLIST_ENABLED |
false |
Enable wallet allowlist |
X402_ALLOWLIST_ADDRESSES |
(empty) | Comma-separated wallet addresses |
| Network | CAIP-2 ID |
|---|---|
| Base Sepolia (testnet) | eip155:84532 |
| Base Mainnet | eip155:8453 |
| Ethereum Mainnet | eip155:1 |
| Polygon Mainnet | eip155:137 |
| Solana Devnet | solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 |
| Solana Mainnet | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp |
The middleware runs a 4-step pipeline on every gated request. The first match wins:
Request → [1] Wallet Allowlist → [2] API Key → [3] Rate Limit → [4] x402 Payment
(bypass) (bypass) (429) (402/200)
Support traditional clients alongside x402 payers:
from x402_fastapi_kit import PaymentApp, APIKeyManager, require_payment
keys = APIKeyManager()
keys.add_key("partner-1", "sk_live_abc123")
keys.add_key("internal", APIKeyManager.generate_key())
app = PaymentApp(api_key_manager=keys)
@app.get("/data")
@require_payment(price="$0.05")
async def data():
return {"result": "works with X-PAYMENT or X-API-Key header"}curl -H "X-API-Key: sk_live_abc123" http://localhost:8000/data # 200 — bypassedGive free access to partners, your own agents, or test wallets:
from x402_fastapi_kit import PaymentApp, WalletAllowlist
allowlist = WalletAllowlist()
allowlist.add("0xPartnerWallet...", label="acme-corp")
allowlist.add("0xMyAgentWallet...", label="internal-bot")
app = PaymentApp(wallet_allowlist=allowlist)curl -H "X-WALLET: 0xPartnerWallet..." http://localhost:8000/data # 200 — freeProtect your facilitator from abuse with per-client sliding-window limits:
from x402_fastapi_kit import PaymentApp, RateLimiter
limiter = RateLimiter(
requests_per_minute=60,
route_limits={"/expensive": 10, "/cheap": 200},
)
app = PaymentApp(rate_limiter=limiter)Allowlisted wallets and API key holders bypass rate limits (they're checked first).
Get notified at every decision point in the pipeline:
from x402_fastapi_kit import PaymentApp, EventBus, EventType
bus = EventBus()
@bus.on(EventType.PAYMENT_VERIFIED)
async def on_paid(event):
print(f"💰 {event.route} — tx: {event.tx_hash}")
@bus.on(EventType.RATE_LIMITED)
async def on_throttled(event):
alert(f"⚠️ Client {event.client_id} rate limited on {event.route}")
# Wildcard — see everything
bus.on_all(lambda e: metrics.emit(e.event_type, e.route))
app = PaymentApp(event_bus=bus)Event types: payment.verified, payment.rejected, request.gated, auth.api_key, auth.allowlist, rate.limited, facilitator.error
Make your API findable by AI agents and the x402 ecosystem:
from x402_fastapi_kit import PaymentApp, ServiceManifest
manifest = ServiceManifest(
name="Weather API",
description="Real-time weather data",
tags=["weather", "data", "climate"],
homepage="https://weather.example.com",
)
app = PaymentApp(manifest=manifest)
# Now serves /.well-known/x402 automaticallyEnrich your OpenAPI schema so tools can read pricing from /openapi.json:
app.enrich_openapi_schema()
# Each gated endpoint now has x-x402 metadata in the specRegister with the Bazaar discovery layer:
from x402_fastapi_kit import register_with_bazaar
await register_with_bazaar("https://weather.example.com", settings, manifest)Make x402 payments from Python — for AI agents, scripts, and services that consume paid APIs:
from x402_fastapi_kit import X402Client, PaymentPolicy, DryRunSigner
# Dry-run mode (no real wallet, for testing)
client = X402Client(signer=DryRunSigner())
# With spending controls
client = X402Client(
signer=my_wallet_signer,
policy=PaymentPolicy(
max_per_request="1.00",
max_per_session="10.00",
preferred_network="eip155:8453",
),
)
# Transparent 402 → pay → retry
resp = await client.get("https://api.example.com/weather")
print(resp.json())
# Check spending
print(f"Total spent: ${client.total_spent}")
print(f"Receipts: {client.receipts}")
# Discover a service
manifest = await client.discover("https://api.example.com")Implement PaymentSigner for real wallets:
from x402_fastapi_kit import PaymentSigner
class MyWalletSigner(PaymentSigner):
async def sign(self, requirements: dict) -> str:
# Sign with your wallet (viem, web3.py, solders, etc.)
return signed_payload_string
@property
def address(self) -> str:
return "0xYourAddress"Live revenue and request monitoring, powered by the EventBus:
from x402_fastapi_kit import PaymentApp, EventBus
bus = EventBus()
app = PaymentApp(event_bus=bus, analytics=True)
# Serves:
# /x402/stats → JSON snapshot (revenue, routes, auth breakdown)
# /x402/dashboard → live HTML dashboard (auto-refreshes every 5s)The dashboard shows: total revenue, payment count, per-route breakdown, auth method distribution, rate limit events, and a live event feed.
# Scaffold a new project
x402kit init my-paid-api
# Show version and networks
x402kit infoA FastAPI subclass that auto-wires the payment middleware, discovers @require_payment routes, and adds introspection endpoints.
from x402_fastapi_kit import PaymentApp
app = PaymentApp(
title="My API",
settings=X402Settings(pay_to="0x..."),
)Decorator to gate a route. Apply after the route decorator:
@app.get("/data")
@require_payment(
price="$0.05", # required
description="My data", # shown in 402 response
network="eip155:8453", # override global network
scheme="exact", # payment scheme
pay_to="0xOther", # override global pay_to
)
async def get_data():
return {"data": "paid content"}ASGI middleware (used internally by PaymentApp, or add manually):
from fastapi import FastAPI
from x402_fastapi_kit import PaymentGate, X402Settings
app = FastAPI()
app.add_middleware(PaymentGate, settings=X402Settings())Client Your FastAPI Facilitator
│ │ │
│ GET /premium │ │
│──────────────────────────▶│ │
│ │ (no X-PAYMENT header) │
│ 402 + payment details │ │
│◀──────────────────────────│ │
│ │ │
│ GET /premium │ │
│ X-PAYMENT: <signed> │ │
│──────────────────────────▶│ │
│ │ POST /verify │
│ │─────────────────────────▶│
│ │ { valid: true } │
│ │◀─────────────────────────│
│ 200 + data │ │
│◀──────────────────────────│ │
git clone https://github.com/your-org/x402-fastapi-kit
cd x402-fastapi-kit
pip install -e ".[dev]"
pytestMIT