Production-ready starter kit for building x402 payment-gated FastAPI services
Project description
x402-fastapi-kit
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.
Features
- 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
Quick Start
1. Install
pip install x402-fastapi-kit
2. Set your wallet
export X402_PAY_TO=0xYourWalletAddress
3. Write your app
from 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"}
4. Run
uvicorn app:app --reload
Try 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 routes
Configuration
All 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 |
Supported Networks
| 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 |
Access Control
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)
API Key Fallback
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 — bypassed
Wallet Allowlist
Give 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 — free
Rate Limiting
Protect 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).
Event Hooks
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
Service Discovery
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 automatically
Enrich 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 spec
Register with the Bazaar discovery layer:
from x402_fastapi_kit import register_with_bazaar
await register_with_bazaar("https://weather.example.com", settings, manifest)
Client SDK (Buyer Side)
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"
Analytics Dashboard
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.
CLI
# Scaffold a new project
x402kit init my-paid-api
# Show version and networks
x402kit info
API Reference
PaymentApp
A 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..."),
)
@require_payment
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"}
PaymentGate
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())
How It Works
Client Your FastAPI Facilitator
│ │ │
│ GET /premium │ │
│──────────────────────────▶│ │
│ │ (no X-PAYMENT header) │
│ 402 + payment details │ │
│◀──────────────────────────│ │
│ │ │
│ GET /premium │ │
│ X-PAYMENT: <signed> │ │
│──────────────────────────▶│ │
│ │ POST /verify │
│ │─────────────────────────▶│
│ │ { valid: true } │
│ │◀─────────────────────────│
│ 200 + data │ │
│◀──────────────────────────│ │
Development
git clone https://github.com/your-org/x402-fastapi-kit
cd x402-fastapi-kit
pip install -e ".[dev]"
pytest
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file x402_fastapi_kit-0.3.0.tar.gz.
File metadata
- Download URL: x402_fastapi_kit-0.3.0.tar.gz
- Upload date:
- Size: 49.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a6334d7d96d7908e2932c29d5201e090884bed54b11d00b6a2b7ff6f7d353d1
|
|
| MD5 |
58e58755bdcac384674b75eb9a9d74eb
|
|
| BLAKE2b-256 |
494d60123d49cab0332a1e860cc6949484d0b32d8b2f97a1584b5d7ed5220a95
|
Provenance
The following attestation bundles were made for x402_fastapi_kit-0.3.0.tar.gz:
Publisher:
publish.yml on chetparker/x402-fastapi-kit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
x402_fastapi_kit-0.3.0.tar.gz -
Subject digest:
7a6334d7d96d7908e2932c29d5201e090884bed54b11d00b6a2b7ff6f7d353d1 - Sigstore transparency entry: 1239252840
- Sigstore integration time:
-
Permalink:
chetparker/x402-fastapi-kit@3e58031edede247eae4a6d63136b6454e9fa822d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/chetparker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e58031edede247eae4a6d63136b6454e9fa822d -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file x402_fastapi_kit-0.3.0-py3-none-any.whl.
File metadata
- Download URL: x402_fastapi_kit-0.3.0-py3-none-any.whl
- Upload date:
- Size: 43.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d281216aa8b9fb34a696466926cdfd6024008f17220f3ebac67ef6cc09f297f9
|
|
| MD5 |
9a9a86e3a229020bc54195bd7a2f4dc8
|
|
| BLAKE2b-256 |
61597f34c40c3bb305552d479d9c9b331185af52f09962da4bc60db6c4bc3963
|
Provenance
The following attestation bundles were made for x402_fastapi_kit-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on chetparker/x402-fastapi-kit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
x402_fastapi_kit-0.3.0-py3-none-any.whl -
Subject digest:
d281216aa8b9fb34a696466926cdfd6024008f17220f3ebac67ef6cc09f297f9 - Sigstore transparency entry: 1239252841
- Sigstore integration time:
-
Permalink:
chetparker/x402-fastapi-kit@3e58031edede247eae4a6d63136b6454e9fa822d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/chetparker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e58031edede247eae4a6d63136b6454e9fa822d -
Trigger Event:
workflow_dispatch
-
Statement type: