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’re Building Toward
Here’s an AI agent querying Polymarket smart money data in real time:
What You Need
- Solana wallet with USDC (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.
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.
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.
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.
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
| Status | Meaning | Action |
|---|
| 402 | Payment required | Normal first step. Parse and sign. |
| 200 | Success | Data returned. Payment settled. |
| 504 | Timeout | No charge. Retry with narrower parameters or use a fallback endpoint. |
| 503/429 | Rate limit | No charge. Back off per Retry-After header. |
| 500 | Server error | No charge. Some endpoints have known fallbacks (see Endpoints). |
Next Steps