x402
x402 Micropayments
Headless Oracle supports the x402 protocol for per-request USDC micropayments on Base mainnet. This lets autonomous agents pay for API access without a subscription or pre-registered key.
When x402 kicks in
Free tier keys (ho_free_*) have a 500 requests/day limit. After the limit is reached:
- 1. If your account has prepaid credits → one credit is consumed automatically.
- 2. If
X-Paymentheader is present with a valid Base mainnet USDC transaction → request is fulfilled. - 3. Otherwise → HTTP 402 with a machine-readable payment instruction.
Paid Paddle subscriptions (Builder/Pro/Protocol) and internal keys are never rate-limited by this gate.
The 402 Response
When payment is required, the server returns HTTP 402 with:
{
"error": "PAYMENT_REQUIRED",
"message": "Free tier exhausted. Pay 0.001 USDC per request via x402 on Base network...",
"x402": {
"version": "1",
"scheme": "exact",
"network": "base-mainnet",
"chainId": 8453,
"amount": "1000",
"currency": "USDC",
"decimals": 6,
"paymentAddress": "0x26D4Ffe98017D2f160E2dAaE9d119e3d8b860AD3",
"usdcContractAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"memo": "{key_hash}:{date}:{request_id}",
"maxAge": 300
},
"alternatives": {
"monthly": "https://headlessoracle.com/pricing",
"free_key": "https://headlessoracle.com/v5/keys/instant",
"prepaid": "https://headlessoracle.com/v5/credits/purchase"
}
}
Response headers also include:
X-Payment-Required: true
X-Payment-Scheme: x402
X-Payment-Network: base-mainnet
X-Payment-Chain-ID: 8453
X-Payment-Amount: 0.001 USDC
Paying with X-Payment header
- 1. Send 0.001 USDC (1000 units at 6 decimals) to the
paymentAddresson Base mainnet. - 2. Retry the request with the
X-Paymentheader containing the transaction details.
X-Payment: {"txHash":"0x...","network":"base-mainnet","amount":"1000","paymentAddress":"0x26D4...","memo":""}
The server verifies:
- · Transaction is on Base mainnet
- · USDC Transfer event credits the correct payment address
- · Amount ≥ 1000 units (0.001 USDC)
- · Transaction is < 300 seconds old
- · Transaction hash has not been used before (replay protection)
Node.js auto-pay agent
import { createPublicClient, createWalletClient, http, parseUnits } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
const ORACLE_KEY = 'ho_free_your_key_here';
const PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY; // Base wallet with USDC
const USDC_CONTRACT = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const USDC_ABI = [
{ name: 'transfer', type: 'function', inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' }
], outputs: [{ type: 'bool' }] }
];
async function fetchOracleWithAutoPayment(mic = 'XNYS') {
// First attempt — no payment
let res = await fetch(`https://headlessoracle.com/v5/status?mic=${mic}`, {
headers: { 'X-Oracle-Key': ORACLE_KEY }
});
if (res.status !== 402) return res.json();
const body = await res.json();
const { paymentAddress, amount } = body.x402;
// Pay on Base mainnet via viem
const account = privateKeyToAccount(PRIVATE_KEY);
const client = createPublicClient({ chain: base, transport: http() });
const { request } = await client.simulateContract({
address: USDC_CONTRACT, abi: USDC_ABI,
functionName: 'transfer', args: [paymentAddress, BigInt(amount)], account,
});
const walletClient = createWalletClient({ chain: base, transport: http() });
const txHash = await walletClient.writeContract(request);
await client.waitForTransactionReceipt({ hash: txHash });
// Retry with X-Payment header
const payment = JSON.stringify({ txHash, network: 'base-mainnet', amount, paymentAddress, memo: '' });
res = await fetch(`https://headlessoracle.com/v5/status?mic=${mic}`, {
headers: { 'X-Oracle-Key': ORACLE_KEY, 'X-Payment': payment }
});
return res.json();
}
Python auto-pay agent
import json, os, requests
from web3 import Web3
ORACLE_KEY = "ho_free_your_key_here"
PRIVATE_KEY = os.environ["WALLET_PRIVATE_KEY"]
USDC_CONTRACT = Web3.to_checksum_address("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913")
USDC_ABI = [{"name": "transfer", "type": "function",
"inputs": [{"name": "to", "type": "address"}, {"name": "amount", "type": "uint256"}],
"outputs": [{"type": "bool"}]}]
w3 = Web3(Web3.HTTPProvider("https://mainnet.base.org"))
account = w3.eth.account.from_key(PRIVATE_KEY)
usdc = w3.eth.contract(address=USDC_CONTRACT, abi=USDC_ABI)
def fetch_oracle_with_autopay(mic="XNYS"):
r = requests.get(f"https://headlessoracle.com/v5/status?mic={mic}",
headers={"X-Oracle-Key": ORACLE_KEY})
if r.status_code != 402:
return r.json()
x402 = r.json()["x402"]
payment_address = Web3.to_checksum_address(x402["paymentAddress"])
amount = int(x402["amount"]) # 1000 = 0.001 USDC
nonce = w3.eth.get_transaction_count(account.address)
tx = usdc.functions.transfer(payment_address, amount).build_transaction({
"from": account.address, "nonce": nonce,
"gas": 100000, "gasPrice": w3.eth.gas_price, "chainId": 8453,
})
signed = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction).hex()
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60)
if receipt["status"] != 1:
raise RuntimeError("USDC transfer failed")
payment = json.dumps({"txHash": tx_hash, "network": "base-mainnet",
"amount": str(amount), "paymentAddress": x402["paymentAddress"], "memo": ""})
r2 = requests.get(f"https://headlessoracle.com/v5/status?mic={mic}",
headers={"X-Oracle-Key": ORACLE_KEY, "X-Payment": payment})
return r2.json()
Prepaid Credits
Buy credits in bulk to avoid per-request on-chain transactions.
Buy 100 credits (0.09 USDC)
# 1. Pay 90000 USDC units to the payment address on Base mainnet # 2. POST to credits/purchase with X-Payment header curl -X POST https://headlessoracle.com/v5/credits/purchase \ -H "X-Oracle-Key: ho_free_your_key" \ -H 'X-Payment: {"txHash":"0x...","network":"base-mainnet","amount":"90000","paymentAddress":"0x26D4Ffe98017D2f160E2dAaE9d119e3d8b860AD3","memo":""}'
Buy 1000 credits (0.80 USDC)
Same as above but send 800000 USDC units.
Check balance
curl https://headlessoracle.com/v5/credits/balance \
-H "X-Oracle-Key: ho_free_your_key"
# {"balance": 42, "estimated_requests_remaining": 42, "last_purchased": "2026-03-17T..."}
Security Notes
Transactions expire after 300 seconds. Do not reuse old receipts. The server rejects transactions older than 5 minutes.
Each txHash can only be used once.
Replay protection is enforced via a 600s KV TTL. Attempting to reuse a transaction hash returns PAYMENT_ALREADY_USED.
Verify the paymentAddress before paying.
The paymentAddress in the 402 response is read from the operator's server environment. Agents should verify it matches the expected operator address before sending funds.
Resources
- API Reference
All endpoints, response schemas, authentication - x402.org
The x402 protocol specification - Pricing
Free tier, monthly plans, and pay-per-use - Claude API Integration
Full tool_use integration example