quarryDocs

Paying with x402

Pay for each question from your agent's own USDC wallet, over HTTP or inside MCP, and read the payment receipt.

x402 is an open standard for paying for a web request. The server answers 402 Payment Required with what to pay; the client signs a USDC payment with its wallet and sends the request again with it. quarry uses x402 version 2 and charges in USDC on Base.

This is for agents that hold their own wallet, with no quarry account. It doesn't apply to free datasets, which answer without payment, or to datasets that only sell monthly subscriptions (their contract has "payment": null), which can't be paid this way: see Subscriptions and API keys. To give an assistant such as Claude Code or Codex a wallet without writing code, see Give your assistant a wallet.

Over HTTP

  1. Send POST /{username}/{slug} with a JSON body, { "query": "...", "top_k": 5 }, and no payment.
  2. quarry answers 402. The PAYMENT-REQUIRED header holds the requirements as base64 JSON: an accepts list with the scheme (exact), network, USDC contract, amount in USDC's smallest unit, and payTo, the seller's payout wallet.
  3. Sign a payment for one of the accepts entries and send the same request again, with the signed payment, base64-encoded, in the PAYMENT-SIGNATURE header.
  4. You get the passages. The PAYMENT-RESPONSE header, base64 JSON, holds the receipt: success, transaction (the transaction hash), network, and payer.

Only x402 version 2 is accepted: a version 1 X-PAYMENT header gets another 402.

The x402 client libraries handle steps 2 to 4. Install them with npm install @x402/fetch @x402/evm viem, and run this as an ES module (for example node agent.mjs):

import { decodePaymentResponseHeader, wrapFetchWithPayment, x402Client } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const endpointUrl = "https://quarry.market/alice/market-outlook";

const client = new x402Client();
registerExactEvmScheme(client, { signer: privateKeyToAccount(process.env.AGENT_PRIVATE_KEY) });
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

const res = await fetchWithPayment(endpointUrl, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ query: "Which segment grew fastest?", top_k: 5 }),
});
if (res.status === 402) {
  // Refused: the reason is in PAYMENT-RESPONSE (settlement) or PAYMENT-REQUIRED (verification).
  const header = res.headers.get("PAYMENT-RESPONSE") ?? res.headers.get("PAYMENT-REQUIRED");
  const info = JSON.parse(Buffer.from(header, "base64").toString());
  console.log("Not paid:", info.errorReason ?? info.error);
} else {
  console.log(res.status, await res.json());
  const receipt = res.headers.get("PAYMENT-RESPONSE");
  if (receipt) console.log(decodePaymentResponseHeader(receipt).transaction);
}

endpointUrl is the dataset's request.url from its contract, and AGENT_PRIVATE_KEY the private key of a wallet holding USDC on the right network. The wallet needs no ETH: the facilitator pays the network fee. The client refuses to sign more than $1 per payment by default, and fetchWithPayment throws when it can't sign; raise the cap with client.setSpendControls({ maxAmountPerPayment: "$5" }).

The answer is described in Ask over HTTP.

Inside MCP

The same payment works inside a tool call: ask returns the PaymentRequired object, and you call it again with the signed payment in _meta["x402/payment"]. See Pay per question inside MCP.

Try it

GET https://quarry.market/api/x402/demo charges 0.001 USDC and answers { "message": "Hello, paying agent.", "timestamp": "…" }. Use it to check a wallet setup before paying for a dataset. With the client above: await fetchWithPayment("https://quarry.market/api/x402/demo").

Networks

NetworkIdentifier in acceptsUSDC
Baseeip155:8453Real USDC
Base Sepoliaeip155:84532Test USDC, free from Circle's faucet

quarry currently runs on Base. The dataset contract's payment.network and payment.caip2 say the same. Sign on that network: a payment for another network is refused.

When you're charged

  • quarry checks the payment, runs the search, and only then settles the payment on-chain, before sending the passages. A failed search is never charged, and the signed payment stays unspent, so you can send it again.
  • A request with an invalid body is refused with 400 and isn't charged. Without a payment, though, you get the 402 first, since the body is only read once a payment is attached.
  • If settlement itself fails, you get a 402 whose PAYMENT-RESPONSE has "success": false, and nothing is charged.
  • Send one request at a time with a given signed payment. A second request using it while the first is running gets 409 PAYMENT_IN_FLIGHT.
  • When the relevance check finds no passage that answers the question, you get 404 NO_MATCH and nothing settles: the same signed payment can be sent again with another question to the dataset. Passages that answer only part of the question are charged: see How paying works.
  • A wallet over its limit of paid questions gets 429 RATE_LIMITED once its payment is checked, and nothing settles: send the same signed payment again after Retry-After.

Rate limits are in Limits, and every error in Error codes.

Facilitator and Bazaar

quarry verifies and settles payments through Coinbase's x402 facilitator. Your agent only talks to quarry. Priced datasets also carry x402 Bazaar discovery metadata, which Coinbase's x402 Bazaar uses to list them. To search quarry's own catalog, use the MCP server.

On this page

View as Markdown