For agents · x402 v2 · USDC on Base

Find it, pay it,
call it.

13 paid routes. No signup or API key: each call is paid on its own in USDC on Base. Give your agent the skill file, or plug in the MCP server and set a spend cap.

How an agent pays

One 402, one signature, one retry.

The same flow on every route. Errors are not charged, apart from the exceptions noted on a route in the docs.

01

Call

Send the request with no payment header. Paid routes answer HTTP 402.

02

Read the challenge

Base64-decode the PAYMENT-REQUIRED response header (x402 v2 JSON). The 402 body is empty JSON {}.

03

Check it

Pay only if scheme is exact, network is eip155:8453 (Base), asset is USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, payTo is 0x5b32c973596078a967562ca652761404f19be0e9 and amount matches the listed price.

04

Sign

Sign an EIP-3009 transferWithAuthorization for that USDC amount to payTo with an x402 client library. No gas, no on-chain transaction from you.

05

Retry

Send the identical request again with the PAYMENT-SIGNATURE header (x402 v2). X-PAYMENT is the legacy v1 header; do not rely on it.

06

Use the result

200 returns the JSON plus a PAYMENT-RESPONSE receipt header. Settlement happens only after the route succeeds; error responses are not charged.

The flow with curl
# 1. Unpaid call: HTTP 402, challenge in the PAYMENT-REQUIRED header
curl -si https://horizonpulse.dev/api/pulse | grep -i '^payment-required' | cut -d' ' -f2 | base64 -d

# 2. Check scheme exact, network eip155:8453, USDC, payTo and amount
# 3. Sign EIP-3009 with an x402 client, retry with PAYMENT-SIGNATURE
# 4. 200 + JSON, receipt in the PAYMENT-RESPONSE header
agent.mjs
// npm i @x402/core@2.27.0 @x402/evm@2.27.0 viem@2.37.5
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const PAY_TO = "0x5b32c973596078a967562ca652761404f19be0e9"; // the only payTo to accept
const account = privateKeyToAccount(process.env.PRIVATE_KEY); // dedicated low-balance wallet
const http = new x402HTTPClient(
  x402Client.fromConfig({
    schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
    policies: [(v, reqs) => reqs.filter((r) => r.payTo.toLowerCase() === PAY_TO)],
    spendControls: { maxAmountPerPayment: "$0.05" }, // per-payment cap
  }),
);

const url = "https://horizonpulse.dev/api/pulse";
const r1 = await fetch(url); // 402 + PAYMENT-REQUIRED
const req = http.getPaymentRequiredResponse((h) => r1.headers.get(h), await r1.json());
const payload = await http.createPaymentPayload(req); // throws if payTo or cap don't match
const r2 = await fetch(url, { headers: http.encodePaymentSignatureHeader(payload) });
console.log(r2.status, (await r2.json()).assets.BTC.priceUsd); // 200, $0.005 USDC on Base
Skill

A skill file your agent can follow.

/skill.md is plain markdown: when to use each route, exact prices, the pay flow step by step, error and billing rules, and safety checks. It is generated from /openapi.json, so prices match the 402s.

Load it
curl https://horizonpulse.dev/skill.md
MCP

Every route as a tool, paid with a cap.

The local MCP server (stdio, Node) exposes each route as a tool plus free catalog, quote and demo tools, and pays the x402 challenge with the official x402 client from a wallet you configure.

Guards

Pays only0x5b32c973596078a967562ca652761404f19be0e9AssetUSDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 on BasePrice checkRefuses any amount above the price in /openapi.jsonCapsHP_MAX_USD_PER_CALL (default $0.05), HP_MAX_USD_TOTAL per session (default $1)No keyQuote only, never pays

Wallet

Use a dedicated buyer wallet with a small USDC balance on Base. The key stays in your MCP config on your machine; it is never sent to Horizon Pulse, only the signed payment is. No ETH or gas is needed.

Source and README on GitHub

Install
git clone https://github.com/horizon-pulse/horizon-pulse.git
cd horizon-pulse/mcp
npm install   # installs pinned deps (@x402/core + @x402/evm 2.27.0) and builds dist/
MCP client config (Claude Desktop, Cursor and other stdio clients)
{
  "mcpServers": {
    "horizon-pulse": {
      "command": "node",
      "args": ["/absolute/path/to/horizon-pulse/mcp/dist/index.js"],
      "env": {
        "HP_PRIVATE_KEY": "0x...key of a dedicated, low-balance buyer wallet",
        "HP_MAX_USD_PER_CALL": "0.05",
        "HP_MAX_USD_TOTAL": "1"
      }
    }
  }
}
Hosted MCP at https://horizonpulse.dev/mcp (for MCP clients that can pay x402)
{
  "mcpServers": {
    "horizon-pulse": { "url": "https://horizonpulse.dev/mcp" }
  }
}
Demo · dry run

From a domain name to a payment, without paying.

mcp/examples/demo.mjs starts with only horizonpulse.dev, finds the routes through /.well-known/x402, llms.txt and skill.md, then drives the local MCP server: catalog, quote, then a paid tool. It runs with HP_DRY_RUN=1 and no private key, so it never signs and never pays. It prints the real 402 requirements and the exact authorization a client would sign, with the payTo, price and cap checks.

Run it yourself (dry run)
git clone https://github.com/horizon-pulse/horizon-pulse.git
cd horizon-pulse/mcp
npm install
node examples/demo.mjs horizonpulse.dev   # dry run: never signs, never pays
Captured output, 2026-10-08 03:48 UTC (dry run: nothing signed, nothing paid; trimmed where marked)
DRY RUN: this demo never signs and never pays. No private key is loaded.
Start: only the domain "horizonpulse.dev"  (2026-10-08T03:48:50.090Z)

=== 1. Discover routes from the domain ===
GET https://horizonpulse.dev/.well-known/x402 → 15 paid operations
  … [trimmed: the 15 operations, GET/POST /api/…]
GET https://horizonpulse.dev/llms.txt → published payTo 0x5b32c973596078a967562ca652761404f19be0e9
  POST `/mcp` — hosted MCP (streamable HTTP, stateless); `initialize` and `tools/list` free, `tools/call` returns the same
GET https://horizonpulse.dev/skill.md → HTTP 200, 127 lines (pay flow + safety checks)
  [PASS] llms.txt payTo equals the MCP server's hardcoded payTo 0x5b32c973596078a967562ca652761404f19be0e9

=== 2. Local MCP server (stdio), HP_DRY_RUN=1, no HP_PRIVATE_KEY ===
tools/list → 16 tools: catalog, quote, demo, extract, fetch, funding, gas, http, pdf, portfolio, pulse, screenshot, search, signals, x402_check, yield
catalog → 13 routes from https://horizonpulse.dev/openapi.json; mode: dry-run (never pays); payTo 0x5b32c973596078a967562ca652761404f19be0e9; caps $0.05/call, $1.00/session
  … [trimmed: 13 tools with method, path and price]
quote pulse → HTTP 402, result "quote", $0.005, wouldPayWithCurrentConfig=false
tools/call pulse → result "quote" (not paid). Server says: HP_DRY_RUN is set: not paying. This call costs $0.005 USDC on Base to 0x5b32c973596078a967562ca652761404f19be0e9.
session spend after the dry run: $0.00 of $1.00

=== 3. Real HTTP 402 from GET /api/pulse (unpaid) ===
HTTP 402; PAYMENT-REQUIRED header: 3040 chars base64, decoded:
{
  "x402Version": 2,
  "resource": "https://horizonpulse.dev/api/pulse",
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "5000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x5b32c973596078a967562ca652761404f19be0e9",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USD Coin",
        "version": "2"
      }
    }
  ]
}

=== 4. Checks before signing ===
  [PASS] scheme "exact" is exact
  [PASS] network eip155:8453 is Base mainnet
  [PASS] asset 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 is Base USDC
  [PASS] payTo 0x5b32c973596078a967562ca652761404f19be0e9 equals the pinned payTo
  [PASS] amount 5000 ($0.005) ≤ advertised price 5000 in /openapi.json
  [PASS] amount $0.005 ≤ per-call cap $0.05
  [PASS] amount $0.005 ≤ remaining session budget $1.00

=== 5. What WOULD be signed (EIP-712 TransferWithAuthorization), NOT SIGNED ===
{
  "domain": {
    "name": "USD Coin",
    "version": "2",
    "chainId": 8453,
    "verifyingContract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
  },
  "types": { … [trimmed: TransferWithAuthorization(from, to, value, validAfter, validBefore, nonce)] },
  "primaryType": "TransferWithAuthorization",
  "message": {
    "from": "<your buyer wallet address>",
    "to": "0x5b32c973596078a967562ca652761404f19be0e9",
    "value": "5000",
    "validAfter": "0",
    "validBefore": "1791431633",
    "nonce": "0xfbdff919058d838a9ac7733f272baee4e13265cce7c25d76b6a45962415af9d6"
  }
}

With a key, the client would sign this, retry GET /api/pulse with a PAYMENT-SIGNATURE header, and pay $0.005 USDC on Base only if the route succeeds.
DRY RUN complete: nothing was signed, nothing was sent, no payment happened.
For API owners

Want your own API found by agents?

Agent promotion gets your API found and used by AI agents: a description pack (llms.txt, skill.md, an OpenAPI summary and a Coinbase Bazaar description), directory submissions or ready-to-submit packs, and a monthly agent-traffic report. See Agent promotion →

Routes

Tools and prices, straight from the API.

Tool names are the same in the skill file and both MCP servers. Inputs marked * are required. Full schemas and examples are in the docs.

ToolRouteInputsUSDC per call
pulseGET /api/pulse
BTC, ETH and SOL spot prices with 24h momentum (Coinbase Exchange, CoinGecko fallback)
none$0.005
signalsGET /api/signals
RSI, MACD and Bollinger bands plus perpetual funding (CoinGecko, OKX)
none$0.015
yieldGET /api/yield
Ranked DeFi yields, pools with TVL of $10M or more (DefiLlama)
none$0.02
portfolioGET /api/portfolio
Base and Ethereum balances, rule-based risk score and rebalance flags (not financial advice)
address*$0.04
gasGET /api/gas
Base and Ethereum fees with a suggested max fee (public RPC)
none$0.01
fundingGET /api/funding
BTC, ETH and SOL perpetual funding with a crowding hint (OKX)
none$0.01
fetchGET /api/fetch
Any public URL as clean text or markdown
url*$0.02
httpGET/POST /api/http
Universal HTTP proxy: your method, headers and body
url*, method, headers, body$0.01
extractGET/POST /api/extract
Page to structured fields, or your own CSS selectors
url*, fields, html$0.015
x402_checkGET /api/x402-check
Audit any x402 endpoint without paying it
url*, method, body$0.01
screenshotGET /api/screenshot
Headless Chromium render to PNG or JPEG
url*, width, height, fullPage, format, delayMs$0.02
searchGET /api/search
Web search with the top pages as clean text and sources
q*, n$0.03
pdfGET /api/pdf
PDF URL to text per page plus metadata
url*, pages$0.02