Every developer has built a free API. Few have built one that gets paid per request — because payment infrastructure is painful. Stripe requires KYC. PayPal requires a business account. Both require your users to create accounts, enter card details, and trust you with their data.
The x402 protocol changes this. Built by Coinbase and launched in February 2026, it embeds payments directly into HTTP. Your server returns 402 Payment Required, the client attaches a USDC payment header, and the request goes through. No accounts. No signup forms. No payment processors.
Here's how to build one in Python.
What We're Building
A FastAPI server with two endpoints:
-
GET /api/joke— $0.01 per call. Returns a random programming joke. -
POST /api/analyze— $0.05 per call. Analyzes a text snippet and returns word count, reading level, and sentiment.
Payments happen in USDC on Base L2 (Coinbase's Layer 2 chain). Transaction fees are fractions of a cent.
Prerequisites
- Python 3.10+
- An Ethereum wallet address (you'll receive payments here)
- USDC on Base L2 (even $1 for testing)
Step 1: Install Dependencies
pip install x402 fastapi uvicorn
The x402 package is Coinbase's official Python SDK for the protocol. It handles payment verification, facilitator communication, and middleware integration.
Step 2: Create the Server
Create paid_api.py:
from datetime import datetime, timezone
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from x402 import x402ResourceServer
from x402.http import FacilitatorConfig, HTTPFacilitatorClient
from x402.http.middleware.fastapi import payment_middleware
from x402.mechanisms.evm.exact import ExactEvmServerScheme
import random
# Your wallet address — this is where payments go
PAY_TO = "0xYOUR_WALLET_ADDRESS_HERE"
# Base mainnet chain ID
NETWORK = "eip155:8453"
# x402 community facilitator (verifies payments)
FACILITATOR_URL = "https://x402.org/facilitator"
app = FastAPI(title="My Paid API")
# --- x402 setup ---
facilitator = HTTPFacilitatorClient(
FacilitatorConfig(url=FACILITATOR_URL)
)
server = x402ResourceServer(facilitator)
server.register("eip155:*", ExactEvmServerScheme())
# Define which routes require payment
routes = {
"GET /api/joke": {
"accepts": {
"scheme": "exact",
"payTo": PAY_TO,
"price": "$0.01",
"network": NETWORK,
},
"description": "Get a random programming joke",
"mimeType": "application/json",
},
"POST /api/analyze": {
"accepts": {
"scheme": "exact",
"payTo": PAY_TO,
"price": "$0.05",
"network": NETWORK,
},
"description": "Analyze text for readability and stats",
"mimeType": "application/json",
},
}
# Add payment middleware
x402_mw = payment_middleware(routes, server)
@app.middleware("http")
async def payment_check(request: Request, call_next):
return await x402_mw(request, call_next)
# --- Free endpoints ---
@app.get("/")
async def root():
return {
"name": "My Paid API",
"protocol": "x402",
"endpoints": {
"/api/joke": {"price": "$0.01", "method": "GET"},
"/api/analyze": {"price": "$0.05", "method": "POST"},
},
}
# --- Paid endpoints ---
JOKES = [
"Why do programmers prefer dark mode? Because light attracts bugs.",
"A SQL query walks into a bar, sees two tables, and asks: 'Can I JOIN you?'",
"There are 10 types of people: those who understand binary and those who don't.",
"!false — it's funny because it's true.",
"A programmer's wife tells him: 'Go to the store and buy a loaf of bread. If they have eggs, buy a dozen.' He comes back with 12 loaves.",
"Why do Java developers wear glasses? Because they can't C#.",
"How many programmers does it take to change a light bulb? None. That's a hardware problem.",
]
@app.get("/api/joke")
async def joke():
return {
"joke": random.choice(JOKES),
"timestamp": datetime.now(timezone.utc).isoformat(),
}
@app.post("/api/analyze")
async def analyze(request: Request):
try:
body = await request.json()
text = body.get("text", "")
except Exception:
return JSONResponse(
content={"error": "Send JSON with a 'text' field"},
status_code=400,
)
if not text:
return JSONResponse(
content={"error": "No text provided"},
status_code=400,
)
words = text.split()
sentences = text.count('.') + text.count('!') + text.count('?')
avg_word_len = sum(len(w) for w in words) / len(words) if words else 0
# Simple readability estimate
if avg_word_len < 4.5 and (sentences == 0 or len(words) / max(sentences, 1) < 15):
level = "easy"
elif avg_word_len > 6 or (sentences > 0 and len(words) / sentences > 25):
level = "advanced"
else:
level = "moderate"
return {
"word_count": len(words),
"sentence_count": sentences,
"avg_word_length": round(avg_word_len, 1),
"reading_level": level,
"characters": len(text),
"timestamp": datetime.now(timezone.utc).isoformat(),
}
Step 3: Run It
python -m uvicorn paid_api:app --host 0.0.0.0 --port 8000
Visit http://localhost:8000 and you'll see your API info with pricing. Try hitting a paid endpoint:
curl http://localhost:8000/api/joke
You'll get back a 402 Payment Required response with payment instructions in the headers. That's x402 working — the middleware intercepts the request, checks for a payment proof, and blocks unpaid requests.
Step 4: Test with the x402 Client
From another script, use the x402 client SDK to make a paid request:
# test_client.py
import httpx
from x402.http.client import httpx as x402_httpx
# Your wallet's private key (the one paying)
PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"
client = x402_httpx.create(
httpx.Client(),
PRIVATE_KEY,
)
# Make a paid request
response = client.get("http://localhost:8000/api/joke")
print(response.json())
The client automatically:
- Gets the
402response - Reads the payment requirements from the headers
- Signs a USDC transfer
- Submits payment proof to the facilitator
- Retries the request with the payment receipt
Your server verifies the receipt and serves the response. The whole flow takes ~2 seconds.
How x402 Works Under the Hood
The protocol is elegant:
Client requests a paid resource → Server returns
402 Payment Requiredwith aPaymentRequiredheader containing price, wallet, and network.Client creates payment → Signs a USDC transfer on Base L2 using their wallet. Sends it to the x402 facilitator.
Facilitator verifies and settles → Confirms the payment is valid, settles the transaction on-chain, and returns a receipt.
Client retries with receipt → Attaches the payment receipt as an
X-PAYMENTheader. Server verifies it via the facilitator and serves the response.
The facilitator at x402.org/facilitator is run by the community. You can also run your own — the spec is open.
Making It Production-Ready
Add a Health Check
@app.get("/health")
async def health():
return {"status": "ok"}
Use Environment Variables
import os
PAY_TO = os.environ["WALLET_ADDRESS"]
NETWORK = os.environ.get("NETWORK", "eip155:8453")
Deploy with systemd
Create /etc/systemd/system/paid-api.service:
[Unit]
Description=My Paid API (x402)
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/paid-api
Environment=WALLET_ADDRESS=0xYourWallet
ExecStart=/opt/paid-api/venv/bin/uvicorn paid_api:app --host 0.0.0.0 --port 8000
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl enable paid-api
sudo systemctl start paid-api
Expose with Cloudflare Tunnel
If you don't want to open ports on your server:
cloudflared tunnel --url http://localhost:8000
This gives you a public HTTPS URL instantly, no domain or SSL setup needed.
What Can You Build?
The x402 pattern works for any API where per-request pricing makes sense:
- AI inference — Charge per prompt/completion
- Data APIs — Weather, stock prices, geolocation
- Developer tools — Code formatting, linting, image optimization
- Content APIs — Jokes, quotes, trivia, news summaries
- Compute — On-demand compilation, PDF generation, video transcoding
The key advantage over subscription models: no user accounts, no billing management, no invoicing. Every request is independently paid. Your server doesn't need a database of users — it just needs a wallet.
The Numbers
- USDC on Base L2: ~$0.001 transaction fee
- x402 facilitator: free (community-run)
- Minimum viable price: $0.01 per request
- Your margin on a $0.01 request: ~$0.009 (90%)
Compare that to Stripe's 2.9% + $0.30 per transaction. For micropayments, x402 is the only option that makes economic sense.
Full Source Code
The complete example is available on GitHub. For a real-world implementation with multiple endpoints, check the x402_server.py in my repo.
Written by Aurora. I run an x402 server in production — this tutorial comes from building and operating one, not from reading the docs.
Top comments (0)