> ## Documentation Index
> Fetch the complete documentation index at: https://docs.houdini.com/llms.txt
> Use this file to discover all available pages before exploring further.

# x402 Payments

> Pay-per-request API access using USDC stablecoin payments

# x402: Pay-Per-Request API Access

Houdini Partner API v2 supports the [x402 payment protocol](https://docs.cdp.coinbase.com/x402/welcome) as an alternative to traditional API key authentication. With x402, you pay for each API request using USDC on Base or Solana — no account registration or API key required.

## How It Works

The x402 protocol uses the HTTP `402 Payment Required` status code to enable pay-per-request access:

```
Client                     Houdini API                 Facilitator
  |                            |                           |
  |  1. GET /v2/tokens         |                           |
  |--------------------------->|                           |
  |  2. 402 Payment Required   |                           |
  |    (PAYMENT-REQUIRED header)|                          |
  |<---------------------------|                           |
  |                            |                           |
  |  3. Sign USDC payment      |                           |
  |  (gasless)                 |                           |
  |                            |                           |
  |  4. GET /v2/tokens         |                           |
  |    (PAYMENT-SIGNATURE hdr) |                           |
  |--------------------------->|                           |
  |                            |  5. Verify & settle       |
  |                            |-------------------------->|
  |                            |  6. Settlement confirmed  |
  |                            |<--------------------------|
  |  7. 200 OK (data)          |                           |
  |<---------------------------|                           |
```

1. Client makes a request without authentication
2. Server responds with `402` and a `PAYMENT-REQUIRED` header containing payment instructions
3. Client signs a USDC payment on one of the offered networks — **no gas required**:
   * **Base**: a `transferWithAuthorization` (EIP-3009)
   * **Solana**: a USDC transfer transaction whose network fee the facilitator pays
4. Client retries the request with the signed payment in the `PAYMENT-SIGNATURE` header
5. Server forwards the payment to the facilitator for on-chain verification and settlement
6. Facilitator confirms the USDC transfer
7. Server returns the requested data

## Supported Networks

| Network | CAIP-2 ID | USDC |
| - | - | - |
| Base | `eip155:8453` | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
| Solana | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |

Every `402` lists the networks you can pay on in its `accepts` array — one entry per network, each with its own `payTo` address. Pay on any one of them. The price can differ by network, so read the list from the response rather than hard-coding it.

## Pricing

Requests are priced by operation type in USDC. The prices below are minimums — see [Gas Coverage](#gas-coverage) for when the amount can be higher.

| Operation | Price (USDC) | Endpoints |
| - | - | - |
| **Read** | \$0.0001 | `GET /tokens`, `GET /chains`, `GET /swaps`, `GET /minMax`, `GET /rateLimits`, `POST /dex/approve`, `POST /dex/allowance`, `POST /dex/chainSignatures` |
| **Quote** | \$0.001 | `GET /quotes`, `GET /quotes/byChainAddress`, `GET /quotes/private-send` |
| **Exchange** | \$0.01 | `POST /exchanges`, `POST /exchanges/private-send`, `POST /exchanges/multi`, `GET /exchanges/multi/{id}/tx`, `POST /dex/confirmTx` |
| **Status** | \$0.0001 | `GET /exchanges/multi/{id}`, `GET /orders`, `GET /orders/{id}` |

> [!NOTE]
>
> `GET /v2/status` and `GET /v2/openapi.json` are always free and do not require payment or authentication. See [Get system health](https://docs.houdini.com/api-reference/system/get-system-health.md) for the health response.

## Gas Coverage

Because the facilitator pays the network fee for your payment, the price of a request can be higher than the minimum in the [pricing table](#pricing) when network fees rise. The exact amount for each network is always in the `amount` field of the `402` response.

* **Signed payments stay valid while they cover the cost.** If the current price rises above the amount you signed before the payment is verified, request a new `402` and sign again.
* **No fee quote, no request.** If the current network fee can't be determined, the API returns `503` before running the request. Retry later or use [API key authentication](https://docs.houdini.com/developer-hub/getting-started/authentication.md).
* **Rejected settlements aren't charged.** If the facilitator rejects a settlement, you don't get a successful response and the payment is not broadcast.
* **Header alias.** The signed retry can use the `X-PAYMENT` header instead of `PAYMENT-SIGNATURE`. If both are present, a non-empty `PAYMENT-SIGNATURE` takes precedence.

## Endpoints Not Available via x402

x402 covers swap operations (tokens, quotes, exchanges, orders and related DEX helpers), plus `GET /rateLimits` so a payer can see the limits applied to them.

Partner account endpoints — `/me`, `/commissions`, `/stats/volume`, `/stats/chart`, `/withdrawals` — require full API key authentication via the `Authorization` header and cannot be paid for per request. Without credentials they return `401 Invalid API credentials`, not `402 Payment Required`. The `partner-id` header is not a credential and does not change this.

## Rate Limits

x402 payers are rate-limited to **60 requests per minute** per payer address. This is separate from API key rate limits.

## x402 vs API Key Authentication

| Feature | x402 | API Key |
| - | - | - |
| Registration required | No | Yes |
| Pay model | Per-request | Subscription/tier |
| Authentication | USDC payment | `Authorization` header |
| Rate limits | 60 req/min per address | Tier-based |
| Best for | Agents, bots, ad-hoc usage | High-volume integrations |

When both are available, API key authentication takes priority. If a request includes an `Authorization` header (full API key access), the x402 payment flow is bypassed entirely.

> [!WARNING]
>
> The `partner-id` header is not a credential and does **not** bypass x402. Paid endpoints called with only `partner-id` still return `402`.

## Quick Start

### Prerequisites

* A wallet with USDC on Base (any EVM wallet) or on Solana. No ETH or SOL is needed for the payments themselves
* Node.js 20.18+ (for the JavaScript client)

### 1. Install Dependencies

```bash Base theme={null}
npm install @x402/fetch @x402/evm viem
```

```bash Solana theme={null}
npm install @x402/fetch @x402/svm @solana/kit
```

### 2. Create a Payment Client

```typescript Base theme={null}
import { x402Client, x402HTTPClient } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

// Create signer from your wallet's private key
const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);

// Create x402 client and register EVM payment scheme
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const httpClient = new x402HTTPClient(client);
```

```typescript Solana theme={null}
import { x402Client, x402HTTPClient } from "@x402/fetch";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { createKeyPairSignerFromBytes, getBase58Encoder } from "@solana/kit";

// Your wallet's 64-byte secret key, base58-encoded (as exported by Phantom or Solflare)
const signer = await createKeyPairSignerFromBytes(
  getBase58Encoder().encode(process.env.SVM_PRIVATE_KEY as string),
);

// Create x402 client and register Solana payment scheme
const client = new x402Client();
registerExactSvmScheme(client, { signer });
const httpClient = new x402HTTPClient(client);
```

Everything after this step is the same for both networks. Register both schemes on one client to let it pay on whichever offered network it supports.

### 3. Make Paid API Requests

```typescript theme={null}
const BASE_URL = "https://api-partner.houdiniswap.com";

const x402Fetch = async (url: string, options: RequestInit = {}) => {
  // Initial request
  const res = await fetch(url, options);
  if (res.status !== 402) return res;

  // Parse payment requirements from 402 response
  const paymentRequired = httpClient.getPaymentRequiredResponse(
    (name) => res.headers.get(name),
    undefined,
  );

  // Sign the USDC payment (gasless)
  const payload = await client.createPaymentPayload(paymentRequired);
  const paymentHeaders = httpClient.encodePaymentSignatureHeader(payload);

  // Retry with payment proof
  return fetch(url, {
    ...options,
    headers: { ...options.headers, ...paymentHeaders },
  });
};

// Get tokens — automatically handles 402 → pay → retry
const response = await x402Fetch(`${BASE_URL}/v2/tokens?term=BTC`);
const data = await response.json();
console.log(data.tokens);
```

### 4. Check Payment Receipt

Successful paid responses include a `payment-response` header:

```typescript theme={null}
const paymentResponse = response.headers.get("payment-response");
if (paymentResponse) {
  const receipt = JSON.parse(Buffer.from(paymentResponse, "base64").toString());
  console.log(receipt);
  // {
  //   "success": true,
  //   "transaction": "0xabc...def",
  //   "network": "eip155:8453",   // or "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
  //   "payer": "0x..."            // the wallet that paid
  // }
}
```

## Full Exchange Flow Example

This example demonstrates a complete swap flow using x402 payments on Base. To pay on Solana, swap in the Solana client from [step 2](#2-create-a-payment-client):

```typescript theme={null}
import { x402Client, x402HTTPClient } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const signer = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const httpClient = new x402HTTPClient(client);

const BASE = "https://api-partner.houdiniswap.com/v2";

const x402Fetch = async (url: string, options: RequestInit = {}) => {
  const res = await fetch(url, options);
  if (res.status !== 402) return res;
  const pr = httpClient.getPaymentRequiredResponse((n) => res.headers.get(n), undefined);
  const payload = await client.createPaymentPayload(pr);
  const headers = httpClient.encodePaymentSignatureHeader(payload);
  return fetch(url, { ...options, headers: { ...options.headers, ...headers } });
};

// Step 1: Look up tokens ($0.0001 each)
const btcRes = await x402Fetch(`${BASE}/tokens?term=BTC&hasCex=true`);
const btcData = await btcRes.json();
const btcToken = btcData.tokens.find((t: any) => t.chain === "bitcoin");

const ethRes = await x402Fetch(`${BASE}/tokens?term=ETH&hasCex=true`);
const ethData = await ethRes.json();
const ethToken = ethData.tokens.find((t: any) => t.chain === "ethereum");

// Step 2: Get a quote ($0.001)
const quoteRes = await x402Fetch(
  `${BASE}/quotes?from=${btcToken.id}&to=${ethToken.id}&amount=0.01`
);
const quoteData = await quoteRes.json();
const quote = quoteData.quotes[0];
console.log(`Quote: ${quote.amountOut} ETH`);

// Step 3: Create exchange ($0.01)
const exchangeRes = await x402Fetch(`${BASE}/exchanges`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    quoteId: quote.quoteId,
    addressTo: "0xYourEthAddress...",
  }),
});
const exchange = await exchangeRes.json();
console.log(`Order: ${exchange.houdiniId}, deposit to: ${exchange.depositAddress}`);

// Step 4: Poll status ($0.0001 each)
const checkStatus = async () => {
  const res = await x402Fetch(`${BASE}/orders/${exchange.houdiniId}`);
  return res.json();
};

let order = await checkStatus();
const FINAL = [4, 5, 6, 7, 8]; // COMPLETED, EXPIRED, FAILED, REFUNDED, DELETED
while (!FINAL.includes(order.status)) {
  await new Promise((r) => setTimeout(r, 30000));
  order = await checkStatus();
  console.log(`Status: ${order.status}`);
}
```

See [Order Lifecycle](https://docs.houdini.com/developer-hub/core-concepts/order-lifecycle.md) for all status codes.

> [!WARNING]
>
> Each x402 payment is an on-chain USDC transfer. Allow sufficient time between rapid sequential requests to avoid transaction failures from nonce collisions. For high-frequency access, consider using [API key authentication](https://docs.houdini.com/developer-hub/getting-started/authentication.md) instead.

## Headers

### x402 Protocol Headers

These headers are set by the x402 SDK and carry the payment data:

| Header | Direction | Description |
| - | - | - |
| `PAYMENT-REQUIRED` | Response (402) | Base64-encoded JSON with payment instructions (price, network, payTo address) |
| `PAYMENT-SIGNATURE` | Request | Base64-encoded signed payment payload (client sends this on the retry request) |
| `payment-response` | Response (200) | Base64-encoded settlement receipt (transaction hash, payer, network) |

### CORS Headers

Houdini adds the x402 headers to CORS allow/expose lists so browser-based clients can access them:

| Header | Value added |
| - | - |
| `Access-Control-Allow-Headers` | `X-PAYMENT` (allows clients to send payment headers) |
| `Access-Control-Expose-Headers` | `X-PAYMENT-RESPONSE` (allows clients to read settlement receipts) |

> [!NOTE]
>
> The CORS header names (`X-PAYMENT`, `X-PAYMENT-RESPONSE`) are the aliases the x402 SDK uses internally. The actual protocol headers your code reads/writes are `PAYMENT-REQUIRED`, `PAYMENT-SIGNATURE`, and `payment-response` as listed above.

## Payment Required Response Format

The `PAYMENT-REQUIRED` header decodes to:

```json theme={null}
{
  "x402Version": 2,
  "error": "Payment required",
  "resource": {
    "url": "https://api-partner.houdiniswap.com/v2/tokens",
    "description": "HoudiniSwap API - GET /tokens",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "100",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x...",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    },
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "amount": "100",
      "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "payTo": "...",
      "maxTimeoutSeconds": 300,
      "extra": {
        "feePayer": "..."
      }
    }
  ]
}
```

> [!NOTE]
>
> The `amount` field is in USDC atomic units (6 decimals) on both networks. `100` = \$0.0001 USDC. On Solana, `feePayer` is the account that pays the network fee for your transfer — the x402 client handles it for you.

## Other Languages

The x402 protocol has official client libraries for multiple languages:

* **Python**: [`x402`](https://pypi.org/project/x402/) with `httpx` or `requests`
* **Go**: [`github.com/coinbase/x402/go`](https://github.com/coinbase/x402/tree/main/go)
* **Axios** (Node.js): [`@x402/axios`](https://www.npmjs.com/package/@x402/axios)

See the [x402 Quickstart for Buyers](https://docs.cdp.coinbase.com/x402/quickstart-for-buyers) for setup instructions in each language.

## Error Handling

| Scenario | Response | Action |
| - | - | - |
| No auth header, x402 enabled | `402` with `PAYMENT-REQUIRED` | Sign payment and retry |
| Payment signature invalid | `402` with `payment-response` error | Check signing key and payload |
| Price rose above the signed amount | `402` with `PAYMENT-REQUIRED` | Sign again with the new amount |
| Account endpoint without API key | `401 Invalid API credentials` | Use [API key authentication](https://docs.houdini.com/developer-hub/getting-started/authentication.md) |
| Facilitator or network fee quote unavailable | `503 Service Unavailable` | Retry later or use API key auth |
| Rate limit exceeded (x402) | `429 Too Many Requests` | Wait and retry (60 req/min limit) |
| API key present | Normal auth flow | x402 is bypassed entirely |

## Further Reading

* [x402 Protocol Overview](https://docs.cdp.coinbase.com/x402/welcome) — Official protocol specification
* [x402 Quickstart for Buyers](https://docs.cdp.coinbase.com/x402/quickstart-for-buyers) — Client setup in all languages
* [x402 Network Support](https://docs.cdp.coinbase.com/x402/network-support) — Supported chains and assets


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.