---
name: horizon-pulse
description: Call Horizon Pulse pay-per-call APIs at https://horizonpulse.dev and pay each request in USDC on Base with x402 v2 (no signup or API key). Use when you need web search with sources, a web page as clean text, structured page fields, a page screenshot, PDF text, a proxied HTTP request, an x402 endpoint audit, or crypto data (spot prices, indicators, perp funding, gas, DeFi yields, wallet holdings).
---

# Horizon Pulse: pay-per-call APIs for agents (x402 v2, USDC on Base)

13 paid routes, $0.005 to $0.04 per call. Every call is paid individually with an x402 v2 `exact` payment in USDC on Base mainnet. No account, no API key.

- Base URL: https://horizonpulse.dev (call and pay this host only)
- Network: Base mainnet, CAIP-2 `eip155:8453`
- Asset: USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` (6 decimals; 10000 atomic = $0.01)
- payTo: `0x5b32c973596078a967562ca652761404f19be0e9` (the only address to pay)
- Facilitator: Coinbase CDP (verifies the payment, settles it only after the route succeeds)

## 1. Discover

- `GET https://horizonpulse.dev/.well-known/x402`: x402 resource list (`METHOD URL` strings)
- `GET https://horizonpulse.dev/openapi.json`: OpenAPI 3.1, typed inputs, response examples, per-operation `x-payment-info` (price, asset, network, payTo)
- `GET https://horizonpulse.dev/llms.txt`: plain-text catalog and rules
- `GET https://horizonpulse.dev/api/demo/{route}`: free sample of a route's real output on a fixed input (no payment)

## 2. Pick a route

`*` = required input. GET routes take query parameters; POST routes take a JSON body.

| Tool | Method | Path | Price (USDC) | Atomic | Inputs | What it returns |
| --- | --- | --- | --- | --- | --- | --- |
| `pulse` | GET | `/api/pulse` | $0.005 | 5000 | none | BTC, ETH and SOL spot prices with 24h momentum (Coinbase Exchange, CoinGecko fallback) |
| `signals` | GET | `/api/signals` | $0.015 | 15000 | none | RSI, MACD and Bollinger bands plus perpetual funding (CoinGecko, OKX) |
| `yield` | GET | `/api/yield` | $0.02 | 20000 | none | Ranked DeFi yields, pools with TVL of $10M or more (DefiLlama) |
| `portfolio` | GET | `/api/portfolio` | $0.04 | 40000 | `address`* | Base and Ethereum balances, rule-based risk score and rebalance flags (not financial advice) |
| `gas` | GET | `/api/gas` | $0.01 | 10000 | none | Base and Ethereum fees with a suggested max fee (public RPC) |
| `funding` | GET | `/api/funding` | $0.01 | 10000 | none | BTC, ETH and SOL perpetual funding with a crowding hint (OKX) |
| `fetch` | GET | `/api/fetch` | $0.02 | 20000 | `url`* | Any public URL as clean text or markdown |
| `http` | GET/POST | `/api/http` | $0.01 | 10000 | `url`*, `method`, `headers`, `body` | Universal HTTP proxy: your method, headers and body |
| `extract` | GET/POST | `/api/extract` | $0.015 | 15000 | `url`*, `fields`, `html` | Page to structured fields, or your own CSS selectors |
| `x402_check` | GET | `/api/x402-check` | $0.01 | 10000 | `url`*, `method`, `body` | Audit any x402 endpoint without paying it |
| `screenshot` | GET | `/api/screenshot` | $0.02 | 20000 | `url`*, `width`, `height`, `fullPage`, `format`, `delayMs` | Headless Chromium render to PNG or JPEG |
| `search` | GET | `/api/search` | $0.03 | 30000 | `q`*, `n` | Web search with the top pages as clean text and sources |
| `pdf` | GET | `/api/pdf` | $0.02 | 20000 | `url`*, `pages` | PDF URL to text per page plus metadata |

Rules of thumb: research a question with sources → `/api/search`; read one page → `/api/fetch`; specific values from a page → `/api/extract` with `fields`; call any public API with your own method/headers/body → `/api/http`; PDF → `/api/pdf`; check an unknown x402 endpoint before paying it → `/api/x402-check`.

## 3. Pay and call (x402 v2)

1. **Call** the route with no payment header, e.g. `GET https://horizonpulse.dev/api/pulse`. Expect `HTTP 402`.
2. **Read the challenge**: base64-decode the `PAYMENT-REQUIRED` response header and parse it as JSON. The 402 body is `{}`. Shape: `{ x402Version: 2, resource: {url, description, ...}, accepts: [{ scheme, network, amount, asset, payTo, maxTimeoutSeconds, extra: { name: "USD Coin", version: "2" } }] }`.
3. **Check it before paying.** Pay only if `scheme` is `exact`, `network` is `eip155:8453`, `asset` is `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`, `payTo` is `0x5b32c973596078a967562ca652761404f19be0e9` and `amount` equals the atomic price in the table. If anything differs, do not pay.
4. **Sign** an EIP-3009 `transferWithAuthorization` for exactly `amount` to `payTo` (USDC EIP-712 domain: name "USD Coin", version "2", chainId 8453), valid for at most `maxTimeoutSeconds`. Use an x402 client library rather than hand-rolling it (Node: `@x402/core` + `@x402/evm`; Python: `x402`). The payer needs USDC on Base; no ETH or gas is needed.
5. **Retry** the identical request (same method, URL and body) with header `PAYMENT-SIGNATURE: {base64 JSON payment payload}`. This is the x402 v2 header; `X-PAYMENT` is the legacy v1 header and is not the settle path here.
6. **Use the result**: `200` returns the route's JSON. The `PAYMENT-RESPONSE` header is a base64 JSON receipt `{ success, transaction, network, payer }`.
7. **If you get 402 again**, decode `PAYMENT-REQUIRED` and read `error` (for example an insufficient USDC balance). Do not retry in a loop.

Optional: `OPTIONS {route}` returns the same challenge without charging.

### Node (official x402 client, v2)

```js
// npm i @x402/core@2.27.0 @x402/evm@2.27.0 viem
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.BUYER_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() === "0x5b32c973596078a967562ca652761404f19be0e9")],
    spendControls: { maxAmountPerPayment: "$0.05" },
  }),
);

const url = "https://horizonpulse.dev/api/pulse";
const r1 = await fetch(url);                                   // 402
const challenge = http.getPaymentRequiredResponse((h) => r1.headers.get(h), await r1.json());
const payload = await http.createPaymentPayload(challenge);    // signs EIP-3009
const r2 = await fetch(url, { headers: http.encodePaymentSignatureHeader(payload) });
console.log(r2.status, await r2.json());
console.log(http.getPaymentSettleResponse((h) => r2.headers.get(h))); // receipt
```

## 4. Or use the MCP server

**Local stdio MCP server (pays for you, with caps).** Exposes every route above as a tool (same names as the Tool column) plus free `catalog`, `quote` and `demo` tools. It reads the live catalog from `/openapi.json`, pays only `0x5b32c973596078a967562ca652761404f19be0e9` in Base USDC, refuses any amount above the listed price, and enforces `HP_MAX_USD_PER_CALL` (default $0.05) and `HP_MAX_USD_TOTAL` per session (default $1). Without `HP_PRIVATE_KEY` it only quotes and never pays.

```sh
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/
```

```json
{
  "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"
      }
    }
  }
}
```

Source and full README: https://github.com/horizon-pulse/horizon-pulse/tree/main/mcp

**Hosted MCP** (`https://horizonpulse.dev/mcp`, streamable HTTP, stateless): `initialize` and `tools/list` are free; `tools/call` returns the same x402 challenge as REST, so it needs an MCP client that can pay x402.

## 5. Errors and billing

- You pay only for a successful response. Errors return `{ "ok": false, "error": "...", "code": "..." }` and are not charged: 400 bad input, 413 too large, 415 wrong content type, 422 nothing usable, 404 no search results, 502 upstream failure, 503 provider unavailable, 504 timeout.
- Exceptions: `/api/http` returns upstream 4xx/5xx as data (charged); `/api/x402-check` bills any HTTP answer from the target.

## 6. Safety rules

- Call and pay only `https://horizonpulse.dev`. Never pay a `payTo` other than `0x5b32c973596078a967562ca652761404f19be0e9`.
- Use a dedicated buyer wallet holding only a small USDC balance, and a per-call cap. Never put a private key in a prompt, a tool argument, a URL or a log.
- Check the price with a free `/api/demo/{route}` sample or the unpaid 402 before paying; do not invent routes or prices.

Services for API owners (not API routes; never pay them via x402): Bazaar listing fix at https://horizonpulse.dev/listing-fix, agent promotion at https://horizonpulse.dev/agent-promotion.

Contact: horizonpulse.co@proton.me
