HTTP 402 Implementation Guide: Complete Technical Reference
Everything you need to implement HTTP 402 Payment Required in your API. Complete with code, flow diagrams, and error handling.
The HTTP 402 Flow
Client → GET /api/data
Server → 402 Payment Required
{
"price_usd": 0.05,
"currency": "USDC",
"network": "base",
"wallet": "0x...",
"nonce": "abc123",
"expires_at": "2026-08-31T12:00:00Z"
}
Client → Signs EIP-3009 permit (gasless)
Client → Retries with receipt header
X-402-Receipt: <signed_receipt>
Server → Verifies receipt → 200 OK
Server Implementation (FastAPI)
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
import secrets
from datetime import datetime, timedelta, timezone
app = FastAPI()
# In-memory challenge store (use Redis in production)
_active_challenges = {}
TREASURY_WALLET = "0xYourUSDCWalletOnBase"
PRICE_USD = 0.05
@app.middleware("http")
async def x402_middleware(request: Request, call_next):
# Skip free paths
if request.url.path.startswith("/.well-known/") or \
request.url.path.startswith("/health") or \
request.url.path.startswith("/openapi"):
return await call_next(request)
receipt = request.headers.get("X-402-Receipt")
if receipt:
if verify_receipt(receipt):
return await call_next(request)
else:
return JSONResponse(
status_code=402,
content={"error": "Invalid payment receipt"},
headers={"X-402-Challenge": "true"}
)
# Issue challenge
nonce = secrets.token_urlsafe(16)
expires_at = datetime.now(timezone.utc) + timedelta(minutes=10)
_active_challenges[nonce] = {"expires": expires_at, "path": request.url.path}
return JSONResponse(
status_code=402,
content={
"error": "Payment Required",
"price_usd": PRICE_USD,
"currency": "USDC",
"network": "base",
"wallet": TREASURY_WALLET,
"nonce": nonce,
"expires_at": expires_at.isoformat()
},
headers={"X-402-Challenge": "true"}
)
def verify_receipt(receipt_header: str) -> bool:
"""Verify x402 receipt. Use nano-empire-tollbooth in production."""
try:
from nano_empire_tollbooth import verify_receipt
return verify_receipt(receipt_header)
except ImportError:
# Fallback: basic validation
return len(receipt_header) > 100 and "receipt" in receipt_header.lower()
Client Implementation (Agent Side)
import httpx
import json
async def call_with_payment(url: str, payload: dict):
async with httpx.AsyncClient() as client:
# First attempt
response = await client.post(url, json=payload)
if response.status_code == 200:
return response.json()
if response.status_code == 402:
challenge = response.json()
# Sign EIP-3009 permit (gasless)
receipt = sign_eip3009_permit(
wallet=challenge["wallet"],
amount=challenge["price_usd"],
nonce=challenge["nonce"]
)
# Retry with receipt
headers = {"X-402-Receipt": receipt}
response = await client.post(url, json=payload, headers=headers)
return response.json()
raise Exception(f"Unexpected status: {response.status_code}")
Receipt Verification (Production)
Use the SDK for production-grade verification:
pip install nano-empire-tollbooth
from nano_empire_tollbooth import verify_receipt
@app.middleware("http")
async def x402_middleware(request: Request, call_next):
receipt = request.headers.get("X-402-Receipt")
if receipt and verify_receipt(receipt):
return await call_next(request)
# ... issue challenge
Free Tier Quota
from collections import defaultdict
from datetime import datetime, timezone
_quota = defaultdict(int)
QUOTA_LIMIT = 5
def check_quota(client_ip: str) -> bool:
today = datetime.now(timezone.utc).date().isoformat()
key = f"{client_ip}:{today}"
if _quota[key] >= 5:
return False
_quota[key] += 1
return True
Error Handling
| Code | Scenario | Response |
|---|---|---|
| 402 | No receipt / invalid receipt | Challenge issued |
| 429 | Free quota exceeded | Upgrade prompt |
| 400 | Invalid receipt format | Error details |
| 500 | Verification service down | Retry later |
Testing
# Free tier test (first 5 calls)
curl -X POST http://localhost:8000/api/endpoint \
-H "Content-Type: application/json" \
-d '{"data": "test"}'
# Should return 200 OK for first 5 calls
# 6th call returns 402 with challenge
Production Checklist
- [ ] Redis for quota tracking
- [ ] Treasury wallet configured
- [ ] Domain verified for Resend emails
- [ ] Proof chain endpoint deployed
- [ ] llms.txt / agents.json deployed
- [ ] Submitted to Smithery & Glama
- [ ] Monitoring + alerts configured
SDK: pip install nano-empire-tollbooth
Live proof: https://api.nanoempireai.com/proof/summary
Top comments (0)