Privacy
Compute State API answers one question — which compute can run this job cheapest right now — for anyone who asks, without an account. This page says exactly what that leaves behind.
Agents: the same text as markdown is at /privacy.txt.
The short version
As a caller, you get no account, no API key, no signup, no cookie and no session, and there are no analytics or advertising scripts of any kind — not on the pages, not on the API. Nothing here tries to work out who you are, and there is nothing for you to log into.
The one exception is the operator. /admin is behind a secret, and signing in there sets a single httpOnly cookie that expires after eight hours. It is the person running this service logging into their own dashboard; it is never set on a public route and never on you.
The only thing that ties a request to anything durable is a payment, because a payment is a public blockchain transaction and a receipt has to be kept.
Free calls
Reading the landing page, /llms.txt, /skill.md, /openapi.json, /v1/example, /healthz or this policy writes no caller-specific record to the database. Every request, paid or not, produces one log line: timestamp, request id, method, the path with the query string stripped off, and the status code. Authorization and cookie headers are redacted before anything is written.
For service-level traffic measurement, every request also increments one aggregate counter for the matched endpoint, grouped by UTC day. Unmatched paths are grouped as "other". This counter contains only the day, the endpoint category and a number; it contains no IP address, wallet address, user agent, query, request ID or other caller identifier. These daily counters are retained so the operator can see traffic over time.
Calling /v1/cheapest — including the free outcomes, a 400, a 402 challenge or an unpaid 503 — writes two things, neither of which identifies you:
- An hourly counter: the kind of outcome and the HTTP status, nothing else. "14 challenges issued between 09:00 and 10:00" is the whole of it. No address, no query, nothing per caller.
- An event row: the outcome, the route, the chain if one was involved, and a short reason string. That reason is the same text the response gave you, so when you sent an invalid parameter it quotes that parameter back — availability=spot, an unrecognised model_tier, a provider slug that does not exist, or the GPU family a 503 had no fresh rows for. Parameter values are capped at 64 characters of letters, digits and ._/+- before they are ever handled. The full query string is never written, and to bound how fast this table can grow, 400s and 402s stop writing event rows above sixty a minute.
Your IP address is used, in memory, to rate-limit requests. It is not written to the database and it is not part of the log line. The hosting platform that runs this server keeps its own edge logs, which are outside this service's control.
Paid calls
Paying is an on-chain USDC transfer on Base or Solana. Those ledgers are public by design: the payment, the amount and the paying address are visible to anyone, whether or not this service records them. It does record them, because that is what a receipt is.
A settled call writes one receipt row containing:
- the network and the amount in atomic units
- the paying address, as reported by the payment facilitator
- the transaction reference
- the route that was paid for, and the HTTP status you received
- a 32-character SHA-256 hash of the normalized query — not the query itself
- a snapshot of the answer: which provider and SKU won, its estimated cost, and how old the price book was
The raw query string is never stored, and no route serves the hash. Be clear-eyed about what a hash is, though: it is a truncated, unsalted SHA-256 of the normalized parameters, so it cannot be turned back into your query, but anyone holding it could confirm a guess by hashing a query themselves. The shape of a question here is short and predictable, so treat the hash as a fingerprint of the question rather than a secret.
A second row records the settlement attempt itself, written before the payment is ever submitted: a stable key derived from your signed payload, the signed payment payload and the challenge it answered, the paying address, and the transaction reference. This exists so that a connection dropped mid-payment cannot charge you twice — a retry of the identical payload is recognised instead of settled again. Attempts that resolve to a clean rejection are deleted immediately. Attempts that settled are kept, because they are what stops a replay being charged or served twice — and because that row, not just the receipt, is what makes a settled payment durable.
If the receipt row cannot be written right away — a brief database outage, nothing to do with your payment — it is held in a separate queue and written into the same receipt table automatically once the database is reachable again, carrying the original settlement time rather than the time it was finally written. If even that queue cannot take it, the settlement-attempt row above is the fallback: once it is marked settled, a periodic pass looks for exactly this case — a confirmed attempt with no receipt anywhere — and writes the missing receipt from it, still carrying the original settlement time. And if the same outage stops the attempt from ever being marked settled in the first place, so it is left looking merely in-progress long after it should have resolved, a further periodic pass asks the payment facilitator directly what actually happened to it before treating it either way — the same question a retry of your own payment would ask. Either way, you are never made to wait on this or asked to pay again; it happens after you already have your answer. The one thing that is never allowed to happen silently is the other direction: if the attempt itself cannot be durably recorded before your payment is submitted, the payment is refused up front rather than taken with nothing to show for it.
Who else sees anything
- The payment facilitator (https://facilitator.payai.network) receives your signed payment payload and the challenge it answers, because it is the party that submits the transaction. It operates under its own terms.
- Base and Solana receive the transaction, publicly and permanently. Nobody can withdraw a settled transaction from a public ledger, including us.
- The hosting platform runs the server and the database and can see what its own platform logs and backups contain.
- Vendors whose pages are the source of the prices see nothing about you. Their pricing pages are fetched on a fixed schedule by the server, never in response to your call, and nothing about a caller is forwarded to them.
Nothing is sold, rented, shared for advertising, or used to build a profile of a caller. There is no ad tech and no cross-site tracking here at all.
What the operator can see
The operator dashboard is behind a secret and shows aggregates: how many calls, how much revenue, how many distinct paying addresses, which SKUs won, and which vendor pages are failing to parse. It also shows all-time request counts by endpoint, a paid-versus-not-paid breakdown for /v1/cheapest, and a daily traffic history. The dashboard displays the most recent 30 days of that history, while the underlying daily counters are retained. Paying addresses, transaction references and query hashes are counted there but never listed — the dashboard queries return integers, not the underlying values.
The server log is a different matter from the dashboard. The routine access line carries no identity, but when a payment goes wrong — an ambiguous settlement, a receipt that failed to write — the log records what is needed to chase it: the transaction reference, the paying address and the authorization nonce, or the attempt key derived from them. That is the operator's own log and the hosting platform's, not a public surface.
The operator can of course read the database directly, as the person responsible for it.
How long it is kept
Honestly: there is no automatic expiry today. Receipts, hourly counters, daily endpoint traffic counters, event rows, settled attempt records and cached answers persist until the operator deletes them. Attempt records that resolve to a rejection are deleted as soon as they resolve. Price rows are replaced each time their vendor is successfully read.
Asking about your data
Write to StateAPI@proton.me. Because there are no accounts, the only way to point at your own rows is a transaction reference or a paying address — without one, a request cannot be matched to anything.
A receipt can be deleted on request. The on-chain transaction behind it cannot: it is not ours, and no one can remove it.
Changes
This policy is generated from the source of the service that implements it, so it changes when the behaviour changes rather than on a review cycle. There is no version history page; the repository is the history.