API Console - Documentation interactive
POST /api/person

Vérification d'une identité particulier contre la base d'enregistrements individus FR. Score 0-100 par champ + score global agrégé.

database
Base+300M enregistrements individus FR (multi-sources)
speed
LatenceRéponse synchrone en temps réel, au format JSON
warning
Cas ambiguScore plafonné à 50 si homonymes

Headers requis

HeaderValeurRequisDescription
X-API-Keyvkey-...ouiClé d'authentification du compte (sinon HTTP 401 missing_api_key)
Content-Typeapplication/jsonouiType du corps de requête JSON. Le corps est parsé en JSON quelle que soit la valeur.

Body

ChampTypeRequisDescription
referencestringnonIdentifiant client retourné tel quel
person.first_namestringouiPrénom
person.last_namestringouiNom de famille
person.genderM | FnonCivilité
person.birth_datestringnon*Format ISO 8601 strict YYYY-MM-DD (sinon HTTP 400 invalid_birth_date)
person.address.streetstringnon*Rue + numéro
person.address.citystringnon*Ville
person.address.postal_codestringnon*5 chiffres (zéro-padding auto)
person.emailstringnon*Email (validation format)
person.mobilestringnon*Mobile FR strict (sinon HTTP 400 invalid_mobile)
person.landlinestringnon*Fixe FR strict (sinon HTTP 400 invalid_landline)
person.phonestringnon*Téléphone FR (auto-détection mobile/fixe). Renvoie matches.phone.resolved_as = mobile | landline
match_ruleobjectnonRègle booléenne optionnelle : arbre {"and":[...]} / {"or":[...]} sur les champs (+ alias address = street ET city ET postal_code). Renvoie rule.passed. Malformée -> HTTP 400 invalid_match_rule.
min_scoreintegernonSeuil 1-100 (défaut 50) : un champ est satisfait si son score ≥ min_score. Pris en compte avec match_rule seulement. Hors plage -> HTTP 400 invalid_min_score.

* Au moins un critère discriminant requis en plus de first_name + last_name : birth_date, email, mobile, landline, phone, ou une adresse complète (address.street + address.city + address.postal_code). Sinon HTTP 400 insufficient_data.

Les champs email, mobile, landline et phone acceptent la valeur en clair OU son empreinte MD5 (32 hex) / SHA256 (64 hex), auto-détectée. L'empreinte porte sur la forme canonique (email en minuscules ; téléphone national FR à 10 chiffres). Un champ haché ne renvoie que match ou no_match.

Réponse 200

ChampTypeDescription
transaction_idstringIdentifiant unique de la transaction
referencestring | nullRéférence fournie par le client (passthrough)
scorenumber | nullScore global 0-100 (moyenne des champs avec status match / partial / no_match). Les statuts missing et not_searched sont exclus du calcul. Plafonné à 50 si ambiguous=true.
ambiguousbooleantrue si plusieurs profils distincts matchent les critères fournis (homonymes au même nom+adresse avec contacts divergents). Le client doit fournir plus de contacts (email, mobile, DOB) pour lever l'ambiguïté.
matches[field].statusenummatch, partial, no_match, not_searched, missing
matches[field].scorenumber | nullScore 0-100 pour ce champ (null si missing ou not_searched)
timing_msnumberDurée du traitement côté serveur
ruleobjectPrésent uniquement si match_rule est fourni : {passed, min_score} (verdict de la règle ; n'altère ni matches ni score).

Exemple

curl -X POST https://verify.zecible.fr/api/person \
  -H 'X-API-Key: vkey-...' \
  -H 'Content-Type: application/json' \
  -d '{
    "reference": "tx-001",
    "person": {
      "first_name": "Jean",
      "last_name": "Dupont",
      "birth_date": "1980-05-15"
    }
  }'
const r = await fetch('https://verify.zecible.fr/api/person', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.VERIFY_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    reference: 'tx-001',
    person: { first_name: 'Jean', last_name: 'Dupont', birth_date: '1980-05-15' }
  })
});
const data = await r.json();
console.log(data.score, data.matches);
import requests, os
r = requests.post(
    'https://verify.zecible.fr/api/person',
    headers={'X-API-Key': os.environ['VERIFY_KEY']},
    json={
        'reference': 'tx-001',
        'person': {'first_name': 'Jean', 'last_name': 'Dupont', 'birth_date': '1980-05-15'}
    }
)
print(r.json()['score'])
<?php
$ch = curl_init('https://verify.zecible.fr/api/person');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'X-API-Key: ' . getenv('VERIFY_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'reference' => 'tx-001',
        'person'    => ['first_name' => 'Jean', 'last_name' => 'Dupont', 'birth_date' => '1980-05-15'],
    ]),
]);
$data = json_decode(curl_exec($ch), true);
echo $data['score'];
renvoyé tel quel dans la réponsetag
prénom (accents acceptés)person
nom de famille (accents acceptés)badge
M masculin / F fémininwc
ISO 8601 strict (ex 1980-05-15)cake
rue + numérosignpost
libellé BAN normalisélocation_city
5 chiffres (zero-pad auto si < 5)markunread_mailbox
format RFC 5321 (un seul email)alternate_email
FR 10 chiffres - auto mobile/fixe (renvoie resolved_as)phone_iphone
FR 10 chiffres mobile strict (06/07)smartphone
FR 10 chiffres fixe strict (01-05, 09)call
Arbre JSON {and|or:[...]} sur les champs
Seuil 1-100 (défaut 50)speed

infoDonnées de démonstration fictives (aucune identité réelle).

Réponse
Soumettez le formulaire pour voir la réponse JSON.
POST /api/business BETA

Vérification d'une entreprise FR (KYB) contre la base B2B : SIRET, raison sociale, contacts, dirigeants. Score 0-100 par champ.

database
Base+50M enregistrements entreprises FR
speed
LatenceRéponse synchrone en temps réel, au format JSON
manage_accounts
DirigeantsJusqu'à 10 par requête, best-match indépendant
warning
Cas ambiguScore plafonné à 50 si SIRET multiples

Headers requis

HeaderValeurRequisDescription
X-API-Keyvkey-...ouiClé d'authentification du compte (sinon HTTP 401 missing_api_key)
Content-Typeapplication/jsonouiType du corps de requête JSON. Le corps est parsé en JSON quelle que soit la valeur.

Body

ChampTypeRequisDescription
referencestringnonIdentifiant client retourné tel quel
business.siretstringnon*14 chiffres (établissement)
business.sirenstringnon*9 chiffres (entité juridique)
business.vat_intracomstringnonN° TVA intracom (dérivé du SIREN, vérifié par clé)
business.namestringnon*Raison sociale
business.legal_formstringnonForme juridique (SAS, SARL, EURL...)
business.nafstringnonCode NAF / APE (ex : 7311Z)
business.address.streetstringnonRue + numéro
business.address.citystringnonVille
business.address.postal_codestringnon5 chiffres
business.emailstringnonEmail de contact
business.mobilestringnonMobile FR strict (sinon HTTP 400 invalid_mobile)
business.landlinestringnonFixe FR strict (sinon HTTP 400 invalid_landline)
business.phonestringnonTéléphone FR (auto-détection mobile/fixe). Renvoie matches.business.phone.resolved_as = mobile | landline
business.websitestringnonURL du site web
executives[N].first_namestringnonPrénom du dirigeant ou contact
executives[N].last_namestringnonNom du dirigeant ou contact
executives[N].rolestringnonFonction (président, gérant, directeur, etc.)

* Au moins un parmi : business.siret, business.siren, ou business.name + (business.address.city OU business.address.postal_code). executives[] : jusqu'à 10 entrées par requête (sinon HTTP 400 too_many_executives). Scoring best-match indépendant pour chaque entrée contre tous les contacts du SIRET de la société matchée (cap 100 contacts/SIRET). En cas d'ambiguous=true, le pool reste limité au Niveau 1 du pipeline.

Les champs siren, email, mobile, landline et phone acceptent la valeur en clair OU son empreinte MD5 (32 hex) / SHA256 (64 hex), auto-détectée (siren : 9 chiffres bruts). Un champ haché ne renvoie que match ou no_match.

Réponse 200

ChampTypeDescription
transaction_idstringIdentifiant unique de la transaction
referencestring | nullRéférence fournie par le client (passthrough)
scorenumber | nullScore global 0-100 (moyenne des champs avec status match / partial / no_match). Les statuts missing et not_searched sont exclus du calcul. Plafonné à 50 si ambiguous=true.
ambiguousbooleantrue si plusieurs entreprises distinctes matchent les critères fournis (établissements du même groupe ou homonymes au même nom+adresse avec contacts divergents). Le client doit fournir SIRET ou un contact unique (email, phone, website) pour lever l'ambiguïté.
matches.business.<field>.statusenummatch, partial, no_match, not_searched, missing
matches.business.<field>.scorenumber | nullScore 0-100 du champ (null si missing ou not_searched)
matches.executivesarrayToujours présent (vide si aucun dirigeant en input). Ordre préservé = ordre d'input.
matches.executives[N].<field>objectStatut + score par champ pour chaque dirigeant input (best-match indépendant, même enum que business)
timing_msnumberDurée du traitement côté serveur

Exemple

curl -X POST https://verify.zecible.fr/api/business \
  -H 'X-API-Key: vkey-...' \
  -H 'Content-Type: application/json' \
  -d '{
    "reference": "tx-biz-001",
    "business": {
      "siret": "00000000000000",
      "name": "ACME SAS",
      "legal_form": "SAS"
    },
    "executives": [
      { "first_name": "Jean", "last_name": "DUPONT", "role": "président" }
    ]
  }'
const r = await fetch('https://verify.zecible.fr/api/business', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.VERIFY_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    reference: 'tx-biz-001',
    business: { siret: '00000000000000', name: 'ACME SAS' },
    executives: [{ first_name: 'Jean', last_name: 'DUPONT' }]
  })
});
console.log((await r.json()).score);
import requests, os
r = requests.post(
    'https://verify.zecible.fr/api/business',
    headers={'X-API-Key': os.environ['VERIFY_KEY']},
    json={
        'reference': 'tx-biz-001',
        'business': {'siret': '00000000000000', 'name': 'ACME SAS'},
        'executives': [{'first_name': 'Jean', 'last_name': 'DUPONT'}]
    }
)
print(r.json()['score'])
<?php
$ch = curl_init('https://verify.zecible.fr/api/business');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'X-API-Key: ' . getenv('VERIFY_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'reference'  => 'tx-biz-001',
        'business'   => ['siret' => '00000000000000', 'name' => 'ACME SAS'],
        'executives' => [['first_name' => 'Jean', 'last_name' => 'DUPONT']],
    ]),
]);
$data = json_decode(curl_exec($ch), true);
echo $data['score'];
renvoyé tel quel dans la réponsetag
14 chiffres (espaces tolérés)fingerprint
9 chiffres (entité juridique)fingerprint
FR + 11 chiffres (calculé depuis SIREN)payments
raison sociale ou enseigne (fuzzy)storefront
SAS, SARL, EURL, SA, SCI...gavel
4 chiffres + 1 lettre (ex 7311Z)category
ex : 12 rue de la Paixsignpost
ville de l'établissementlocation_city
5 chiffresmarkunread_mailbox
email de contact (RFC 5321)alternate_email
FR 10 chiffres (auto mobile/fixe)call
URL complète (https:// inclus)language
FR 10 chiffres mobile strict (06/07)smartphone
FR 10 chiffres fixe strict (01-05, 09)call
prénom (accents OK)person
nom de famille (accents OK)badge
président, gérant, directeur (fuzzy)work

infoDonnées de démonstration fictives (aucune identité réelle).

Réponse
Soumettez le formulaire pour voir la réponse JSON.
GET /api/usage

Reporting de consommation : nombre de requêtes + breakdown par produit facturable + total HT.

person
ScopeUtilisateur API du caller (clé courante)
date_range
Période défautMois en cours (1er → dernier jour)
euro
DeviseEUR HT, prorata réel par produit

Headers requis

HeaderValeurRequisDescription
X-API-Keyvkey-...ouiClé d'authentification du compte (sinon HTTP 401 missing_api_key)

Query params

ParamTypeRequisDescription
startstringnonDate début YYYY-MM-DD (défaut : 1er jour mois courant)
stopstringnonDate fin YYYY-MM-DD (défaut : dernier jour mois courant)

Réponse 200

ChampTypeDescription
periodobject{ start, stop } - période effectivement requêtée
requestsnumberNombre total de requêtes
succeedednumberRequêtes avec au moins un produit facturable
failednumberRequêtes sans aucun produit facturable
total_pretaxnumberTotal HT EUR sur la période
currencystringCode devise (EUR)
products[]arrayVentilation par produit (key, label, count, cpm_pretax, total_pretax)

Exemple

curl 'https://verify.zecible.fr/api/usage?start=2026-05-01&stop=2026-05-31' \
  -H 'X-API-Key: vkey-...'
YYYY-MM-DD - défaut : 1er jour du mois en coursevent_available
YYYY-MM-DD - défaut : dernier jour du mois en coursevent_busy

infoDonnées de démonstration fictives (aucune identité réelle).

Réponse
Soumettez le formulaire pour voir la réponse JSON.
429 Rate limiting

Compteur global par clé API, fenêtre fixe d'1 minute, partagé entre /api/person, /api/business et /api/usage. Limite par défaut : 60 requêtes par minute. Surchargeable per-account (nous contacter pour les besoins entreprise).

Headers de réponse (toutes les réponses)

HeaderDescription
X-RateLimit-LimitQuota maximum pour la fenêtre courante (ex : 60). 0 = compte sans limite.
X-RateLimit-RemainingQuota restant après cette requête. -1 si le compte est sans limite.
X-RateLimit-ResetTimestamp Unix UTC du prochain reset (début de la prochaine minute). 0 si compte sans limite.
Retry-AfterPrésent uniquement sur HTTP 429. Nombre de secondes avant le reset de la fenêtre.

Réponse HTTP 429 (quota dépassé)

ChampDescription
error"rate_limit_exceeded"
messageMessage lisible avec le nombre de secondes avant le prochain reset.
limitQuota du compte.
reset_unixTimestamp Unix UTC du prochain reset.

Stratégie recommandée

  • Lire X-RateLimit-Remaining à chaque réponse pour anticiper le throttling côté client.
  • En cas de HTTP 429, respecter Retry-After avant de réessayer (backoff exponentiel inutile : la fenêtre reset à la minute exacte).
  • Pour des volumes > 60 req/min, demander un quota relevé : nous contacter.
  • Le compteur démarre à la première requête de chaque minute UTC, indépendamment de l'endpoint appelé.

Exemple de réponse 429

# HTTP 429 Too Many Requests
# Headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1779994620
Retry-After: 42

# Body
{
  "error":      "rate_limit_exceeded",
  "message":    "Limite de 60 requetes par minute atteinte. Reessayer dans 42s.",
  "limit":      60,
  "reset_unix": 1779994620
}
4XX Codes d'erreur

Liste exhaustive des codes d'erreur renvoyés par l'API. Format de réponse uniforme : {"error": "code_machine", "message": "explication humaine"}. Toujours en JSON, charset UTF-8.

Authentification (401)

CodeEndpoint(s)Cause
missing_api_keytousHeader X-API-Key absent ou vide.
invalid_api_keytousClé inconnue ou révoquée. Vérifier la valeur exacte (sensible à la casse) et qu'elle correspond bien à un compte actif.
account_disabledtousCompte supprimé ou désactivé côté Zecible. Contacter le support pour réactivation.
ip_not_allowedtousIP appelante absente de la whitelist du compte. Le message contient l'IP rejetée. Ajouter l'IP via le support.

Méthode HTTP (405)

CodeEndpoint(s)Cause
method_not_allowedtousPOST attendu sur /api/person et /api/business. GET attendu sur /api/usage.

Validation du body (400)

CodeEndpoint(s)Cause
invalid_bodyperson, businessBody absent, JSON malformé, ou root non-objet.
invalid_referenceperson, businessLe champ reference dépasse 256 caractères. Raccourcir le passthrough client.
missing_required_fieldspersonperson.first_name et person.last_name sont obligatoires.
missing_required_fieldsbusinessAu moins un parmi business.siret, business.siren, ou business.name + business.address.city OU business.address.postal_code.
insufficient_datapersonIdentité fournie mais aucun critère discriminant (birth_date, téléphone, email ou adresse complète) pour identifier une cible.
insufficient_databusinessAucune combinaison exploitable (SIRET / SIREN / nom + adresse / nom + contact).
invalid_birth_datepersonperson.birth_date n'est pas au format ISO 8601 strict YYYY-MM-DD.
invalid_mobileperson, businessLe champ mobile doit être un numéro mobile FR (10 chiffres débutant par 06 ou 07).
invalid_landlineperson, businessLe champ landline doit être un numéro fixe FR (pas un mobile).
invalid_phoneperson, businessLe champ phone doit être un numéro de téléphone FR valide (10 chiffres).
invalid_emailperson, businessLe champ email doit être une adresse email valide.
invalid_genderpersonLe champ gender doit être M ou F.
invalid_siretbusinessLe champ siret doit être un SIRET valide (14 chiffres, clé de Luhn).
invalid_sirenbusinessLe champ siren doit être un SIREN valide (9 chiffres, clé de Luhn).
too_many_executivesbusinessexecutives[] contient plus de 10 entrées. Limiter la requête à 10 dirigeants ou en chaîner plusieurs.

Taille du payload (413)

CodeEndpoint(s)Cause
body_too_largeperson, businessBody de requête supérieur à 32 KB. Réduire la taille du JSON (ex : limiter le nombre de dirigeants, raccourcir les champs texte).

Quotas (429)

CodeEndpoint(s)Cause
rate_limit_exceededtousQuota par minute atteint. Voir la section Rate limiting pour la stratégie de retry et les headers X-RateLimit-*.

Crédits / budget (402)

CodeEndpoint(s)Cause
quota_exceededperson, businessPlafond de crédits de la période atteint (budget mensuel ou portefeuille épuisable du compte). Les headers X-Credit-Limit / X-Credit-Used / X-Credit-Remaining (EUR HT) indiquent le budget le plus contraignant. Contacter le support pour relever le plafond.

Format de réponse

# HTTP 4xx
# Content-Type: application/json; charset=UTF-8

{
  "error":   "invalid_birth_date",
  "message": "person.birth_date doit etre au format ISO 8601 (YYYY-MM-DD)."
}

Le champ error est stable et destiné au traitement programmatique. Le champ message est destiné à l'affichage et peut évoluer entre versions.