Référence API ClaimTrace (v1)
URL de base : https://claimtrace.dev. Authentification : header X-API-Key: afk_…. Le contrat de transport v1 est stable : aucun champ n'est retiré ni ne change de sémantique sans version majeure.
Endpoints
| Méthode | Chemin | Rôle |
|---|---|---|
POST | /v1/signup | Clé self-serve (email → clé afk_… envoyée par email, jamais dans la réponse, 50 crédits d'essai) |
POST | /v1/fetch | Appel bloquant — JSON FetchResultV1 complet |
GET | /v1/fetch/stream | Flux SSE — meta → entity… → done |
GET | /v1/requests/{request_id} | Relecture d'un résultat (TTL 24 h, isolé par clé) |
GET | /v1/usage | Votre tier, crédits restants, requêtes du jour |
GET | /v1/stats | Compteurs publics du service du jour (sans clé) |
GET | /v1/health | État du service + version du schéma |
Requête — POST /v1/fetch
{
"thematique": "restaurants menus du jour",
"lieu": "Neuchâtel",
"prompt": "le menu du jour réel, avec prix si publié",
"depth": "standard",
"max_entities": 8,
"day": "2026-08-08"
}
| Champ | Type | Contraintes |
|---|---|---|
thematique | string | requis, 2–200 caractères — quel type d'entités |
prompt | string | requis, 2–4000 caractères — le fait à établir |
lieu | string | optionnel, ≤ 200 caractères — périmètre géographique |
depth | string | fast · standard (défaut) · deep (Pack M+) |
max_entities | int | 1–30, défaut 8 |
day | string | optionnel, AAAA-MM-JJ — pour les données datées (ex. menus du jour) |
deadline_s | number | optionnel, 1–900 — mur de temps global de la lecture : à l'échéance, les entités non finies sont rendues avec attributes.interrupt_reason = "deadline" (jamais « rien publié ») et la réponse arrive au plus tard deadline_s + 5 s |
cache_mode | auto · bypass | bypass relit tout à froid (mesure de reproductibilité). Honoré uniquement pour une clé à scope * ; une clé free est ramenée à auto sans erreur — lisez usage.credits_charged pour savoir si le run était froid |
Tous les paramètres acceptés
Générée depuis le modèle de requête v1 (FetchRequest) — tout autre champ est ignoré sans erreur (n'envoyez que les champs ci-dessus).
<!-- gen:params (scripts/claimtrace_api_reference.py) -->
| Paramètre | Type | Défaut | Bornes | Comportement |
|---|---|---|---|---|
thematique | string | obligatoire | ≥ 2 car., ≤ 200 car. | quel type d'entités |
lieu | string | — | ≤ 200 car. | périmètre géographique |
prompt | string | obligatoire | ≥ 2 car., ≤ 4000 car. | le fait à établir |
sources | list of web_search · places · website · social · files · images | — | — | restreint les familles de sources du planificateur ; par défaut : le planificateur décide |
max_entities | int | 8 | ≥ 1, ≤ 30 | plafonné par la limite serveur ; les crédits sont réservés avant la lecture |
depth | fast · standard · deep | standard | — | deep exige un Pack M ou L (sinon 403) |
output_hint | object | — | — | {"entity_fields": [...]} ajoute jusqu'à 14 champs attendus au plan ; ne change jamais le contrat de réponse |
language | string | fr | ≤ 5 car. | langue de la recherche et des noms de jours |
country | string | — | ≥ 2 car., ≤ 2 car. | ISO 3166-1 alpha-2 — locale de recherche |
currency | string | — | ≥ 3 car., ≤ 3 car. | accepté, non utilisé en v1 : un prix porte la devise prouvée par sa source (ClaimV1.currency) |
day | string | — | ≤ 10 car. | AAAA-MM-JJ — pour les données datées (ex. menus du jour) |
radius_km | number | — | > 0, ≤ 25 | rayon autour de lat/lng |
lat | number | — | ≥ -90, ≤ 90 | point d'une liste de zone (avec lng, radius_km) |
lng | number | — | ≥ -180, ≤ 180 | voir lat |
background | bool | false | — | réservé — honoré seulement pour une clé interne, ignoré sinon |
known_contacts | list of object | — | — | réservé — honoré seulement pour un appelant interne de confiance, ignoré sinon |
cache_mode | auto · bypass | auto | — | bypass n'est honoré que pour une clé interne ; toute autre clé est ramenée à auto (lire usage.credits_charged) |
deadline_s | number | — | > 0, ≤ 900 | budget global de la lecture ; les entités non terminées reviennent avec attributes.interrupt_reason = "deadline" |
skip_image_curation | bool | false | — | fixé par le serveur — la valeur envoyée est ignorée |
profile | menu_daily | — | — | menu_daily : profil menu du jour (sans curation d'images) |
entity_name | string | — | ≤ 120 car. | recherche d'une entité NOMMÉE au lieu d'une liste de zone |
<!-- /gen:params -->
Réponse — FetchResultV1
| Champ | Type | Signification |
|---|---|---|
schema_version | "1" | Version du contrat |
request_id | string | Poignée de relecture pour /v1/requests/{id} (24 h) |
query | QueryEchoV1 | Écho : thematique, lieu, day, depth, vertical |
entities | EntityV1[] | Les entités résolues |
verification | VerificationV1 | truth_rules_version, verified_live, checked_at, notes |
usage | UsageV1 | credits_charged, cache_hits, cost_class |
warnings | string[] | Les dégradations, dites plutôt que cachées — ex. phase_failed:<phase>:<Erreur> quand une étape (découverte, planification) a échoué : une réponse vide n'est jamais muette |
attribution | string[] | ex. © OpenStreetMap contributors (ODbL) |
EntityV1
| Champ | Type | Signification | |
|---|---|---|---|
id | string | Identifiant stable de l'entité dans la réponse | |
title | string | Nom de l'entité | |
summary | string \ | null | Description courte si disponible |
location | GeoV1 \ | null | address, city, country, lat, lng |
contact | ContactV1 \ | null | phone, email, website |
attributes | object | Attributs propres au vertical | |
claims | ClaimV1[] | Les faits vérifiés — voir ci-dessous | |
images | ImageV1[] | url, role, caption | |
confidence | float \ | null | Confiance au niveau entité, 0–1 |
not_found | NotFoundV1 \ | null | Présent quand la donnée demandée n'est pas publiée |
ClaimV1 — le cœur du produit
| Champ | Type | Signification | ||||
|---|---|---|---|---|---|---|
field | string | Quel fait, ex. menu_du_jour | ||||
value | any | Le fait lui-même — null autorisé et assumé | ||||
currency | "CHF" \ | "EUR" \ | "USD" \ | "GBP" \ | null | Code ISO 4217 d'un montant, prouvé par le verbatim de la source ou le nom de champ — jamais deviné, jamais converti. null = la source ne le prouve pas |
match_level | "exact" \ | "equivalent" \ | "comparable" \ | null | Vertical produit, sur les claims de prix : exact = la référence demandée elle-même, equivalent = successeur/équivalent du même fabricant, comparable = produit similaire d'une autre gamme. Vocabulaire fermé | |
provenance.source_url | string \ | null | La page ou le document où le fait a été lu | |||
provenance.fetched_at | string \ | null | Quand il a été lu (ISO 8601 UTC) | |||
provenance.verbatim | string \ | null | Court extrait de preuve, littéral depuis la source | |||
provenance.page_kind | string \ | null | ex. menu_du_jour, carte | |||
provenance.domain_owned_by_entity | bool \ | null | La source appartient-elle à l'entité | |||
confidence | float \ | null | 0–1 | |||
verified_live | bool \ | null | Tri-état : confirmé live / démenti / non vérifié |
NotFoundV1 — l'absence est une réponse
| Champ | Type | Signification | |
|---|---|---|---|
reason | enum, vocabulaire fermé | Exactement l'une de : not_published (la source a été lue, la donnée demandée n'y est pas publiée), wrong_page_kind (la source publie un autre type de contenu — ex. une carte permanente, pas un menu du jour), not_verifiable (la donnée n'a pas pu être extraite avec une preuve verbatim vérifiable — ou la lecture n'a pas eu lieu : attributes.interrupt_reason vaut alors deadline, cost_budget, llm_unavailable ou error ; source sociale derrière un mur de connexion ; site déclaré qui ne nomme pas l'entité), source_unreachable (la source n'a pas pu être lue). Ce vocabulaire fait partie du contrat : un switch exhaustif est possible. Toute nouvelle valeur implique une nouvelle schema_version — jamais un ajout silencieux | |
detail | string \ | null | Explication lisible |
source_checked | string \ | null | La source réellement consultée |
Ce que ClaimTrace lit — et ne lit pas
| Lecture | État |
|---|---|
| Page HTML du site de l'entité (y compris contenu servi dans un JSON embarqué type Inertia / Next, sans exécuter de JS) | lue |
| Page rendue en JavaScript (coquille vide sans navigateur) | rendue en navigateur headless |
| PDF lié, PDF dans une iframe, PDF multi-pages (≤ 6 pages) | lu ; texte natif, sinon vision page par page (PDF scanné, police non décodable) |
| Image de menu publiée par le site (ardoise, affiche) | lue en vision |
| Page hébergée par Google Sites | lue comme une page HTML |
| Facebook / Instagram | jamais téléchargés (mur de connexion) : seuls les extraits datés de l'index public comptent ; sinon not_verifiable |
| Google Business (fiche, posts) | jamais une source — découverte interne seulement (conditions Google Maps Platform) |
| Annuaires et agrégateurs (listas.ch, TripAdvisor, TheFork, plateformes de commande…) | jamais une source, jamais le « site » d'une entité |
Un fait servi depuis un PDF est revalidé sur le texte natif du PDF (verified_live) ; un PDF scanné ou une image restent verified_live: null (tri-état honnête).
Reproductibilité — le harnais de rejeu
Même entrée → même sortie ? Vérifiez-le vous-même, sans rien d'autre que curl et jq :
# scripts/claimtrace-replay.sh (dépôt AXIONYX — fourni sur demande avec votre clé)
CLAIMTRACE_API_KEY=afk_… ./claimtrace-replay.sh -n 3 --stream requests.jsonl
requests.jsonl : une requête POST /v1/fetch par ligne ({"id":"…","body":{…}}). Sorties : summary.tsv (par run : durée, cold/cache, entités, claims, not_found par motif, interruptions, crédits, hash des faits, délai du 1er entity en flux), variance.tsv (par requête : k/N runs aux faits identiques, composition d'entités, latences) et divergences.txt (chaque fait qui diffère, avec les runs qui le servent). Le hash des faits ignore l'ordre, request_id et les horodatages : deux runs qui servent les mêmes faits ont le même hash.
Ce que la v1 ne renvoie jamais
Les champs dont la seule source est Google Places — notes, nombres d'avis, niveaux de prix, images hébergées par Google — sont exclus du payload pour des raisons juridiques (les conditions Google Maps Platform interdisent d'exporter ou repartager le contenu Places). Les claims proviennent exclusivement de sources appartenant à l'entité, garanties par une liste blanche de propriété de domaine.
Erreurs
| Code | Signification |
|---|---|
401 | Clé API absente ou invalide |
402 | Crédits épuisés — rechargez ou attendez la recharge mensuelle |
403 | Portée absente pour cette clé, ou depth: "deep" sans Pack M ou L — vérifié avant toute exécution, jamais facturé |
404 | Sur /v1/requests/{id} : résultat inconnu ou expiré pour cette clé |
409 | Sur /v1/signup : une clé active existe déjà pour cet email |
422 | Erreur de validation — un champ viole les contraintes ci-dessus |
429 | Limite de débit — la réponse porte Retry-After |
405 | Mauvaise méthode HTTP sur une route documentée (ex. GET /v1/fetch) |
Les refus ne sont jamais facturés.