Skip to content

Buying at Volume

github-actions[bot] edited this page Nov 15, 2026 · 5 revisions

Buying at Volume

An agent that buys hundreds or thousands of calls in a burst, over x402 or MPP, needs four things: a price it can check before it pays, a retry that never pays twice, a ceiling on spend, and limits it can plan around. This page names each mechanism and the file that enforces it. The live figures, rendered from that code, are on the guide: agent402.tools/guides/buying-at-volume.

Prices

  • /api/pricing lists every route, its list price, the chains it takes (networks) and, on a route that runs as a job, its async block. Chains with a settlement premium or a price floor are listed under payment.premiumUsdByNetwork and payment.minPriceUsdByNetwork.
  • The 402 is the quote for the request in hand. On the metered model routes it is computed from the body, and the paid retry is priced again from its own body (acceptsForItem in src/payments.js), so the paid retry carries the body that was quoted.
  • Price changes are on the changelog. Read /api/pricing at the start of each burst.

Throughput limits

There is no per-wallet rate limit on paid calls. Every limit below answers before any payment is taken; the default column says how long to wait.

What is counted Default Source
Unpaid price checks to priced catalog routes, per client address 3,000 an hour; 30,000 an hour for the rest of the clock hour once that address settles a payment, and for 10 minutes after each paid call src/unpaid-quote-budget.js (PAID_CLIENT_QUOTE_MULTIPLIER)
Unpaid quotes on /v1/metered, per client address 60 a minute, 1,200 an hour (429, Retry-After: 60, with a guide link); past that, 2 more for each paid /v1/metered call from that address (METERED_QUOTE_CREDITS_PER_CALL), used within 10 minutes, up to 600 a minute and 12,000 an hour. Retry-After: 60 is the minute window; past the hourly figure the wait runs until the oldest quote in the last hour ages out METERED_UNPAID_QUOTES_PER_MIN / _PER_HOUR and PAID_CLIENT_METERED_QUOTES_PER_MIN / _PER_HOUR in src/unpaid-quote-budget.js, enforced in src/server.js
Refused payment credentials on /v1/metered the same figures, counted once a credential is refused src/server.js
Payments that verified and then failed to settle for a reason about the payment itself (its balance, its signature, or an authorization already used or expired), per wallet and per network (a signed EVM payer is counted on the network each payment named; a Tempo sender, a credits key or an address is one key) refused on that network at 3 in 15 minutes (9 for failures whose cause the settlement did not name, refused in words that blame no one) while failures are more than half of the wallet's outcomes there, or 40 whatever it settled. The 429 names the network and when it lifts (refusedNetwork, retryAfterSeconds, retry: "other-network"), and the same wallet can pay on another network the route offers at once src/gateway-settle-breaker.js
Report and media runs that failed to settle or failed after the run, per buyer (signed payer, verified Tempo sender, credits key, else address) blocked for 30 minutes (429, no Retry-After) at 3 in 15 minutes while failures are more than half of the buyer's outcomes, or at 10 whatever it settled COMPOSITE_GUARD_* in src/composite-spend-guard.js
Unpaid requests with a body over 100 KB to a route that reads its price from the body, per client address 60 a minute, 1,200 an hour (429, Retry-After: 60); no raise once the address has paid src/large-body-gate.js, limiter in src/server.js
Paid calls in flight, per wallet, on every paid route that is not free through proof-of-work the wallet's balance of the paying token on the paying chain must cover every call it has in flight plus the new one. On routes other than the report and media routes the balance is read inside about a second, and a wallet whose balance covers its calls can send any number at once: all are started at once, there is no other limit on calls at once, and no call waits for another call's payment to settle. A call the balance does not cover answers 429 with Retry-After: 30; a call that arrives while another call from the wallet is still settling, and whose cover cannot be confirmed yet, answers 429 with Retry-After: 5. When the balance cannot be read right now on those routes, the call is started if the last balance this server read for that wallet within the past 15 minutes, less what the wallet's payments have taken and its calls in progress hold since, covers it. Otherwise, and for a wallet this server has not read in the past 15 minutes, the wallet runs up to 4 calls at once (16 for a wallet with a payment on record) while its balance cannot be read, and a call past that answers 429 with Retry-After: 5. The report and media routes are the exception: the balance read is given up to five seconds, a call the balance does not cover answers 429 with Retry-After: 30, and while the balance cannot be read a wallet runs up to 4 calls at once there, whatever its record. Each of these 429s comes before anything runs; x402, the MPP evm method and a Tempo pull credential are not charged for it. Applies to x402 on EVM, Solana, Algorand and Stellar, the MPP evm method, a Tempo pull credential and a Circle Gateway batched payment src/inflight-cover.js (INFLIGHT_COVER_UNREAD_MAX, INFLIGHT_COVER_UNREAD_PAID_MAX)
Jobs per client address 4 at once (429, Retry-After: 30) ASYNC_JOBS_PER_CLIENT in src/async-jobs.js

On the two failed-settlement rows a buyer's failures count against that buyer alone. On the first, a settlement that could not complete on this server's side or the facilitator's (a timeout, a connection error, an HTTP error, a billing limit on this server's own account) is never counted against the wallet; a run of those is an operator alarm. A burst of them across many buyers is an operator alarm (settleFailures for /v1, unsettledReports for report and media runs, each ok or burst on /api/gateway-status) and is not a refusal.

An IPv6 /64 is one address. A stock x402 client sends one unpaid read and one paid retry per call, and only the unpaid read is counted. Once an address has paid, its unpaid price checks read against 10 times the figure for 10 minutes after each paid call (a final 200 that settled on any rail, or a credits debit; PAID_QUOTE_TRUST_MINUTES in src/paid-quote-trust.js, PAID_CLIENT_QUOTE_MULTIPLIER in src/unpaid-quote-budget.js). A /v1/metered quote past its plain figure needs a credit that a paid /v1/metered call earned, so a steady buyer is paced by its own paid calls and one cheap paid call raises the limit by only a few quotes. A payment credential that is refused still counts.

Retries that never pay twice

  • Idempotency-Key: the same key with the same credential, route, query and body returns the first 200 (X-Idempotent-Replay: true) with no second run and no second settlement; a copy that arrives while the first runs gets 409 idempotent_request_in_flight with Retry-After: 2 (src/idempotency.js, store in src/idempotency-store.js).
  • Implicit replay on EVM payments: the signed payment (network, payer, nonce) is the key, so a re-sent payment returns the first answer marked X-Replayed-Answer: 1, except on identity-bound routes, routes with a lasting effect and streamed calls.
  • Solana: no implicit key; send Idempotency-Key with the exact payment header that went out the first time.
  • On x402 a response of 400 or above is not settled, so a retry after one costs nothing extra. Read PAYMENT-RESPONSE on each answer to see whether it settled.

Slow calls

A route with an async block takes Prefer: respond-async: 202 and a Location job link at once, GET /api/jobs/{id} (free) until status is completed or failed, payment settled only on the delivered result (src/async-jobs.js, src/slow-routes.js). agent402-client and agent402-mcp switch to jobs on their own.

Spend caps in the packages

Package Per call Across calls Source
agent402-client maxPerCallUsd dailyLimitUsd, maxPerHostUsd (rolling 24 hours, per instance) client/index.js
agent402-mcp AGENT402_MAX_PER_CALL (on by default, off removes it) AGENT402_BUDGET (whole session, opt-in) mcp/index.js
agent402-openclaw maxPerCallUsd or AGENT402_MAX_PER_CALL_USD (defaults to the metered per-call cap) none openclaw/index.js

Every cap is checked before a payment is signed. All three packages refuse an accept asking for a signing validity longer than a day (MAX_VALIDITY_SECONDS in each known-assets.js). AGENT402_NETWORKS (agent402-mcp) restricts and orders the chains a wallet pays on.

Rails

Rail Settles When the call fails
x402 exact, EVM chain after the tool answers, only under 400 not charged
x402 exact, Solana after the tool answers, only under 400 not charged
x402 exact, Stellar after the tool answers, only under 400 not charged
x402 exact, Algorand after the tool answers, only under 400 not charged
x402 exact, Gateway batched (where offered; paid from a Circle Gateway deposit) settled off chain by Circle Gateway after the tool answers, only under 400 not charged
MPP evm charge the same x402 settlement not charged
MPP Tempo pull broadcast after the tool answers, only under 400 not charged
MPP Tempo push final before the call runs booked as a refund owed, repaid after on-chain proof

On the EVM chains and Tempo pull, a settlement whose outcome is not known when the call is answered is read on chain again; one that lands after a failed answer is booked as a refund owed and repaid after on-chain proof.

The long routes (report products, video, the slow media tiers) take x402 exact on EVM chains only, and Tempo is not offered on identity-bound or long-running routes. A Solana payment embeds a recent blockhash that lives about a minute, so sign each one right before it is sent. A Solana payment refused for a missing or underfunded USDC account answers retry: "fund-wallet" with Retry-After (src/svm-funding-refusal.js).

Metered chat

  • Model ids: GET /v1/models. An unlisted id is refused before payment with suggestions and a howToCall retried with the first.
  • Input size: each model's cap is x402.meteredMaxInputChars on /v1/models (maxInputCharsFor in src/tools/llm-gateway-kit.js); over it, input_too_large before payment, with suggestions of models whose window takes it.
  • Every wire: both refusals are answered before payment on /v1/metered/chat/completions, /v1/metered/messages, /v1/metered/responses and /v1/metered/gemini alike (src/gateway-prepayment.js), each in the error object its SDK reads, with the same fields beside it.
  • Per-call cap: a body quoting above METERED_MAX_QUOTE_USD is refused with a 400 before any upstream call.
  • Set a client timeout of at least 120 s: the answer is generated and the payment settled before the response is sent, which takes a few seconds on most calls and longer for long answers, and a client that disconnects after the payment settles is charged without the answer (the charge is recorded as owed and reviewed for a refund).

Errors

  • 400, 422: input refused; the body carries howToCall (method, URL, ready body). Not charged on x402.
  • 402: the first-contact body carries the offer, howToCall and, on a job route, async.
  • 409: the same keyed call is still running; resend after Retry-After.
  • 429, 503: a limit or a busy route; wait Retry-After. image-ocr answers 503 large-image-busy while it reads another large image.
  • 422 with attribution: "caller" or "upstream-access": a URL you supplied answered with a 4xx, or refused this server's access.
  • 502, 503 with attribution: "upstream": a source the tool reads answered with a failure (its own status rides in upstreamStatus), not the input; retry later or pick another source.
  • 502, 503, 504 with attribution: "ours": our side: our time limit passed (hint: "our-deadline" or "budget-spent"), our DNS lookup failed ("our-egress"), the source refused our own key ("our-key"), our own allowance or pacing ("our-allowance", "our-pacing"), or a defect in our own code ("internal"); hint: "smaller-input" means an input this long outlasts the limit. Retry the same call, or send a shorter input when the hint says so.
  • 502, 503 with attribution: "unknown" and hint: "connect-failed": the connection to a source failed before any answer (refused, reset or unreachable), and nothing shows which side failed; retry the same call later.
  • 504 with hint: "source-timeout" or "source-unreachable" (attribution: "unknown"): a URL you supplied did not answer within our limit, or could not be connected to; check the URL, or retry.

See also: Paying with x402, Paying with MPP, LLM Gateway (OpenAI /v1), Agentic Finance.

Clone this wiki locally