Documentation Index
Fetch the complete documentation index at: https://docs.polygon.technology/llms.txt
Use this file to discover all available pages before exploring further.
This tutorial walks through setting up an x402 v2 buyer client on Polygon. By
the end, your client will automatically detect 402 Payment Required responses,
pay in USDC, and retrieve the unlocked resource.
Polygon facilitators on Amoy and
mainnet run x402 v2. Use the @x402/*
packages shown below. If you have an existing V1 integration, see
Migration: V1 to V2.
This tutorial uses test credentials and local endpoints for clarity.
In production, never expose private keys or facilitator URLs publicly.
Prerequisites
Before you begin, ensure you have:
- A crypto wallet with USDC on Polygon Amoy (or mainnet for production)
- Node.js 18+ with npm or Bun
- A service that requires payment via x402, or a local seller from the Quickstart for Sellers
For Go or Python client examples, see the official x402 buyer quickstart.
Install dependencies
Install the x402 v2 client packages and viem for signing:
Wherever bun is used, replace it with npm or your preferred package manager.
bun install @x402/fetch @x402/core @x402/evm viem dotenv
bun install @x402/axios @x402/core @x402/evm axios viem dotenv
Create a wallet signer
Create a signer from your private key. In x402 v2, the client selects the
correct network from the 402 response; you do not bind a chain-specific wallet
client.
import { privateKeyToAccount } from "viem/accounts";
import "dotenv/config";
const privateKey = process.env.EVM_PRIVATE_KEY;
if (!privateKey) throw new Error("EVM_PRIVATE_KEY not set in .env");
const signer = privateKeyToAccount(
privateKey.startsWith("0x")
? (privateKey as `0x${string}`)
: (`0x${privateKey}` as `0x${string}`)
);
console.log("Wallet address:", signer.address);
Fund this wallet with test USDC on Amoy before calling paid endpoints. See the
Polygon faucet for test POL.
Make paid requests automatically
@x402/fetch extends the native fetch API to handle 402 responses and
payment headers for you.import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import "dotenv/config";
const signer = privateKeyToAccount(
process.env.EVM_PRIVATE_KEY as `0x${string}`
);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const RESOURCE_URL =
process.env.RESOURCE_URL || "http://127.0.0.1:4021/weather";
(async () => {
const response = await fetchWithPayment(RESOURCE_URL, { method: "GET" });
const data = await response.json();
console.log("Response:", data);
if (response.ok) {
const httpClient = new x402HTTPClient(client);
const paymentResponse = httpClient.getPaymentSettleResponse((name) =>
response.headers.get(name)
);
console.log("Payment settled:", paymentResponse);
}
})();
@x402/axios adds a payment interceptor to Axios so requests retry with
payment headers automatically.import { x402Client, wrapAxiosWithPayment, x402HTTPClient } from "@x402/axios";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import axios from "axios";
import "dotenv/config";
const signer = privateKeyToAccount(
process.env.EVM_PRIVATE_KEY as `0x${string}`
);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
const api = wrapAxiosWithPayment(
axios.create({
baseURL: process.env.RESOURCE_BASE_URL || "http://127.0.0.1:4021",
}),
client
);
(async () => {
const response = await api.get("/weather");
console.log("Response:", response.data);
const httpClient = new x402HTTPClient(client);
const paymentResponse = httpClient.getPaymentSettleResponse((name) =>
response.headers[name.toLowerCase()]
);
console.log("Payment settled:", paymentResponse);
})();
The client:
- Sends the request
- Receives 402 Payment Required with payment requirements
- Pays in USDC via the seller’s facilitator (Polygon Amoy:
https://x402-amoy.polygon.technology)
- Retries with a
PAYMENT-SIGNATURE header
- Returns the unlocked response and a
PAYMENT-RESPONSE receipt header
Payment schemes
Most Polygon endpoints use the exact scheme (fixed price per request).
Servers may also advertise upto (usage-based) or batch-settlement
(high-volume batched micropayments). Register the matching scheme when the server
requires it. See x402 payment schemes.
Error handling
try {
const response = await fetchWithPayment(url, { method: "GET" });
// Handle success
} catch (error) {
if (error instanceof Error && error.message.includes("No scheme registered")) {
console.error("Network not supported: register the appropriate scheme");
} else if (error instanceof Error && error.message.includes("Payment already attempted")) {
console.error("Payment failed on retry");
} else {
console.error("Request failed:", error);
}
}
Reference
Configuration, error codes, and guardrails.
Schema
| name | type | required | example | description |
|---|
EVM_PRIVATE_KEY | string | yes | "abcd1..." | Wallet key for signing payments |
RESOURCE_URL | string | yes | "https://api.example.com/paid-endpoint" | Target API URL |
RESOURCE_BASE_URL | string | optional | "http://127.0.0.1:4021" | Base URL when using Axios |
| USDC | token | implied | USDC on Polygon | Payment asset |
The facilitator URL is configured on the seller side. Buyers do not set it
directly; the 402 response includes payment requirements for the seller’s
facilitator.
Errors
| code / case | meaning | fix |
|---|
MISSING_CONFIG | Wallet or URL not set | Verify .env keys |
No scheme registered | Client missing scheme for network | Register ExactEvmScheme with "eip155:*" |
Payment already attempted | Payment failed on retry | Check wallet balance and facilitator health |
402_LOOP | API keeps returning 402 | Ensure seller uses x402 v2 and matching network (eip155:80002 or eip155:137) |
Do / Don’t Do Guardrails
| Do | Don’t |
|---|
Use @x402/fetch or @x402/axios with v2 packages | Use legacy x402-fetch or x402-axios against Polygon facilitators |
| Test on Amoy before mainnet | Hardcode private keys in scripts |
Read receipts via x402HTTPClient.getPaymentSettleResponse() | Parse PAYMENT-RESPONSE headers manually |
Use wrapFetchWithPayment or wrapAxiosWithPayment | Re-implement 402 logic yourself |
References