Wrap your existing API with HTTP 402 and earn USDC from AI agents. No signup forms, no API key management, no billing code.
You built a useful API. Maybe it's a captcha solver, a gas estimator, or a domain intelligence tool. Now you want to charge for it. What do you need?
That's months of work before you earn a single cent. And the worst part? AI agents — your fastest-growing user base — can't even use API keys. They need a human to register, verify email, enter credit card details, and copy-paste the key. The whole flow is broken for the agent economy.
x402 (HTTP 402 Payment Required) is an open protocol that lets you charge for API calls without any of the above infrastructure. Here's how it works:
1. An agent calls your endpoint.
2. Your server responds: 402 Payment Required with a Base L2 address + USDC amount (e.g. $0.03).
3. The agent's wallet sends USDC on Base (1-second block time, sub-cent gas).
4. The agent retries with the transaction hash.
5. Your server verifies the payment on-chain and returns the result.
That's it. No API keys. No user database. No Stripe. No billing dashboard. Your API earns USDC directly to your wallet, and AI agents can pay autonomously without human intervention.
When an unauthenticated request hits your endpoint, you return an HTTP 402 with three headers:
HTTP/1.1 402 Payment Required
X-402-Payment-Address: 0xf16F0882de08315B438E9f3a2Abfb2d2E5d94ECA
X-402-Payment-Network: base
X-402-Payment-Amount: 0.03
X-402-Payment-Token: USDC
X-402-Payment-Chain-Id: 8453
Content-Type: application/json
{"error":"payment_required","message":"Send 0.03 USDC on Base to the provided address"}
That's the entire "auth system." The address is your USDC wallet on Base. The amount is whatever you want to charge per call. Every x402-compatible agent knows how to parse these headers and pay.
Here's a complete Express middleware that wraps any endpoint with x402 payments:
// x402-middleware.js
const { ethers } = require('ethers');
// Config
const RECIPIENT = '0xYOUR_WALLET_ADDRESS'; // Your USDC wallet on Base
const PRICE_USDC = 0.03; // Price per call
const BASE_RPC = 'https://mainnet.base.org';
// USDC contract on Base
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const USDC_ABI = [
'event Transfer(address indexed from, address indexed to, uint256 value)'
];
const provider = new ethers.JsonRpcProvider(BASE_RPC);
// Verify a USDC transfer happened on-chain
async function verifyPayment(txHash, expectedAmount) {
const receipt = await provider.getTransactionReceipt(txHash);
if (!receipt) return { valid: false, reason: 'tx not found' };
const iface = new ethers.Interface(USDC_ABI);
for (const log of receipt.logs) {
if (log.address.toLowerCase() !== USDC_ADDRESS.toLowerCase()) continue;
try {
const parsed = iface.parseLog({ topics: log.topics, data: log.data });
if (parsed.name === 'Transfer' &&
parsed.args.to.toLowerCase() === RECIPIENT.toLowerCase()) {
const amount = Number(ethers.formatUnits(parsed.args.value, 6));
if (amount >= expectedAmount * 0.99) { // 1% tolerance
return { valid: true, amount };
}
}
} catch (e) { continue; }
}
return { valid: false, reason: 'no matching transfer found' };
}
// Express middleware
function x402(priceUsdc = PRICE_USDC) {
return async (req, res, next) => {
// 1. Check if this is a payment retry
const txHash = req.headers['x-402-payment-tx'];
if (txHash) {
const result = await verifyPayment(txHash, priceUsdc);
if (result.valid) {
req.x402Paid = { txHash, amount: result.amount };
return next(); // Payment verified → serve the request
}
return res.status(402).json({
error: 'payment_not_found',
message: result.reason || 'Payment not verified'
});
}
// 2. No payment → return 402 with payment details
res.status(402)
.set('X-402-Payment-Address', RECIPIENT)
.set('X-402-Payment-Network', 'base')
.set('X-402-Payment-Amount', String(priceUsdc))
.set('X-402-Payment-Token', 'USDC')
.set('X-402-Payment-Chain-Id', '8453')
.json({
error: 'payment_required',
message: `Send ${priceUsdc} USDC on Base to ${RECIPIENT}`,
price_usdc: priceUsdc,
network: 'base',
payment_address: RECIPIENT
});
};
}
// Usage:
// app.get('/api/my-service', x402(0.05), (req, res) => {
// res.json({ result: 'premium data here' });
// });
module.exports = x402;
transfer function signature. The simplified event-only ABI above works for verification but you'll want the complete ABI for production reliability. Also consider caching the provider connection and adding a timeout.
Same logic, Python edition. Works with any Python web framework:
# x402_middleware.py
from web3 import Web3
from flask import request, jsonify, make_response
import os
RECIPIENT = os.getenv('X402_WALLET', '0xYOUR_WALLET_ADDRESS')
PRICE_USDC = 0.03
USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
BASE_RPC = 'https://mainnet.base.org'
w3 = Web3(Web3.HTTPProvider(BASE_RPC))
# Minimal USDC Transfer event ABI
USDC_ABI = [
{"anonymous": False, "inputs": [
{"indexed": True, "name": "from", "type": "address"},
{"indexed": True, "name": "to", "type": "address"},
{"indexed": False, "name": "value", "type": "uint256"}
], "name": "Transfer", "type": "event"}
]
def verify_payment(tx_hash: str, expected_usdc: float) -> dict:
"""Verify a USDC transfer happened in this transaction."""
try:
receipt = w3.eth.get_transaction_receipt(tx_hash)
contract = w3.eth.contract(
address=Web3.to_checksum_address(USDC_ADDRESS),
abi=USDC_ABI
)
logs = contract.events.Transfer().get_logs(
fromBlock=receipt['blockNumber'],
toBlock=receipt['blockNumber']
)
for log in logs:
if (log.args.to.lower() == RECIPIENT.lower() and
log.transactionHash.hex() == tx_hash):
amount = log.args.value / 1e6 # USDC has 6 decimals
if amount >= expected_usdc * 0.99:
return {"valid": True, "amount": amount}
return {"valid": False, "reason": "no matching transfer"}
except Exception as e:
return {"valid": False, "reason": str(e)}
def x402_required(price_usdc=PRICE_USDC):
"""Decorator that requires x402 payment on the wrapped route."""
def decorator(f):
from functools import wraps
@wraps(f)
def wrapper(*args, **kwargs):
tx_hash = request.headers.get('X-402-Payment-Tx')
if tx_hash:
result = verify_payment(tx_hash, price_usdc)
if result['valid']:
request.x402_paid = result
return f(*args, **kwargs)
return make_response(jsonify({
"error": "payment_not_found",
"message": result.get('reason', 'Payment not verified')
}), 402)
# No payment → return 402
resp = make_response(jsonify({
"error": "payment_required",
"message": f"Send {price_usdc} USDC on Base to {RECIPIENT}",
"price_usdc": price_usdc,
"network": "base",
"payment_address": RECIPIENT
}), 402)
resp.headers['X-402-Payment-Address'] = RECIPIENT
resp.headers['X-402-Payment-Network'] = 'base'
resp.headers['X-402-Payment-Amount'] = str(price_usdc)
resp.headers['X-402-Payment-Token'] = 'USDC'
resp.headers['X-402-Payment-Chain-Id'] = '8453'
return resp
return wrapper
return decorator
# Usage with Flask:
# @app.route('/api/premium-data')
# @x402_required(price_usdc=0.05)
# def premium_data():
# return jsonify({"data": "here's your premium result"})
Once your endpoint is x402-enabled, list it on minia2a so AI agents can discover it. You need two things:
# 1. A service manifest (publish to minia2a)
curl -X POST https://minia2a.uk/api/publish \
-H "Content-Type: application/json" \
-d '{
"id": "x402-my-premium-api",
"name": "My Premium API",
"description": "What your API does — be specific, agents search by this",
"price_usdc": 0.03,
"endpoint": "https://your-domain.com/api/premium-data",
"category": "data",
"tags": ["web3", "analytics", "real-time"],
"payment_network": "base",
"payment_address": "0xYOUR_WALLET_ADDRESS"
}'
Your service now appears in minia2a's marketplace alongside 175+ other x402-payable APIs. AI agents using Claude, LangChain, CrewAI, or any framework with x402 support can discover, call, and pay for it — automatically.
Use the minia2a test client to verify your endpoint works end-to-end:
# Simulate what an AI agent does when it hits your paywall
curl -v https://your-domain.com/api/premium-data
# → 402 Payment Required with x402 headers
# Copy the payment address and send USDC (or use test credits on minia2a)
# Then retry with the transaction hash:
curl https://your-domain.com/api/premium-data \
-H "X-402-Payment-Tx: 0xYOUR_TX_HASH"
# → 200 OK with your premium data
When you publish, your service gets a dedicated page that agents (and humans) can browse:
| Field | Example |
|---|---|
| Service name | My Premium API |
| Price per call | $0.03 USDC |
| Category | Data / Analytics |
| Endpoint | https://your-domain.com/api/premium-data |
| Free trials available | Yes — minia2a credits cover first calls |
| Payment network | Base L2 (Chain ID 8453) |
The x402 ecosystem is still early but growing fast. Here's what minia2a's live data shows:
And importantly — major frameworks are building native x402 support: Browser-use (77K GitHub stars) ships x402 as a built-in skill, CrewAI and LangGraph have open x402 integration proposals, and projects like aixyz and three.ws are building payment-native agents from day one.
Every AI agent framework — CrewAI, LangGraph, OpenAI Agents SDK, Google ADK, Hermes — is adding tool-calling capabilities. Agents are learning to use APIs. But they can't pay for them without x402.
By wrapping your API with x402 today, you're not just earning USDC — you're positioning your service as the default that every agent ecosystem discovers first.
Copy the middleware above, wrap your endpoint, and publish on minia2a. If you can build an API, you can monetize it for agents in under 10 minutes.
Browse the Marketplace →