Skip to main content

Overview

x402x-fetch extends native fetch. When encountering HTTP 402 (Payment Required), it automatically parses the server’s payment requirements, creates a signature (Permit / EIP-3009 / Permit2), and retries the original request with the X-Payment header.

Key features

  • Seamless integration: compatible with native fetch API
  • Multiple authorization types: EIP-3009 / EIP-2612 Permit / Permit2
  • Multi-chain support: common EVM chains built-in, custom chains supported
  • Safety: max amount cap, signature validation

Install and dependencies

npm i x402x-fetch viem
Dependencies (example versions):
{
  "viem": "^2.21.26",
  "zod": "^3.24.2",
  "x402x": "workspace:^"
}

Quick start

One-liner wrapper

import { wrapFetchWithPayment } from "x402x-fetch";
const fetchWithPay = wrapFetchWithPayment(fetch, walletClient, "1000000");

Full example

import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { wrapFetchWithPayment } from "x402x-fetch";
import { bscTestnet } from "viem/chains";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const client = createWalletClient({ account, transport: http(), chain: bscTestnet });

// Pay up to 10 USDC (6 decimals)
const fetchWithPay = wrapFetchWithPayment(fetch, client, "10000000");

const response = await fetchWithPay("https://api.example.com/premium", { method: "GET" });
const data = await response.json();
console.log("✅", data);

Parameters

  • walletClient: to sign
  • maxValue: maximum allowed payment amount (highly recommended)
  • paymentRequirementsSelector: custom selection for multi-option resources

Workflow

1) Request a paid resource
2) Receive 402 response (contains x402Version and accepts list)
3) Select/validate payment requirements (amount ≤ maxValue)
4) Generate X-Payment header (signature)
5) Automatically retry the original request with the payment header
6) Receive 200 and data (server verifies and settles)

Typical use cases

  • Access paid APIs/content
  • Consistent UX across environments (Browser / Node)

API reference (condensed)

function wrapFetchWithPayment(
  fetch: typeof globalThis.fetch,
  walletClient: WalletClient,
  maxValue?: string,
  paymentRequirementsSelector?: (response: Response402) => PaymentRequirements
): typeof globalThis.fetch
Supported paymentType: eip3009 | permit | permit2

Multi-chain support

You can create a signer by chain name / viem Chain / custom config:
import { createEvmSigner, wrapFetchWithPayment } from "x402x-fetch";
import { polygon } from "viem/chains";

// Chain name
const signer1 = createEvmSigner("bsc-testnet", "0xYourPrivateKey");

// viem Chain
const signer2 = createEvmSigner(polygon, "0xYourPrivateKey");

// Custom
const signer3 = createEvmSigner({ chainId: 56, name: "BSC", rpcUrl: "https://rpc" }, "0xKey");

const fetchWithPay = wrapFetchWithPayment(fetch, signer1);

Usage examples

GET

const res = await fetchWithPay("https://api.example.com/premium-data");
const data = await res.json();

POST (with body)

const res = await fetchWithPay("https://api.example.com/compute", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ complexity: 10 }),
});

Custom headers

await fetchWithPay("https://api.example.com/data", {
  headers: {
    Authorization: "Bearer token",
    "X-Custom": "value",
  },
});

Error handling and amount protection

const fetchPay = wrapFetchWithPayment(fetch, client, "1000000");
try {
  const res = await fetchPay("https://api.example.com/data");
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
} catch (e) {
  if (e instanceof Error && e.message.includes("exceeds maximum")) {
    // Server asked for an amount exceeding the cap
  }
}

Read payment response header

const res = await fetchWithPay("https://api.example.com/premium");
const header = res.headers.get("X-Payment-Response");
if (header) {
  const info = JSON.parse(Buffer.from(header, "base64").toString());
  console.log(info.transactionHash, info.payer);
}

Best practices

  • Set a reasonable maxValue in production
  • Centralize logging and error classification for debugging
  • In browsers, use a wallet connection (don’t expose private keys)

Resources

  • Source: https://github.com/WTFLabs-WTF/x402x/tree/main/typescript/packages/x402x-fetch
  • Issues: https://github.com/WTFLabs-WTF/x402x/issues