One settlement, N units: pricing a batch for agents
An agent checking 500 counterparties should not sign 500 payments. How a batch endpoint quotes a variable price in the 402 challenge, before the agent signs anything.
By Matthias Begot · · Updated
An agent that has to clear five hundred counterparties before a payout run does not want five hundred settlements. It wants one call, one signature, one on-chain transfer — and it wants to know the price before it signs, for a batch whose size it chose itself. Those two requirements pull against each other: a fixed-price scheme signs a fixed number, and a batch has no fixed size.
The resolution is that a batch is not actually a variable-cost resource. Its size is
declared in the request body, so the server can count the units and quote the exact
amount in the 402 challenge itself, using the plain exact scheme. This article
describes that mechanism, the cases where it has to refuse rather than round, and the
part sellers get wrong: per-unit pricing is only honest if every unit comes back with
its own verdict.
Why one settlement instead of N calls
The cost of a settlement is not the cost of the work. Each paid call carries a
signature, a facilitator round trip to /verify, and — after the response exists — a
second round trip to /settle that broadcasts a transfer. For a lookup answered from
a local store in single-digit milliseconds, that machinery dominates the wall clock and
the fee entirely.
Run the same lookup five hundred times and you have paid for that machinery five
hundred times, serialized behind an agent that now has five hundred opportunities to
lose a nonce, hit a rate limit, or half-finish a job. One call over five hundred items
collapses all of it into a single authorization, a single transfer, and a single
PAYMENT-RESPONSE to reconcile.
That is the entire argument for batching, and it is an argument about settlement overhead, not about compute. It only holds if the per-unit price of the work stays honest.
The problem: a fixed-price scheme, a variable-size request
A flat price for a batch endpoint is wrong in both directions. Set it at the price of one unit and a full batch is nearly free — the seller funds everyone else’s bulk job. Set it at the price of a full batch and a two-item call is extortionate, so nobody sends one, so the endpoint is only usable at maximum size. Neither is a price; both are a bet on the shape of somebody else’s job.
So the amount has to depend on N. But x402’s exact scheme, the one nearly every
deployment uses, is defined for fixed-price requests where the buyer authorizes the
advertised amount — it does not support usage-based billing. The advertised amount is
whatever the server puts in accepts[].amount when it answers 402.
Which is the whole trick: the server gets to decide that number per request.
N is knowable before the work, and that changes everything
The x402 specification defines a scheme for genuinely variable costs. upto lets the
buyer authorize a maximum, the server observe actual consumption, and the settled
amount land anywhere at or below the ceiling — at verification time amount is the
maximum, at settlement time it is the actual amount to settle. The use cases it names
are LLM token generation, bandwidth metering, and dynamic compute pricing: all cases
where the cost is unknown until the resource has been consumed.
A batch is not one of them. Its size is sitting in the request body when the request arrives, before a single unit of work has been done. So the seller never needs a ceiling and a reconciliation — it can compute the real number up front and quote it.
The mechanism, end to end:
- The agent
POSTs its batch body to the endpoint, unpaid, exactly as it would send the real call. - The gateway reads the length of the array named by that endpoint’s
unitField— and nothing else. Never a value inside it. The business service remains the sole judge of what the items mean. - It computes
base + N × unit, with N floored at one unit and capped at that endpoint’smaxUnits. - It answers
402with that amount inaccepts[], underscheme: "exact". - The agent signs that number and replays. The gateway recomputes the amount from the body it actually received and never trusts a number sent by the client.
Step 5 is the part that makes step 4 safe. The quote is not a token the agent carries around; it is a pure function of the request, evaluated twice, on the server, from the same input. An agent that signs for a small batch and replays with a large one gets priced on what it sent.
What each surface publishes
The two halves of the price live in two different places, and that split is deliberate:
| Surface | Carries | Why |
|---|---|---|
/catalog | the formula — mode, baseUsd, unitUsd, minUsd, maxUnits, unitField | an agent can budget a job before making any call |
the 402 challenge | the resolved amount for the body just sent | the agent signs one number, for one batch |
An agent planning a run of ten thousand checks reads /catalog, divides by maxUnits,
multiplies out baseUsd and unitUsd, and knows its bill before it opens a
connection. An agent about to sign reads the challenge. Neither ever reads a price from
prose — including this article, which quotes none.
Quotes recomputed against the production gateway on 13 August 2026 for N = 1, 2, 10,
100 and 500 on POST /iban/resolve/batch tracked
base + N × unit exactly at every point, matching the pricing object published for
that endpoint in /catalog. All fourteen batch endpoints in the catalog are priced
this way; their unitField and maxUnits differ per endpoint, because the cap is the
business service’s real limit, not a billing knob.
Refusing beats rounding
A batch above the cap raises a question with two plausible answers, and only one of them is defensible.
Clamping is the tempting one: bill the cap, forward the request, let the business
service sort it out. It is wrong, because the business service is going to reject that
batch anyway — the cap published as maxUnits is its hard limit. The agent would
have signed a maximum-size price for work nobody will do.
So the oversized batch is refused before any challenge is issued, with no payment terms attached and nothing to settle:
// POST /iban/resolve/batch with 501 items → 400, no 402 issued, not billed
{ "code": "BATCH_TOO_LARGE",
"error": "batch of 501 items exceeds the maximum of 500; split the batch" }
Captured on the production gateway, 13 August 2026. The message names the cap it
enforces and the action that fixes the call, because an agent that receives it should
be able to repair its own next request — the same reason
4xx responses are unbilled and guiding.
The floor works the other way. A body that is unreadable, missing its unitField, or
carrying an empty array is priced at one unit rather than rejected by the pricing
layer — the gateway does not get to fail a request on business grounds, because it does
not understand the business. The service behind it does, and answers accordingly:
// POST /screen/entity/batch with {"items": []} → 400, not billed
{ "code": "EMPTY_BATCH", "error": "the batch is empty: at least one name is required" }
Two layers, two competences, one rule each: the gateway counts, the service judges.
Per-unit pricing is a promise about the response
Here is where a batch endpoint stops being a pricing question. If you charge for N units, you owe N verdicts. A batch that quietly drops the items it could not handle, or returns one aggregate result for the whole lot, is charging per unit and delivering per call — and the agent has no way to tell, because the missing verdicts look exactly like items that came back clean.
Three properties make the count real. From a live batch of three names against
POST /screen/entity/batch, one of them deliberately
malformed:
// → HTTP 200, billed for the whole batch
{ "data": {
"count": 3,
"summary": { "hit": 1, "weak_hit": 0, "no_hit": 1, "error": 1 },
"results": [
{ "index": 0, "verdict": "hit", "hits": [ /* … */ ] },
{ "index": 1, "code": "INVALID_INPUT",
"error": "parameter 'name' must be between 2 and 200 characters once whitespace is collapsed" },
{ "index": 2, "verdict": "no_hit", "hits": [], "total_matches": 0 }
],
"lists_screened": [ /* four lists, with their entity counts */ ],
"limits": "…" },
"provenance": { "source": "ofac-sdn+ofac-consolidated+eu-fsf+un-sc",
"freshness": { "kind": "snapshot", "as_of": "2026-07-27T00:00:00Z" } } }
Every unit answers at its own index. A bad item carries the {error, code} a
unitary call would have returned for it, at its position, and does not fail the batch —
which stays a paid 200. The summary counts the error, so an agent can assert on the
totals without walking the array.
What does not vary is served once. The lists screened, the scope caveat, and the provenance are properties of the snapshot, not of the name you asked about. Repeating them per item would inflate the response by a factor of N for zero information — and on a batch of five hundred, a response an agent has to stream and parse is a real cost.
One provenance covers the whole batch, at its oldest input. When a batch is
answered from several reference sets, freshness.as_of is the oldest of them. A
composite is never fresher than its stalest ingredient, and the agent gets told which
date it is actually acting on.
Partial coverage follows the same discipline where the data is geographic rather than
list-shaped: on /climate/point/batch and
/weather/forecast/batch, an item off-grid, out of
window, or naming an unknown city comes back with coverage.complete = false and its
own reason, while its neighbours answer normally. The call fails only when zero
items are covered — because then there is nothing to sell.
The rule underneath all three: a batch is a request for N answers, not a request for a best-effort. Silently swallowing failures is a trust regression dressed as an optimization, and an agent that cannot audit its own results will eventually stop buying them.
Three models, and when each is correct
Batch pricing has converged on three shapes across the API market, only one of which is specific to agents.
| Model | Price | Correct when |
|---|---|---|
| Flat per call, any N | one price regardless of size | the marginal cost and the marginal value are both ~0 — rare, and usually a mispricing |
| N unitary calls | N × unit, N settlements | items arrive at different times, need independent retry, or are genuinely unrelated |
| Per unit, one settlement | base + N × unit, one settlement | the whole set is known at once and the settlement overhead dominates the work |
Per-record charging on bulk endpoints is already the norm outside x402, and the published terms are blunt about it. Apollo bills its bulk enrichment endpoints at the same credit rate as their single-record counterparts, with no discount for batching and a cap of ten records per request. Coresignal’s Bulk Collect deducts one credit per record downloaded — a hundred profiles, a hundred credits — and rejects requests above its ten-thousand-record limit rather than truncating them.
What agentic payments add is the reason the base part exists at all. On a subscription-billed API, batching saves an HTTP round trip. Under x402 it saves a signature and an on-chain transfer, which is a real, per-call, non-amortizable cost to the seller. Charging it once for the batch instead of once per item is not a volume discount granted out of generosity; it is the seller passing back the cost it stopped paying.
And the third model stays right for the third row of that table. Items that arrive independently should stay independent calls — the batch discount is not worth serializing a stream behind a buffer, and a per-item retry after a single bad response is far cheaper than re-signing five hundred units of work.
If you sell a batch endpoint
- Derive N from the request, server-side, at challenge time and again at replay. Never accept a client-supplied quantity, and never bill a number the agent sent you.
- Read only the length. The pricing layer counting array elements is not the same component as the one that understands what an element means; keep it that way.
- Publish the formula, not just the price.
unitField,maxUnits, base and unit belong in the discovery surface so an agent can budget a job before its first call. - Refuse an oversized batch before the challenge. Billing the cap for work your upstream will reject is the one failure mode with no clean remedy under a protocol with no chargebacks.
- Return one verdict per unit, at its index, errors included. A malformed item is reported in place; it does not fail the batch and it does not vanish from it.
- Serve the shared blocks once, and date the batch at its oldest source.
The mechanics of the loop itself — discovery, challenge, signature, replay — are
runnable end to end in the Quickstart, the machine-readable
surfaces are mapped in For agents, and the rails and assets are
listed in Payments and rails. What a batch call looks like
in a real job is worked through in
Screen a vendor file of IBANs in bulk, up to 500 per call.
Prices and caps live in /catalog and in the 402
challenge — never in prose.
Sources
exactfor fixed-price requests with no usage-based billing;uptofor buyer-set maximums;batch-settlementfor accumulating authorizations against a reusable channel — docs.x402.org: Payment schemes.amountas maximum at verification and actual amount at settlement; use cases named as LLM token generation, bandwidth metering, dynamic compute pricing — x402uptoscheme specification.- Payment requirements and the
acceptsarray returned with402— x402 v2 specification. - Bulk endpoints billed at the single-record rate, no batch discount, ten records per request — Apollo API pricing.
- One credit per record downloaded, requests above the limit rejected — Coresignal: Bulk Collect.
- Quotes,
BATCH_TOO_LARGE,EMPTY_BATCHand the partial-failure batch response: captured against Invoket production services, 13 August 2026.