Skip to main content
Three npm packages, a Solana wallet with USDC, and you’re querying smart money data across Polymarket, Hyperliquid, and Meteora. The entire flow is gasless — no SOL required.

What You Need

  • Solana wallet with USDC (that’s it — x402 is gasless, no SOL needed)
  • Node.js 18+ or Bun runtime
  • Three packages:
npm install @x402/core @x402/svm @solana/kit

How x402 Works

The x402 protocol turns HTTP 402 (“Payment Required”) into a machine-readable payment flow. No API keys, no accounts. Your wallet is your identity.
1

First Request Returns 402

You call an endpoint. The server responds with HTTP 402 and a PAYMENT-REQUIRED header containing the exact USDC price and payment instructions.
2

Sign Payment Locally

Your agent parses the payment requirement, signs a USDC transfer with your Solana keypair, and builds a payment signature. No funds move yet.
3

Re-send With Payment

Attach the PAYMENT-SIGNATURE header and re-send the same request. The server verifies the signature, settles the payment on-chain, and returns your data.
4

Verify Settlement

The 200 response includes a PAYMENT-RESPONSE header with on-chain settlement proof.
If the query fails (timeout, server error, bad parameters), no payment is settled. You only pay for successful responses.

Setup Code

One-time setup that your agent reuses across all requests:
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { toClientSvmSigner } from "@x402/svm";
import { getBase58Encoder, createKeyPairSignerFromBytes } from "@solana/kit";
import type { PaymentRequired, SettleResponse } from "@x402/core/types";

// Load your Solana keypair
const bytes = getBase58Encoder().encode(process.env.SOLANA_PRIVATE_KEY!);
const signer = await createKeyPairSignerFromBytes(bytes);

// Initialize x402 client
const client = new x402Client();
registerExactSvmScheme(client, { signer: toClientSvmSigner(signer) });
const httpClient = new x402HTTPClient(client);

The paidFetch Function

Wrap the two-step handshake in a reusable function:
const BASE_URL = "https://agent.metengine.xyz";

async function paidFetch(
  path: string,
  options?: { method?: string; body?: Record<string, unknown> },
): Promise<{ data: unknown; settlement: SettleResponse; price: number }> {
  const method = options?.method ?? "GET";
  const url = `${BASE_URL}${path}`;
  const fetchOpts: RequestInit = { method };

  if (options?.body) {
    fetchOpts.headers = { "Content-Type": "application/json" };
    fetchOpts.body = JSON.stringify(options.body);
  }

  // Step 1: Get 402 with price
  const initial = await fetch(url, fetchOpts);
  if (initial.status !== 402)
    throw new Error(`Expected 402, got ${initial.status}`);
  const body = await initial.json();

  // Step 2: Parse payment requirements
  const paymentRequired: PaymentRequired =
    httpClient.getPaymentRequiredResponse(
      (name) => initial.headers.get(name),
      body,
    );
  const price = Number(paymentRequired.accepts[0]!.amount);

  // Step 3: Sign payment locally
  const paymentPayload =
    await httpClient.createPaymentPayload(paymentRequired);
  const paymentHeaders =
    httpClient.encodePaymentSignatureHeader(paymentPayload);

  // Step 4: Re-send with payment
  const paid = await fetch(url, {
    ...fetchOpts,
    headers: {
      ...(fetchOpts.headers as Record<string, string>),
      ...paymentHeaders,
    },
  });

  if (paid.status !== 200) {
    const err = await paid.json();
    throw new Error(
      `Payment failed (${paid.status}): ${JSON.stringify(err)}`,
    );
  }

  const paidBody = (await paid.json()) as { data: unknown };

  // Step 5: Extract settlement proof
  const settlement = httpClient.getPaymentSettleResponse(
    (name) => paid.headers.get(name),
  );

  return { data: paidBody.data, settlement, price };
}

Your First Query

Fetch trending Polymarket markets:
const { data, price } = await paidFetch(
  "/api/v1/markets/trending?timeframe=24h&limit=5"
);
console.log(`Paid $${price} USDC for ${(data as any[]).length} markets`);
POST endpoint (smart money intelligence for a specific market):
const { data } = await paidFetch("/api/v1/markets/intelligence", {
  method: "POST",
  body: { condition_id: "0xabc123...", top_n_wallets: 10 },
});

Session Memory for Agents

If your agent runs across multiple sessions, persist setup details to avoid re-reading the full spec every time. Store in ~/.claude/agents/metengine-memory.md:
  • Wallet address and balance verification status
  • Working bootstrap code snippet
  • Last 10 endpoint calls with latency and cost
  • Any quirks or fallback strategies discovered
This cuts token overhead by roughly 80% on subsequent sessions.

Error Handling

StatusMeaningAction
402Payment requiredNormal first step. Parse and sign.
200SuccessData returned. Payment settled.
504TimeoutNo charge. Retry with narrower parameters or use a fallback endpoint.
503/429Rate limitNo charge. Back off per Retry-After header.
500Server errorNo charge. Some endpoints have known fallbacks (see Endpoints).

Next Steps