Skip to main content

What is x402?

x402 is an open payment standard built on the HTTP 402 Payment Required status code. It lets clients pay for API access per-request using USDC stablecoins on Base or Solana, with no accounts, API keys, or subscriptions needed. Exa supports x402 on two endpoints: /search and /contents. When you send a request without an API key or payment header, Exa responds with 402 and a PAYMENT-REQUIRED header containing pricing details and the supported payment networks. Your client signs a USDC payment, retries the request with a PAYMENT-SIGNATURE header, and receives the results once settlement confirms on-chain. This is ideal for AI agents that need to autonomously pay for web search without pre-provisioned credentials.
x402 and API key access are independent. If your request includes an x-api-key or Authorization: Bearer header, the normal API key billing flow is used and x402 is bypassed entirely.

Supported endpoints

EndpointMethodDescription
/searchPOSTWeb search with all search types (instant, auto, fast, deep, deep-lite, deep-reasoning)
/contentsPOSTContent retrieval by URL or document ID
All other endpoints are not available via x402.

How it works

x402 payment flow sequence diagram: Client sends request to server, gets 402 with PAYMENT-REQUIRED header, creates payment payload, retries with PAYMENT-SIGNATURE, server verifies via facilitator, does work, settles on-chain, returns 200 with results and PAYMENT-RESPONSE

Step 1: Discovery

Send a request to a supported endpoint without an API key or payment header:
curl -X POST "https://api.exa.ai/search" \
  -H "Content-Type: application/json" \
  -d '{"query": "best machine learning frameworks", "numResults": 5}'
You’ll receive a 402 response with a base64-encoded PAYMENT-REQUIRED header. Decoded, it looks like:
{
  "x402Version": 2,
  "resource": {
    "url": "https://api.exa.ai/search",
    "description": "Exa /search endpoint"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "7000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x...",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USD Coin", "version": "2" }
    },
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "amount": "7000",
      "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "payTo": "...",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USD Coin", "version": "2", "feePayer": "..." }
    }
  ]
}
The amount is in USDC atomic units (6 decimals), so "7000" = $0.007. The client can pay with any advertised accepts entry it supports. Solana entries include facilitator-provided fields such as extra.feePayer; use the exact entry from the PAYMENT-REQUIRED header when constructing the payment.

Step 2: Pay and retry

Sign the payment with your wallet and re-send the request with a PAYMENT-SIGNATURE header containing your base64-encoded payment payload. The x402 client SDKs handle this automatically.

Step 3: Settlement

Exa verifies your payment signature with the facilitator, then starts on-chain settlement in parallel with processing your request. The response is held until settlement confirms. On success, you receive:
  • HTTP 200 with your results
  • A PAYMENT-RESPONSE header containing the settlement receipt (base64-encoded), including the on-chain transaction hash
If settlement fails, you get 402 with both PAYMENT-RESPONSE (error details) and PAYMENT-REQUIRED (so you can retry).

Pricing

x402 uses the same bundled pricing as API key billing. Prices are calculated upfront based on your request parameters (not actual results returned).

Search (/search)

Search typeBase price (up to 10 results)Per result beyond 10
instant, auto, fast$0.007 / requestN/A (capped at 10)
deep-lite$0.012 / requestN/A (capped at 10)
deep$0.012 / requestN/A (capped at 10)
deep-reasoning$0.015 / requestN/A (capped at 10)
Adding contents.summary costs an additional $0.001 per result.
x402 requests are capped at 10 results maximum. If you request more than 10, numResults is silently clamped to 10 and pricing is based on 10 results.

Contents (/contents)

Each content type is charged per page/URL:
Content typePrice per page
text$0.001
highlights$0.001
summary$0.001
If you request no content types (no text, highlights, or summary), text is enabled by default.

Examples

RequestPriceUSDC atomic
/search with 10 results, type: "auto"$0.0077000
/search with 5 results, type: "fast"$0.0077000
/search with 3 results + summary, type: "auto"$0.01010000
/search with 10 results, type: "deep-lite"$0.01212000
/search with 10 results, type: "deep"$0.01212000
/contents for 2 URLs with text: true$0.0022000
/contents for 1 URL with text + summary$0.0022000

Quickstart

Install dependencies

npm install @x402/fetch @x402/core @x402/evm viem
# For Solana support, also install:
npm install @x402/svm @solana/kit @scure/base
pip install "x402[requests]" eth-account
# For Solana support, also install:
pip install "x402[svm]"
No install is needed for cURL, but you’ll need to handle the 402 challenge and payment signing manually. The SDK approach is recommended for production use.
Don’t want to manage private keys? Coinbase Agentic Wallets provide TEE-isolated key management for AI agents. Your agent never sees the private key. The wallet is viem-compatible, so it works directly with @x402/fetch.

Make a paid search request

import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
// For Solana support, also import:
// import { ExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const signer = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as `0x${string}`);
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(signer));
// Register a Solana signer too if you want the client to use Solana accept
// entries such as `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`:
// client.register("solana:*", new ExactSvmScheme(svmSigner));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

const response = await fetchWithPayment("https://api.exa.ai/search", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    query: "best machine learning frameworks",
    numResults: 5,
  }),
});

const data = await response.json();
console.log(data.results);

// Check settlement receipt
const httpClient = new x402HTTPClient(client);
const receipt = httpClient.getPaymentSettleResponse(
  (name) => response.headers.get(name)
);
console.log("Transaction:", receipt?.transaction);
import os
from eth_account import Account
from x402.mechanisms.evm import EthAccountSigner
from x402.clients.requests import x402RequestsClient

account = Account.from_key(os.environ["WALLET_PRIVATE_KEY"])
signer = EthAccountSigner(account)
client = x402RequestsClient(signer)

response = client.post("https://api.exa.ai/search", json={
    "query": "best machine learning frameworks",
    "numResults": 5,
})

data = response.json()
for result in data["results"]:
    print(result["url"], result["title"])
# Step 1: Discovery, get pricing info
curl -s -o /dev/null -w "%{http_code}" -D - \
  -X POST "https://api.exa.ai/search" \
  -H "Content-Type: application/json" \
  -d '{"query": "best machine learning frameworks", "numResults": 5}'
# Returns 402 with PAYMENT-REQUIRED header containing base64-encoded pricing

# Step 2: Sign the payment with your wallet (use the SDK for this)
# Step 3: Retry with payment signature
curl -X POST "https://api.exa.ai/search" \
  -H "Content-Type: application/json" \
  -H "PAYMENT-SIGNATURE: <base64-encoded-payment>" \
  -d '{"query": "best machine learning frameworks", "numResults": 5}'
# Returns 200 with results + PAYMENT-RESPONSE header (settlement receipt)
cURL requires manual payment signing. For production, use the JavaScript or Python SDK which handles the full 402 > sign > retry flow automatically.

Discovery mode (no wallet needed)

Probe pricing without a wallet by sending unauthenticated requests:
const res = await fetch("https://api.exa.ai/search", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query: "test query", numResults: 3 }),
});

// res.status === 402
const paymentRequired = JSON.parse(
  atob(res.headers.get("PAYMENT-REQUIRED")!)
);
console.log(
  paymentRequired.accepts.map(({ network, amount }) => ({
    network,
    amount,
  }))
);
import base64, json, requests

res = requests.post("https://api.exa.ai/search", json={
    "query": "test query",
    "numResults": 3,
})

# res.status_code == 402
pricing = json.loads(base64.b64decode(res.headers["PAYMENT-REQUIRED"]))
print([(accept["network"], accept["amount"]) for accept in pricing["accepts"]])
curl -s -D - -X POST "https://api.exa.ai/search" \
  -H "Content-Type: application/json" \
  -d '{"query": "test query", "numResults": 3}'
# Look for the PAYMENT-REQUIRED header in the 402 response
# Decode it: echo "<header-value>" | base64 -d | jq .

Payment networks

Exa advertises every currently supported network in the accepts array. Choose the entry that matches your wallet and registered x402 client scheme.
NetworkIdentifierTokenAsset
Base (Ethereum L2)eip155:8453USDC0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Solana mainnetsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpUSDCEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Both use 6-decimal USDC (1000000 = $1.00) and settle on-chain via an x402 facilitator.

Rate limits

x402 has its own rate limiting separate from API key limits:
LimitThresholdWindow
Unpaid discovery requests (per IP)5 requests60 seconds
Paid requests (per wallet)10 requests/second1 second
After 5 unauthenticated 402 discovery requests from the same IP within 60 seconds, further requests return 429 Too Many Requests. Making a successful paid request decrements the counter. Per-wallet QPS is enforced across all paid requests from the same wallet address.

Headers reference

Request headers

HeaderDescription
PAYMENT-SIGNATUREBase64-encoded payment payload (x402 v2)
payment-signatureAlias (also accepted)
x-paymentLegacy alias (v1 compatibility)

Response headers

HeaderWhenDescription
PAYMENT-REQUIRED402 responsesBase64-encoded PaymentRequired object with pricing and payment instructions
PAYMENT-RESPONSE200 or 402 (after payment attempt)Base64-encoded settlement result with transaction hash or error

Error codes

StatusTagDescription
402X402_PAYMENT_REQUIREDNo payment provided. Includes pricing in PAYMENT-REQUIRED header
402X402_VERIFICATION_FAILEDPayment signature did not pass facilitator verification
400X402_INVALID_SIGNATUREMalformed or unparseable payment signature
429X402_TOO_MANY_UNPAIDToo many unpaid discovery requests from this IP
429X402_WALLET_RATE_LIMITEDWallet exceeded 10 requests/second
500X402_INTERNAL_ERRORServer-side error generating payment requirements

FAQ

If your request includes an x-api-key header or Authorization: Bearer token, the API key flow takes priority and x402 is bypassed. They don’t stack. It’s one or the other per request.
Your response is blocked. You receive a 402 with both PAYMENT-RESPONSE (containing the error) and PAYMENT-REQUIRED (so your client can retry). No results are returned until settlement succeeds.
x402 requests enforce a maximum of 10 results per search. If you need more, use the API key flow with a paid plan.
Any EVM-compatible wallet that can sign EIP-712 typed data on Base, or a Solana wallet supported by the x402 SVM client for solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp. The x402 SDK supports viem, ethers, Coinbase Wallet signers, and Solana SVM signers. For EVM-based AI agents, Coinbase Agentic Wallets offer TEE-isolated key management so your agent never handles raw private keys directly.

Resources

Last modified on June 26, 2026