Prüfung einer Privatpersonen-Identität gegen die FR-Personendatenbank. Score 0-100 pro Feld + aggregierter Gesamtscore.
Erforderliche Header
| Header | Wert | Pflichtfeld | Beschreibung |
|---|---|---|---|
| X-API-Key | vkey-... | ja | Authentifizierungsschlüssel des Kontos (sonst HTTP 401 missing_api_key) |
| Content-Type | application/json | ja | Typ des Anfrage-Body (JSON). Der Body wird unabhängig vom Wert als JSON geparst. |
Body
| Feld | Typ | Pflichtfeld | Beschreibung |
|---|---|---|---|
| reference | string | nein | Kundenkennung, unverändert zurückgegeben |
| person.first_name | string | ja | Vorname |
| person.last_name | string | ja | Nachname |
| person.gender | M | F | nein | Anrede |
| person.birth_date | string | nein* | Striktes ISO-8601-Format YYYY-MM-DD (sonst HTTP 400 invalid_birth_date) |
| person.address.street | string | nein* | Straße + Hausnummer |
| person.address.city | string | nein* | Stadt |
| person.address.postal_code | string | nein* | 5 Ziffern (automatisches Null-Padding) |
| person.email | string | nein* | E-Mail (Formatvalidierung) |
| person.mobile | string | nein* | FR-Mobilnummer strict (sonst HTTP 400 invalid_mobile) |
| person.landline | string | nein* | FR-Festnetz strict (sonst HTTP 400 invalid_landline) |
| person.phone | string | nein* | FR-Telefon (automatische Erkennung mobil/Festnetz). Gibt matches.phone.resolved_as = mobile | landline zurück |
| match_rule | object | nein | Optionale boolesche Regel: Baum {"and":[...]} / {"or":[...]} über die Felder (+ Alias address = street UND city UND postal_code). Gibt rule.passed zurück. Fehlerhaft -> HTTP 400 invalid_match_rule. |
| min_score | integer | nein | Schwellenwert 1-100 (Standard 50): ein Feld gilt als erfüllt, wenn sein Score ≥ min_score. Nur mit match_rule berücksichtigt. Außerhalb des Bereichs -> HTTP 400 invalid_min_score. |
* Mindestens ein unterscheidendes Kriterium neben first_name + last_name erforderlich: birth_date, email, mobile, landline, phone oder vollständige Adresse (address.street + address.city + address.postal_code). Sonst HTTP 400 insufficient_data.
Die Felder email, mobile, landline und phone akzeptieren den Klartextwert ODER seinen MD5- (32 Hex) / SHA256-Hash (64 Hex), automatisch erkannt. Der Hash wird auf der kanonischen Form berechnet (E-Mail in Kleinbuchstaben; französische nationale Rufnummer mit 10 Ziffern). Ein gehashtes Feld gibt nur match oder no_match zurück.
Antwort 200
| Feld | Typ | Beschreibung |
|---|---|---|
| transaction_id | string | Eindeutige Transaktions-ID |
| reference | string | null | Vom Kunden übermittelte Referenz (Passthrough) |
| score | number | null | Gesamtscore 0-100 (Durchschnitt der Felder mit Status match / partial / no_match). Status missing und not_searched werden nicht einbezogen. Auf 50 begrenzt wenn ambiguous=true. |
| ambiguous | boolean | true wenn mehrere unterschiedliche Profile die übermittelten Kriterien erfüllen (Namensgleichheit mit gleicher Adresse aber abweichenden Kontaktdaten). Der Kunde muss mehr Kontaktinformationen angeben (E-Mail, Mobil, Geburtsdatum), um die Mehrdeutigkeit aufzulösen. |
| matches[field].status | enum | match, partial, no_match, not_searched, missing |
| matches[field].score | number | null | Score 0-100 für dieses Feld (null wenn missing oder not_searched) |
| timing_ms | number | Verarbeitungsdauer serverseitig |
| rule | object | Nur vorhanden, wenn match_rule angegeben ist: {passed, min_score} (Regelergebnis; ändert weder matches noch score). |
Beispiel
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'];
Prüfung eines FR-Unternehmens (KYB) gegen die B2B-Datenbank: SIRET, Firmenname, Kontakte, Führungskräfte. Score 0-100 pro Feld.
Erforderliche Header
| Header | Wert | Pflichtfeld | Beschreibung |
|---|---|---|---|
| X-API-Key | vkey-... | ja | Authentifizierungsschlüssel des Kontos (sonst HTTP 401 missing_api_key) |
| Content-Type | application/json | ja | Typ des Anfrage-Body (JSON). Der Body wird unabhängig vom Wert als JSON geparst. |
Body
| Feld | Typ | Pflichtfeld | Beschreibung |
|---|---|---|---|
| reference | string | nein | Kundenkennung, unverändert zurückgegeben |
| business.siret | string | nein* | 14 Ziffern (Betriebsstätte) |
| business.siren | string | nein* | 9 Ziffern (juristische Einheit) |
| business.vat_intracom | string | nein | USt-IdNr. (aus SIREN abgeleitet, per Schlüssel verifiziert) |
| business.name | string | nein* | Firmenname |
| business.legal_form | string | nein | Rechtsform (SAS, SARL, EURL...) |
| business.naf | string | nein | NAF/APE-Code (z.B. 7311Z) |
| business.address.street | string | nein | Straße + Hausnummer |
| business.address.city | string | nein | Stadt |
| business.address.postal_code | string | nein | 5 Ziffern |
| business.email | string | nein | Kontakt-E-Mail |
| business.mobile | string | nein | FR-Mobilnummer strict (sonst HTTP 400 invalid_mobile) |
| business.landline | string | nein | FR-Festnetz strict (sonst HTTP 400 invalid_landline) |
| business.phone | string | nein | FR-Telefon (automatische Erkennung mobil/Festnetz). Gibt matches.business.phone.resolved_as = mobile | landline zurück |
| business.website | string | nein | Website-URL |
| executives[N].first_name | string | nein | Vorname der Führungskraft oder des Kontakts |
| executives[N].last_name | string | nein | Nachname der Führungskraft oder des Kontakts |
| executives[N].role | string | nein | Funktion (Vorsitzender, Geschäftsführer, Direktor, usw.) |
* Mindestens eines von: business.siret, business.siren oder business.name + (business.address.city ODER business.address.postal_code). executives[]: bis zu 10 Einträge pro Anfrage (sonst HTTP 400 too_many_executives). Unabhängiges Best-Match-Scoring für jeden Eintrag gegen alle Kontakte des SIRET des gematchten Unternehmens (max. 100 Kontakte/SIRET). Bei ambiguous=true bleibt der Pool auf Level 1 der Pipeline beschränkt.
Die Felder siren, email, mobile, landline und phone akzeptieren den Klartextwert ODER seinen MD5- (32 Hex) / SHA256-Hash (64 Hex), automatisch erkannt (siren: 9 reine Ziffern). Ein gehashtes Feld gibt nur match oder no_match zurück.
Antwort 200
| Feld | Typ | Beschreibung |
|---|---|---|
| transaction_id | string | Eindeutige Transaktions-ID |
| reference | string | null | Vom Kunden übermittelte Referenz (Passthrough) |
| score | number | null | Gesamtscore 0-100 (Durchschnitt der Felder mit Status match / partial / no_match). Status missing und not_searched werden nicht einbezogen. Auf 50 begrenzt wenn ambiguous=true. |
| ambiguous | boolean | true wenn mehrere unterschiedliche Unternehmen die übermittelten Kriterien erfüllen (Betriebsstätten desselben Konzerns oder Namensgleichheit mit gleicher Adresse aber abweichenden Kontaktdaten). Der Kunde muss SIRET oder einen eindeutigen Kontakt (E-Mail, Telefon, Website) angeben, um die Mehrdeutigkeit aufzulösen. |
| matches.business.<field>.status | enum | match, partial, no_match, not_searched, missing |
| matches.business.<field>.score | number | null | Score 0-100 des Feldes (null wenn missing oder not_searched) |
| matches.executives | array | Immer vorhanden (leer wenn keine Führungskräfte im Input). Reihenfolge beibehalten = Input-Reihenfolge. |
| matches.executives[N].<field> | object | Status + Score pro Feld für jede Führungskraft im Input (unabhängiges Best-Match, gleiche Enum wie business) |
| timing_ms | number | Verarbeitungsdauer serverseitig |
Beispiel
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'];
Nutzungsreporting: Anzahl der Anfragen + Aufschlüsselung nach abrechenbarem Produkt + Gesamtbetrag netto.
Erforderliche Header
| Header | Wert | Pflichtfeld | Beschreibung |
|---|---|---|---|
| X-API-Key | vkey-... | ja | Authentifizierungsschlüssel des Kontos (sonst HTTP 401 missing_api_key) |
Query-Parameter
| Parameter | Typ | Pflichtfeld | Beschreibung |
|---|---|---|---|
| start | string | nein | Startdatum YYYY-MM-DD (Standard: erster Tag des aktuellen Monats) |
| stop | string | nein | Enddatum YYYY-MM-DD (Standard: letzter Tag des aktuellen Monats) |
Antwort 200
| Feld | Typ | Beschreibung |
|---|---|---|
| period | object | { start, stop } - tatsächlich abgefragter Zeitraum |
| requests | number | Gesamtanzahl der Anfragen |
| succeeded | number | Anfragen mit mindestens einem abrechenbaren Produkt |
| failed | number | Anfragen ohne abrechenbares Produkt |
| total_pretax | number | Gesamtbetrag netto EUR im Zeitraum |
| currency | string | Währungscode (EUR) |
| products[] | array | Aufschlüsselung nach Produkt (key, label, count, cpm_pretax, total_pretax) |
Beispiel
curl 'https://verify.zecible.fr/api/usage?start=2026-05-01&stop=2026-05-31' \ -H 'X-API-Key: vkey-...'
Globaler Zähler pro API-Schlüssel, festes 1-Minuten-Fenster, gemeinsam für /api/person, /api/business und /api/usage. Standardlimit: 60 Anfragen pro Minute. Pro Konto überschreibbar (Kontakt aufnehmen für Unternehmensbedarf).
Antwort-Header (alle Antworten)
| Header | Beschreibung |
|---|---|
| X-RateLimit-Limit | Maximales Kontingent für das aktuelle Fenster (z.B. 60). 0 = Konto ohne Limit. |
| X-RateLimit-Remaining | Verbleibendes Kontingent nach dieser Anfrage. -1 wenn das Konto kein Limit hat. |
| X-RateLimit-Reset | Unix-UTC-Timestamp des nächsten Resets (Beginn der nächsten Minute). 0 bei Konto ohne Limit. |
| Retry-After | Nur bei HTTP 429 vorhanden. Anzahl der Sekunden bis zum Fenster-Reset. |
HTTP-Antwort 429 (Kontingent überschritten)
| Feld | Beschreibung |
|---|---|
| error | "rate_limit_exceeded" |
| message | Lesbarer Text mit der Anzahl der Sekunden bis zum nächsten Reset. |
| limit | Kontingent des Kontos. |
| reset_unix | Unix-UTC-Timestamp des nächsten Resets. |
Empfohlene Strategie
X-RateLimit-Remainingbei jeder Antwort auslesen, um clientseitiges Throttling zu antizipieren.- Bei HTTP 429 den
Retry-After-Wert abwarten, bevor ein erneuter Versuch gestartet wird (exponentielles Backoff unnötig: das Fenster resettet zur genauen Minute). - Für Volumen > 60 Anfragen/Min. ein erhöhtes Kontingent anfragen: Kontakt aufnehmen.
- Der Zähler startet mit der ersten Anfrage jeder UTC-Minute, unabhängig vom aufgerufenen Endpoint.
Beispiel einer 429-Antwort
# 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 }
Vollständige Liste der von der API zurückgegebenen Fehlercodes. Einheitliches Antwortformat: {"error": "code_machine", "message": "explication humaine"}. Immer als JSON, Zeichensatz UTF-8.
Authentifizierung (401)
| Code | Endpoint(s) | Ursache |
|---|---|---|
| missing_api_key | alle | Header X-API-Key fehlt oder ist leer. |
| invalid_api_key | alle | Unbekannter oder widerrufener Schlüssel. Genauen Wert prüfen (Groß-/Kleinschreibung beachten) und sicherstellen, dass er einem aktiven Konto entspricht. |
| account_disabled | alle | Konto seitens Zecible gelöscht oder deaktiviert. Support für Reaktivierung kontaktieren. |
| ip_not_allowed | alle | Anfragende IP nicht in der Whitelist des Kontos. Die abgelehnte IP ist in der Fehlermeldung enthalten. IP über den Support hinzufügen. |
HTTP-Methode (405)
| Code | Endpoint(s) | Ursache |
|---|---|---|
| method_not_allowed | alle | POST erwartet für /api/person und /api/business. GET erwartet für /api/usage. |
Body-Validierung (400)
| Code | Endpoint(s) | Ursache |
|---|---|---|
| invalid_body | person, business | Body fehlt, JSON fehlerhaft oder Root kein Objekt. |
| invalid_reference | person, business | Das Feld reference überschreitet 256 Zeichen. Passthrough-Wert kürzen. |
| missing_required_fields | person | person.first_name und person.last_name sind Pflichtfelder. |
| missing_required_fields | business | Mindestens eines von business.siret, business.siren oder business.name + business.address.city ODER business.address.postal_code. |
| insufficient_data | person | Identität übermittelt, aber kein unterscheidendes Kriterium (Geburtsdatum, Telefon, E-Mail oder vollständige Adresse) zur Identifikation eines Ziels. |
| insufficient_data | business | Keine verwertbare Kombination (SIRET / SIREN / Name + Adresse / Name + Kontakt). |
| invalid_birth_date | person | person.birth_date entspricht nicht dem strikten ISO-8601-Format YYYY-MM-DD. |
| invalid_mobile | person, business | Das Feld mobile muss eine FR-Mobilnummer sein (10 Ziffern, beginnend mit 06 oder 07). |
| invalid_landline | person, business | Das Feld landline muss eine FR-Festnetznummer sein (keine Mobilnummer). |
| invalid_phone | person, business | Das Feld phone muss eine gültige FR-Telefonnummer sein (10 Ziffern). |
| invalid_email | person, business | Das Feld email muss eine gültige E-Mail-Adresse sein. |
| invalid_gender | person | Das Feld gender muss M oder F sein. |
| invalid_siret | business | Das Feld siret muss eine gültige SIRET sein (14 Ziffern, Luhn-Prüfsumme). |
| invalid_siren | business | Das Feld siren muss eine gültige SIREN sein (9 Ziffern, Luhn-Prüfsumme). |
| too_many_executives | business | executives[] enthält mehr als 10 Einträge. Anfrage auf 10 Führungskräfte begrenzen oder mehrere Anfragen verketten. |
Payload-Größe (413)
| Code | Endpoint(s) | Ursache |
|---|---|---|
| body_too_large | person, business | Anfrage-Body größer als 32 KB. JSON-Größe reduzieren (z.B. Anzahl der Führungskräfte begrenzen, Textfelder kürzen). |
Kontingente (429)
| Code | Endpoint(s) | Ursache |
|---|---|---|
| rate_limit_exceeded | alle | Minutenkontingent erreicht. Siehe Abschnitt Rate-Limiting für die Retry-Strategie und die Header X-RateLimit-*. |
Credits / Budget (402)
| Code | Endpoint(s) | Ursache |
|---|---|---|
| quota_exceeded | person, business | Periodenkreditlimit erreicht (monatliches Budget oder erschöpfbares Guthaben des Kontos). Die Header X-Credit-Limit / X-Credit-Used / X-Credit-Remaining (EUR netto) zeigen das restriktivste Budget an. Support für Erhöhung des Limits kontaktieren. |
Antwortformat
# 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)." }
Das Feld error ist stabil und für die programmatische Verarbeitung bestimmt. Das Feld message ist für die Anzeige vorgesehen und kann sich zwischen Versionen ändern.