# 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. Published 2026-08-13 · Updated 2026-08-16 · HTML version: https://invoket.com/blog/one-settlement-n-units-pricing-a-batch-for-agents --- 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: 1. The agent `POST`s its batch body to the endpoint, unpaid, exactly as it would send the real call. 2. 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. 3. It computes `base + N × unit`, with N floored at one unit and capped at that endpoint's `maxUnits`. 4. It answers `402` with that amount in `accepts[]`, under `scheme: "exact"`. 5. 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`](https://api.invoket.com/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`](/docs/api/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: ```json // 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](/blog/paid-200s-and-unbilled-4xx-the-x402-settlement-line). 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: ```json // 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`](/docs/api/screen-entity-batch), one of them deliberately malformed: ```json // → 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`](/docs/api/climate-point-batch) and [`/weather/forecast/batch`](/docs/api/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](/docs/quickstart), the machine-readable surfaces are mapped in [For agents](/docs/for-agents), and the rails and assets are listed in [Payments and rails](/docs/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](/blog/screen-a-vendor-file-of-ibans-in-one-settlement). Prices and caps live in [`/catalog`](https://api.invoket.com/catalog) and in the `402` challenge — never in prose. ## Sources - `exact` for fixed-price requests with no usage-based billing; `upto` for buyer-set maximums; `batch-settlement` for accumulating authorizations against a reusable channel — [docs.x402.org: Payment schemes](https://docs.x402.org/schemes/overview). - `amount` as maximum at verification and actual amount at settlement; use cases named as LLM token generation, bandwidth metering, dynamic compute pricing — [x402 `upto` scheme specification](https://github.com/x402-foundation/x402/blob/main/specs/schemes/upto/scheme_upto.md). - Payment requirements and the `accepts` array returned with `402` — [x402 v2 specification](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md). - Bulk endpoints billed at the single-record rate, no batch discount, ten records per request — [Apollo API pricing](https://docs.apollo.io/docs/api-pricing). - One credit per record downloaded, requests above the limit rejected — [Coresignal: Bulk Collect](https://docs.coresignal.com/company-api/base-company-api/endpoints/bulk-collect). - Quotes, `BATCH_TOO_LARGE`, `EMPTY_BATCH` and the partial-failure batch response: captured against Invoket production services, 13 August 2026.