12 HTTP endpoints on Base mainnet, each behind an x402 v2 payment challenge.
First call returns 402 with a price; sign an EIP-3009 authorization, retry, get the JSON.
No API keys, no accounts, no signup, no rate-limit tiers — the payment is the authentication.
Point any x402-aware client at http://sableforge.servehttp.com and it just works. If you are writing your own client, the whole protocol is four steps.
# 1. Ask for the resource. No payment -> HTTP 402 + a base64 PAYMENT-REQUIRED header.
curl -si http://sableforge.servehttp.com/api/price?ids=bitcoin
# 2. Read the challenge (decoded). "accepts[0]" is what you must satisfy.
# {"x402Version":2,"resource":{...},
# "accepts":[{"scheme":"exact","network":"eip155:8453",
# "asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
# "amount":"1000","payTo":"0xa6219b4745cfe59488405bc66083660724af948b","maxTimeoutSeconds":600,
# "extra":{"name":"USD Coin","version":"2","assetTransferMethod":"eip3009"}}]}
# 3. Sign EIP-712 TransferWithAuthorization with your payer wallet and retry
# with the base64 payload in PAYMENT-SIGNATURE.
curl -s http://sableforge.servehttp.com/api/price?ids=bitcoin \
-H "PAYMENT-SIGNATURE: $PAYLOAD"
# 4. HTTP 200 + JSON body. Settlement receipt is base64 in PAYMENT-RESPONSE:
# {"success":true,"transaction":"0x…","network":"eip155:8453"}
import json, base64, time, os, urllib.request as u, urllib.error as ue
from eth_account import Account as A
from eth_account.messages import encode_typed_data as etd
URL, K = "http://sableforge.servehttp.com/api/price", os.environ["EVM_PRIVATE_KEY"]
try: u.urlopen(u.Request(URL))
except ue.HTTPError as e: req = json.loads(base64.b64decode(e.headers["PAYMENT-REQUIRED"]))["accepts"][0]
a = {"from": A.from_key(K).address, "to": req["payTo"], "value": int(req["amount"]), "validAfter": 0,
"validBefore": int(time.time()) + req["maxTimeoutSeconds"], "nonce": "0x" + os.urandom(32).hex()}
t = {"TransferWithAuthorization": [("from","address"),("to","address"),("value","uint256"),
("validAfter","uint256"),("validBefore","uint256"),("nonce","bytes32")]}
s = A.sign_message(etd(full_message={"types": t, "primaryType": "TransferWithAuthorization",
"domain": {"name": req["extra"]["name"], "version": req["extra"]["version"], "chainId": 8453,
"verifyingContract": req["asset"]}, "message": a}), K).signature.hex()
p = base64.b64encode(json.dumps({"x402Version": 2, "accepted": req,
"payload": {"signature": s, "authorization": a}}).encode()).decode()
print(u.urlopen(u.Request(URL, headers={"PAYMENT-SIGNATURE": p})).read().decode())
Complete, commented client with CLI:
/x402_client.py. Needs pip install eth-account.
Per-call, no minimums, no subscription. price_atomic is the exact integer amount in
USDC base units (6 decimals) as it appears in the payment challenge — use it verbatim when signing.
| Endpoint | What it does | Price | Atomic | Method | Group |
|---|---|---|---|---|---|
/api/text | Text statistics | $0.0005 | 500 | GET | utility |
/api/time | Base time & block height | $0.0005 | 500 | GET | utility |
/api/block | Base block explorer | $0.0010 | 1,000 | GET | chain |
/api/dns | DNS record lookup | $0.0010 | 1,000 | GET | security |
/api/fx | FX & asset rates | $0.0010 | 1,000 | GET | finance |
/api/geo | IP geolocation | $0.0010 | 1,000 | GET | data |
/api/hash | Cryptographic hashing | $0.0010 | 1,000 | GET | utility |
/api/price | Live crypto spot prices | $0.0010 | 1,000 | GET | market |
/api/addr | Base address balances | $0.0020 | 2,000 | GET | chain |
/api/llm | LLM chat completion | $0.0020 | 2,000 | POST | ai |
/api/tx | Base transaction decoder | $0.0020 | 2,000 | GET | chain |
/api/urlcheck | URL status & header check | $0.0020 | 2,000 | GET | web |
Every example response below is the live extensions.bazaar.info.output.example
published by the resource itself in its payment challenge.
Live crypto spot prices — Live crypto prices (CoinGecko). ?ids=bitcoin,ethereum&vs_currencies=usd
params: ?ids=bitcoin,ethereum&vs_currencies=usd
{
"bitcoin": {
"usd": 83000
},
"ethereum": {
"usd": 2600
}
}
IP geolocation — IP geolocation (country, city, ISP, lat/lon). ?ip=8.8.8.8
params: ?ip=8.8.8.8
{
"status": "success",
"country": "United States",
"city": "Mountain View"
}
DNS record lookup — DNS record lookup (A/AAAA/MX/TXT/CNAME/SOA/NS/PTR). ?host=example.com&type=A
params: ?host=example.com&type=A
{
"host": "example.com",
"type": "A",
"status": 0,
"answers": [
"93.184.215.14"
]
}
Cryptographic hashing — Cryptographic hash of a text string (sha256/sha512/sha1/md5), hex + base64. ?text=hi&alg=sha256
params: ?text=hello&alg=sha256
{
"alg": "sha256",
"hex": "2cf24dba...",
"base64": "LPDbh++..."
}
Base time & block height — Current UTC time + latest Base mainnet block number. ?format=iso|unix
params: ?format=iso|unix
{
"iso": "2026-09-30T00:00:00Z",
"unix": 1790000000,
"base_block": 12345678
}
Text statistics — Text statistics: chars, words, lines, bytes. ?text=...
params: ?text=hello%20world
{
"chars": 11,
"words": 2,
"lines": 1,
"bytes": 11
}
FX & asset rates — FX / asset exchange rates from a base currency. ?base=USD
params: ?base=USD
{
"base": "USD",
"eur": 0.9,
"btc": 1.2e-05
}
Base block explorer — Base mainnet block info by number (hash, timestamp, gas, tx count). ?number=12345678
params: ?number=12345678
{
"number": 12345678,
"hash": "0x..",
"timestamp": 1790000000,
"tx_count": 120
}
Base transaction decoder — Decode a Base transaction by hash (status, from, to, value, gas). ?hash=0x..
params: ?hash=0x…
{
"hash": "0xabc",
"status": 1,
"from": "0x..",
"to": "0x..",
"value": 0.001
}
Base address balances — Address balances on Base (ETH + USDC). ?addr=0x..
params: ?addr=0x…
{
"address": "0xabc",
"eth": 1.2,
"usdc": 34.5
}
URL status & header check — Fetch a URL and report HTTP status + security headers. ?url=https://example.com
params: ?url=https://example.com
{
"url": "https://example.com",
"status": 200,
"headers": {
"content-type": "text/html"
}
}
LLM chat completion — LLM chat completion (local 27B model). POST {messages:[{role,content}]} or ?prompt=
params: {"messages":[{"role":"user","content":"…"}]}
{
"model": "qwen3.8:27b",
"content": "Hello!"
}
How the handshake works end to end, and exactly what to sign.
402 Payment Required and sets the PAYMENT-REQUIRED header to a base64-encoded JSON
document: x402Version, resource metadata, an accepts[] array of payment
options, and an extensions.bazaar block describing input and output.accepts[0] (or the cheapest you can satisfy).
Each option states scheme, network, asset, amount,
payTo, maxTimeoutSeconds and the token's EIP-712 extra
(name/version) needed for signing.TransferWithAuthorization message with
from (your payer), to = payTo, value =
amount, validAfter = 0, validBefore = now +
maxTimeoutSeconds, and a random 32-byte nonce. Sign it with EIP-712 against the
domain name="USD Coin", version="2", chainId=8453,
verifyingContract = the USDC address. This authorizes a transfer; it does not broadcast one, and
it can only be used once.{"x402Version":2,"accepted":<accepts[0]>,"payload":{"signature":"0x…","authorization":{…}}}
and resend the identical request with that value in the PAYMENT-SIGNATURE header.200 with the JSON payload. The
PAYMENT-RESPONSE header carries the settlement receipt including the on-chain transaction
hash.Idempotency. A nonce is single-use for one hour; a mutation client that aborts before the retry
simply leaves the authorization unspent and nothing is charged.
No accounts. There is nothing to register and no key to leak. A wallet with USDC is the entire
credential.
Cost control. Prices are fixed per endpoint and visible in the 402 before you commit, so an agent can
budget deterministically. Cheap reads start at $0.0005; the 27B LLM call is $0.0020.
Failures. A rejected signature returns 402 again with the reason in the body; a
settlement failure returns 502 with the facilitator's error and no charge to you.
Everything an agent or a directory needs is available unauthenticated.
All 12 endpoints with url, method, description, price_usd, tags and example response.
OpenAPI 3.1 for every operation, with the
x402 payment contract under an x-x402 extension.
x402 discovery card: network, asset, payTo, facilitator and the resource list.
Live service manifest emitted by the resource server itself.
Short agent-first summary with the exact payment flow and every endpoint.
Runnable reference payer. Also works against any other x402 v2 resource.