What makes this API different
We state the truth about every run, including the unflattering parts:- Delivered work is itemised, per source. Each source pass that delivers ends in a
receipt with the measured cost, the claim it makes, and the records behind it, and
every row on
GET …/rowsnames the receipt that paid for it. If a pass’s cost basis is unavailable, it settles at $0: only measured cost is billed. Two things are billed without a per-source receipt — the run’s own orchestration spend, and passes that delivered nothing — sospend_usdand the ledger, not the receipts sum, are the complete account of a bill (what a receipt covers). - Unchecked sources are stated. If a run ends before every candidate merchant was
dispatched, the terminal event lists them under
unchecked. We never pretend coverage we didn’t buy. - **Abstains are 0
abstainedrun with the reasoning attached](/errors/declined_to_spend) (andwould_run: falseon the free estimate, before you commit), not a silent empty result. - You see why money moved. The event stream carries every routing decision — dispatched, declined, and the reason — in the same stream as the rows.
What a run costs, and how long it takes
Honest numbers from production, so you can design around them:- A session fans out to several merchants and typically costs low single-digit
dollars (~$0.50 per source pass). You set the ceiling:
max_spend_usdis required on every spending call, and the run can never exceed it. - A real run takes tens of minutes, not seconds — measured real sessions ran 11 to 68 minutes end to end (single-merchant questions at the low end; runs that retried flaky sources at the high end), with first rows arriving in ~4–17 minutes. Design for an asynchronous wait, not a request/response loop.
- The quickstart sequences around this: two instant, free calls first, then the real run with the wait announced.
The three surfaces
The two ways in
- Question-first — sessions.
POST /v1/sessionswith a natural-language query. The agent resolves merchants, locates catalogs, dispatches passes, and adjudicates the answer. Follow up with free narrows and asks; a follow-up that costs money comes back as a priced proposal, never a silent charge. - URL-first — extractions. Not yet available — planned. Already know the
catalog page?
POST /v1/extractionswill skip the fan-out entirely: one URL, one pass, one receipt, then refresh and monitor it at marginal cost. See Extractions for the contract it ships against; the changelog will announce it.
Stability, plainly
- The API is versioned by dated
fn-versionheader with account pinning. Additive changes (new fields, new event phases, new endpoints) ship to every version; behavioral changes get a new date. Your client must tolerate unknown fields and unknown event phases — unknown phases are liveness. - Runs are retained at least 90 days — replayable and free to re-read for the whole
window. On-request redaction (
DELETE /v1/sessions/{id}, which will drop a run’s data while the ledger skeleton survives for audit) is planned, not served — it answers501 not_implementedtoday. If you have a data-retention requirement, treat redaction as unavailable until the changelog announces it. - Every error is one envelope with a stable
codeand adoc_urlthat resolves to a page in Errors. Program againstcode.