ClaimTrace by AXIONYX.io Get a key

ClaimTrace API reference (v1)

Base URL: https://claimtrace.dev. Authentication: header X-API-Key: afk_…. The v1 transport contract is stable: no field is removed or changes meaning without a major version.

Endpoints

MethodPathPurpose
POST/v1/signupSelf-serve key (email → afk_… key sent by email, never in the response, 50 trial credits)
POST/v1/fetchBlocking call — full FetchResultV1 JSON
GET/v1/fetch/streamSSE stream — meta → entity… → done
GET/v1/requests/{request_id}Re-read a result (24 h TTL, isolated per key)
GET/v1/usageYour tier, remaining credits, requests today
GET/v1/statsPublic service counters of the day (no key required)
GET/v1/healthService status + schema version

Request — POST /v1/fetch

{
  "thematique": "restaurants menus du jour",
  "lieu": "Neuchâtel",
  "prompt": "the actual daily menu, with prices if published",
  "depth": "standard",
  "max_entities": 8,
  "day": "2026-08-08"
}
FieldTypeConstraints
thematiquestringrequired, 2–200 chars — what kind of entities
promptstringrequired, 2–4000 chars — the fact you want established
lieustringoptional, ≤ 200 chars — geographic scope
depthstringfast · standard (default) · deep (Pack M+)
max_entitiesint1–30, default 8
daystringoptional, YYYY-MM-DD — for dated data (e.g. daily menus)
deadline_snumberoptional, 1–900 — global wall-clock budget of the read: at the deadline, unfinished entities are returned with attributes.interrupt_reason = "deadline" (never "not published") and the response arrives no later than deadline_s + 5 s
cache_modeauto · bypassbypass re-reads everything cold (reproducibility measurement). Honoured only for a *-scoped key; a free key is silently mapped to auto — read usage.credits_charged to know whether the run was cold

Every accepted parameter

Generated from the v1 request model (FetchRequest) — any other field is silently ignored (send only the fields above).

<!-- gen:params (scripts/claimtrace_api_reference.py) -->

ParameterTypeDefaultBoundsBehaviour
thematiquestringrequired≥ 2 chars, ≤ 200 charswhat kind of entities
lieustring—≤ 200 charsgeographic scope
promptstringrequired≥ 2 chars, ≤ 4000 charsthe fact you want established
sourceslist of web_search · places · website · social · files · images——restricts the source families the planner may use; default: the planner decides
max_entitiesint8≥ 1, ≤ 30capped by the server limit; credits are reserved before the read
depthfast · standard · deepstandard—deep requires Pack M or L (otherwise 403)
output_hintobject——{"entity_fields": [...]} adds up to 14 expected fields to the plan; never changes the response contract
languagestringfr≤ 5 charslanguage of the search and of day names
countrystring—≥ 2 chars, ≤ 2 charsISO 3166-1 alpha-2 — search locale
currencystring—≥ 3 chars, ≤ 3 charsaccepted, not used in v1: a price carries the currency proven by its source (ClaimV1.currency)
daystring—≤ 10 charsYYYY-MM-DD — for dated data (e.g. daily menus)
radius_kmnumber—> 0, ≤ 25radius around lat/lng
latnumber—≥ -90, ≤ 90point of an area listing (with lng, radius_km)
lngnumber—≥ -180, ≤ 180see lat
backgroundboolfalse—reserved — honoured only for an internal key, ignored otherwise
known_contactslist of object——reserved — honoured only for a trusted internal caller, ignored otherwise
cache_modeauto · bypassauto—bypass is honoured only for an internal key; any other key is mapped to auto (read usage.credits_charged)
deadline_snumber—> 0, ≤ 900global wall-clock budget; unfinished entities come back with attributes.interrupt_reason = "deadline"
skip_image_curationboolfalse—set by the server — the value sent is ignored
profilemenu_daily——menu_daily: daily-menu profile (no image curation)
entity_namestring—≤ 120 charsnamed-entity lookup instead of an area listing

<!-- /gen:params -->

Response — FetchResultV1

FieldTypeMeaning
schema_version"1"Contract version
request_idstringRe-read handle for /v1/requests/{id} (24 h)
queryQueryEchoV1Echo: thematique, lieu, day, depth, vertical
entitiesEntityV1[]The resolved entities
verificationVerificationV1truth_rules_version, verified_live, checked_at, notes
usageUsageV1credits_charged, cache_hits, cost_class
warningsstring[]Degradations, stated rather than hidden — e.g. phase_failed:<phase>:<Error> when a step (discovery, planning) failed: an empty answer is never silent
attributionstring[]e.g. © OpenStreetMap contributors (ODbL)

EntityV1

FieldTypeMeaning
idstringStable entity id within the response
titlestringEntity name
summarystring \nullShort description when available
locationGeoV1 \nulladdress, city, country, lat, lng
contactContactV1 \nullphone, email, website
attributesobjectVertical-specific attributes
claimsClaimV1[]The verified facts — see below
imagesImageV1[]url, role, caption
confidencefloat \nullEntity-level confidence, 0–1
not_foundNotFoundV1 \nullPresent when the requested data is not published

ClaimV1 — the heart of the product

FieldTypeMeaning
fieldstringWhich fact, e.g. menu_du_jour
valueanyThe fact itself — null is allowed and assumed
currency"CHF" \"EUR" \"USD" \"GBP" \nullISO 4217 code of a monetary value, proven by the source verbatim or the field name — never guessed, never converted. null means the source does not prove it
match_level"exact" \"equivalent" \"comparable" \nullProduct vertical only, on price claims: exact = the requested reference itself, equivalent = successor/equivalent from the same manufacturer, comparable = similar product from another range. Closed set
provenance.source_urlstring \nullThe page or document the fact was read from
provenance.fetched_atstring \nullWhen it was read (ISO 8601 UTC)
provenance.verbatimstring \nullShort proof excerpt, literally from the source
provenance.page_kindstring \nulle.g. menu_du_jour, carte
provenance.domain_owned_by_entitybool \nullWhether the source belongs to the entity
confidencefloat \null0–1
verified_livebool \nullTri-state: live-confirmed / contradicted / not checked

NotFoundV1 — absence is an answer

FieldTypeMeaning
reasonenum, closed setExactly one of: not_published (the source was read, the requested data is not published there), wrong_page_kind (the source publishes another kind of content — e.g. a permanent menu card, not a daily menu), not_verifiable (the data could not be extracted with a verifiable verbatim proof — or the read did not happen: attributes.interrupt_reason is then deadline, cost_budget, llm_unavailable or error; social source behind a login wall; declared website that does not name the entity), source_unreachable (the source could not be read). This vocabulary is part of the contract: you can write an exhaustive switch on it. Any new value implies a new schema_version — never a silent addition
detailstring \nullHuman-readable explanation
source_checkedstring \nullThe source that was actually read

What ClaimTrace reads — and does not

ReadStatus
HTML page of the entity's own site (including content shipped in an embedded JSON such as Inertia / Next, without running JS)read
JavaScript-rendered page (empty shell without a browser)rendered headless
Linked PDF, PDF inside an iframe, multi-page PDF (≤ 6 pages)read; native text, otherwise vision page by page (scanned PDF, undecodable font)
Menu image published by the site (board, poster)read with vision
Google Sites pageread as an HTML page
Facebook / Instagramnever downloaded (login wall): only dated public-index snippets count; otherwise not_verifiable
Google Business (profile, posts)never a source — internal discovery only (Google Maps Platform terms)
Directories and aggregators (listas.ch, TripAdvisor, TheFork, ordering platforms…)never a source, never an entity's "website"

A fact served from a PDF is re-validated against the PDF's native text (verified_live); a scanned PDF or an image stays verified_live: null (honest tri-state).

Reproducibility — the replay harness

Same input → same output? Check it yourself with nothing but curl and jq:

CLAIMTRACE_API_KEY=afk_… ./claimtrace-replay.sh -n 3 --stream requests.jsonl

requests.jsonl: one POST /v1/fetch request per line ({"id":"…","body":{…}}). Outputs: summary.tsv (per run: duration, cold/cache, entities, claims, not_found by reason, interruptions, credits, facts hash, time to first entity in stream mode), variance.tsv (per request: k/N runs with identical facts, entity-set composition, latencies) and divergences.txt (every fact that differs, with the runs serving it). The facts hash ignores order, request_id and timestamps: two runs serving the same facts share the same hash.

What v1 never returns

Fields whose only source is Google Places — ratings, review counts, price levels, Google-hosted images — are excluded from the payload for legal reasons (Google Maps Platform terms of service prohibit exporting or re-sharing Places content). Claims come exclusively from sources owned by the entity, enforced by a domain-ownership whitelist.

Errors

CodeMeaning
401Missing or invalid API key
402Credits exhausted — top up or wait for the monthly refill
403Scope missing for this key, or depth: "deep" without Pack M or L — checked before anything runs, never charged
404On /v1/requests/{id}: unknown or expired result for this key
409On /v1/signup: an active key already exists for this email
422Validation error — a field violates the constraints above
429Rate limit — response carries Retry-After
405Wrong HTTP method on a documented route (e.g. GET /v1/fetch)

Refusals are never charged.