Skip to main content

x402: Pay-Per-Request API Access

HoudiniSwap Partner API v2 supports the x402 payment protocol as an alternative to traditional API key authentication. With x402, you pay for each API request using USDC on supported EVM chains — 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                    HoudiniSwap API              Facilitator
  |                            |                           |
  |  1. GET /v2/tokens         |                           |
  |--------------------------->|                           |
  |  2. 402 Payment Required   |                           |
  |    (PAYMENT-REQUIRED header)|                          |
  |<---------------------------|                           |
  |                            |                           |
  |  3. Sign USDC authorization|                           |
  |  (EIP-3009, 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 transferWithAuthorization (EIP-3009) — no gas required
  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

NetworkCAIP-2 IDUSDC Contract
Base (default)eip155:84530x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Additional EVM networks (Ethereum, Polygon, Arbitrum) can be enabled — contact us for availability.

Pricing

Requests are priced by operation type in USDC:
OperationPrice (USDC)Endpoints
Read$0.0001GET /tokens, GET /chains, GET /swaps, GET /minMax, GET /rateLimits, POST /dex/approve, POST /dex/allowance, POST /dex/chainSignatures
Quote$0.001GET /quotes
Exchange$0.01POST /exchanges, POST /dex/confirmTx
Status$0.0001GET /orders, GET /orders/{id}
GET /v2/health and GET /v2/openapi.json are always free and do not require payment or authentication.

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

Featurex402API Key
Registration requiredNoYes
Pay modelPer-requestSubscription/tier
AuthenticationUSDC paymentAuthorization header
Rate limits60 req/min per addressTier-based
Best forAgents, bots, ad-hoc usageHigh-volume integrations
When both are available, API key authentication takes priority. If a request includes an Authorization header (full API key access) or a partner-id header (public read-only partner access), the x402 payment flow is bypassed entirely.

Quick Start

Prerequisites

  • An EVM wallet with USDC on a supported network
  • Node.js 18+ (for the JavaScript client)
  • USDC on Base (amounts are tiny — a full exchange flow costs ~$0.012 total)

1. Install Dependencies

npm install @x402/fetch @x402/evm viem

2. Create a Payment Client

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);

3. Make Paid API Requests

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 EIP-3009)
  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:
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",
  //   "payer": "0x..."
  // }
}

Full Exchange Flow Example

This example demonstrates a complete swap flow using x402 payments:
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 status = await checkStatus();
const DONE = [4, 5, 6, 7, 8]; // completed, expired, failed, refunded, deleted
while (!DONE.includes(status.status)) {
  await new Promise((r) => setTimeout(r, 30000)); // Wait 30s
  status = await checkStatus();
  console.log(`Status: ${status.status}`);
}
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 instead.

Headers

x402 Protocol Headers

These headers are set by the x402 SDK and carry the payment data:
HeaderDirectionDescription
PAYMENT-REQUIREDResponse (402)Base64-encoded JSON with payment instructions (price, network, payTo address)
PAYMENT-SIGNATURERequestBase64-encoded signed payment payload (client sends this on the retry request)
payment-responseResponse (200)Base64-encoded settlement receipt (transaction hash, payer, network)

CORS Headers

HoudiniSwap adds the x402 headers to CORS allow/expose lists so browser-based clients can access them:
HeaderValue added
Access-Control-Allow-HeadersX-PAYMENT (allows clients to send payment headers)
Access-Control-Expose-HeadersX-PAYMENT-RESPONSE (allows clients to read settlement receipts)
The CORS header names (X-PAYMENT, X-PAYMENT-RESPONSE) are the aliases used by the x402 SDK 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:
{
  "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"
      }
    }
  ]
}
The amount field is in USDC atomic units (6 decimals). 100 = $0.0001 USDC.

Other Languages

The x402 protocol has official client libraries for multiple languages: See the x402 Quickstart for Buyers for setup instructions in each language.

Error Handling

ScenarioResponseAction
No auth header, x402 enabled402 with PAYMENT-REQUIREDSign payment and retry
Payment signature invalid402 with payment-response errorCheck signing key and payload
Facilitator unavailable503 Service UnavailableRetry later or use API key auth
Rate limit exceeded (x402)429 Too Many RequestsWait and retry (60 req/min limit)
API key presentNormal auth flowx402 is bypassed entirely

Further Reading