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
- Send
POST /{username}/{slug}with a JSON body,{ "query": "...", "top_k": 5 }, and no payment. - quarry answers
402. ThePAYMENT-REQUIREDheader holds the requirements as base64 JSON: anacceptslist with the scheme (exact), network, USDC contract,amountin USDC's smallest unit, andpayTo, the seller's payout wallet. - Sign a payment for one of the
acceptsentries and send the same request again, with the signed payment, base64-encoded, in thePAYMENT-SIGNATUREheader. - You get the passages. The
PAYMENT-RESPONSEheader, base64 JSON, holds the receipt:success,transaction(the transaction hash),network, andpayer.
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
| Network | Identifier in accepts | USDC |
|---|---|---|
| Base | eip155:8453 | Real USDC |
| Base Sepolia | eip155:84532 | Test 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
400and isn't charged. Without a payment, though, you get the402first, since the body is only read once a payment is attached. - If settlement itself fails, you get a
402whosePAYMENT-RESPONSEhas"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_MATCHand 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_LIMITEDonce its payment is checked, and nothing settles: send the same signed payment again afterRetry-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.