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.
0.001 USDC / request
Base mainnet · chainId 8453
Replay-protected · 300s TTL
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/request",
"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.