Bulk & API

CSV in, answers out

The same engine the web trace uses, addressable by machine. Every response carries the rung it ran at, so you always know which tier produced the record you are looking at.

Run a batch

A header row with name, optionally city and state. Up to 500 rows. You get a trace id and a poll URL per row — a full trace takes minutes, so nothing here waits for answers.

Held for this request only. Never stored, never sent anywhere but the call itself.

No file to hand? — the same shape the docs below describe.

Single trace

POST /api/v1/trace
Authorization: Bearer <api key>
x-provider-key: <accepted, NOT YET WIRED — see /accuracy>

{ "name": "Alex Luther", "city": "Sacramento", "state": "CA" }

202 -> { id, poll, dossier, rung, metering }

Bulk

POST /api/v1/bulk
Authorization: Bearer <api key>
Content-Type: text/csv

name,city,state
Alex Luther,Sacramento,CA
John Roth,Nashville,TN

200 -> text/csv
name,city,state,trace_id,status,poll_url,dossier_url,rung,note

Bulk enqueues and returns idsrather than holding the connection open. A full trace takes minutes — the live Clean Cut run took 57 seconds and that was a fast one — so a function that tried to finish a 500-row batch inline would be killed by the platform and lose the batch. Poll each row’s poll_url.

Over 500 rows, nothing is queued and the call returns 413. Silently truncating a batch is worse than refusing it: you would not find out until the results came back short, and by then you could not tell which rows were missing.

What failure looks like

Every refusal names itself, because “something went wrong” on a 401 sends you to check your CSV when the key is the problem.

401  the API key was not accepted        nothing enqueued
413  over the 500 row limit           nothing enqueued, no partial batch
429  rate limited, carries retry-after   nothing enqueued
503  FT_API_KEYS unset on this deploy    nothing enqueued

What the metering actually guarantees

Durable: every metered call is written to Neon and the window count is a SUM over that table, so it holds across cold starts and across serverless instances.