Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Basile Enrichissement API
version: 1.0.0
description: '**Basile** est une base de données B2B française : ~26,8 M sociétés et ~29,5 M
contacts, issus du croisement de trois sources :
- **Legal** — registre légal français (INSEE / INPI-RNE) : dirigeants, SIREN, NAF, forme juridique…
- **LKI** — profils LinkedIn : intitulé de poste, séniorité, compétences, écoles…
- **GMB** — Google My Business : note, avis, horaires, catégorie…
L''API expose deux recherches symétriques — **personnes** (`/people`) et
**entreprises** (`/companies`) — avec un système de filtres `include`/`exclude`,
un comptage gratuit avant extraction, et un export CSV enrichi.
### Authentification
Toutes les requêtes portent la clé API dans le header `Authorization`, **en clair,
sans préfixe `Bearer`** : `Authorization: REDACTED_STRIPE_KEY`.
### `find` vs `export` : ce que chacun renvoie
Les deux acceptent exactement les mêmes `filters`. La différence est la **richesse des données**.
- **`/people/find` renvoie la personne, PAS sa société.** Identité, localisation, poste,
URL LinkedIn, plus le **nom** et l''**identifiant interne** de son employeur — mais **aucune**
donnée de cette société : ni SIREN/NAF/forme juridique, ni effectif/secteur/description,
ni note/avis/adresse Google.
- **`/people/export` renvoie la ligne complète : 92 colonnes** — 22 sur la personne,
**18 sur sa société LinkedIn**, **31 sur sa société au registre légal**, **11 Google Maps**,
6 drapeaux de présence. Pour UNE fiche, `GET /people/{id}/full` rend le même modèle sans
passer par un export.
- **`/companies/find` croise DÉJÀ les trois sources** : chaque résultat porte sa fiche et,
quand la correspondance existe, celles des deux autres sources sous `x_legal` / `x_lki` /
`x_gmb`. Contrairement aux personnes, inutile d''exporter pour voir les trois sources.
- **`/companies/export` renvoie 70 colonnes** : les mêmes données mises à plat en CSV.
Règle pratique : pour des contacts avec le contexte de leur entreprise, `/find` sert à cibler
et à compter, puis **c''est `/export` qui produit la donnée exploitable**.
### Facturation
**Compter est gratuit, sans limite.** Envoyez `{ "countOnly": true }` (ou `limit: 0`) :
la réponse contient `total` et un tableau `leads` vide, et **rien n''est débité**.
**Récupérer des enregistrements coûte 1 crédit par enregistrement renvoyé** — sur `/find`
(hors mode count-only), sur `/people/{id}` et `/companies/{id}`, et sur `/export`. Le débit
a lieu **à chaque appel** : redemander les mêmes enregistrements est **re-facturé** (la
donnée a pu être rafraîchie depuis ; il n''existe pas de registre « déjà payé »).
⚠️ `limit: 1` n''est **pas** un mode de comptage : il renvoie 1 enregistrement, donc
1 crédit. Pour compter, utilisez `countOnly: true`.
Quota épuisé → **402** `quota_exhausted`, renvoyé **avant** les enregistrements : rien
n''est livré et rien n''est débité.
Règle d''or : **comptez d''abord** avec `countOnly`, puis n''extrayez que les enregistrements
dont vous avez besoin.'
contact:
name: Support Basile
url: https://basile.cc
email: contact@basile.cc
servers:
- url: https://api.basile.cc
description: Production
security:
- ApiKeyAuth: []
tags:
- name: Enrichissement
description: 'E-mail et numéro de n''importe quelle personne, dans Basile ou non, payés sur le solde
d''enrichissement : 5 centimes par e-mail trouvé, 50 centimes par numéro trouvé, rien si rien
n''est trouvé. En deux étapes (POST rend un id tout de suite, GET rend le résultat). Aucune
limite par minute ni par jour, hors des limites du forfait : une file fait patienter, seul un
plafond de sécurité répond 429. Une clé « réservée à l''enrichissement » (compte à crédits sans
forfait API) n''ouvre que ces routes ; ailleurs, 403 `key_enrichment_only`.'
paths:
/enrichment/find:
post:
tags:
- Enrichissement
summary: 'Étape 1 : envoyer une demande d''enrichissement'
description: 'Rend tout de suite un `id` (202) ; le résultat se lit avec `GET /enrichment/find/{id}`.
L''e-mail exige `domain` et/ou `company_name` (avec les deux : domaine puis société, dans la
même recherche, un seul prix) ; le numéro exige `linkedin_url`. Le montant est réservé quand
la recherche part, débité si elle trouve, rendu sinon. Une coordonnée déjà possédée est
resservie gratuitement. Même `request_id` (ou en-tête `Idempotency-Key`) = même demande,
rien relancé ni refacturé. Avec `"wait": true`, le serveur attend jusqu''à ~25 s le verdict.'
operationId: enrichmentFind
parameters:
- name: Idempotency-Key
in: header
required: false
schema:
type: string
maxLength: 100
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentFindRequest'
example:
full_name: Jean Dupont
domain: acme.fr
company_name: Acme
linkedin_url: https://www.linkedin.com/in/jean-dupont
find: both
request_id: crm-contact-4812
responses:
'200':
description: Déjà terminé (déjà possédé, introuvable récent, ou wait:true).
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentFindResponse'
'202':
description: 'Acceptée : relire GET /enrichment/find/{id}.'
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentFindResponse'
'400':
description: Entrée invalide (name_required, domain_or_company_required, linkedin_url_required, invalid_domain, invalid_linkedin_url, invalid_find, invalid_request_id, invalid_person_id, invalid_json…).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/WalletInsufficient'
'403':
description: api_plan_required (ni crédits ni forfait API), payment_past_due (paiement du renouvellement du forfait API en échec, sans crédits d'enrichissement ; billing_url) ou email_verification_required.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: 'request_id_conflict : ce request_id a servi pour une autre demande.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/EnrichmentSafetyCap'
/enrichment/find/{id}:
get:
tags:
- Enrichissement
summary: 'Étape 2 : récupérer le statut et le résultat'
description: 'Relire toutes les 3 s (`retry_after_seconds`) tant que `status` vaut `pending` (202) ;
200 et `status: completed` à la fin. Relire ne relance ni ne refacture rien ; la recherche va
à son terme même sans relecture.'
operationId: enrichmentStatus
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Terminée.
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentFindResponse'
'202':
description: En cours.
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentFindResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/enrichment/find/batch:
post:
tags:
- Enrichissement
summary: Envoi groupé (jusqu'à 100 personnes)
description: 'Chaque personne est traitée comme un `POST /enrichment/find` (mêmes champs, prix et règles),
sous un `batch_id`. Avec un `request_id`, rejouer le lot rend le même lot et lance les
personnes non parties (`not_started`), sans refacturer les autres.'
operationId: enrichmentFindBatch
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentBatchRequest'
responses:
'200':
description: Déjà terminé.
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentBatchResponse'
'202':
description: 'Accepté : relire GET /enrichment/find/batch/{batchId}.'
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentBatchResponse'
'400':
description: people_required, batch_too_large, invalid_json, ou personne invalide (avec index).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/WalletInsufficient'
'409':
description: request_id_conflict.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
$ref: '#/components/responses/EnrichmentSafetyCap'
/enrichment/find/batch/{batchId}:
get:
tags:
- Enrichissement
summary: Suivre un envoi groupé
description: Relire toutes les 5 s tant que `status` vaut `pending` ; un résultat par personne dans `items`, dans l'ordre envoyé.
operationId: enrichmentBatchStatus
parameters:
- name: batchId
in: path
required: true
schema:
type: string
responses:
'200':
description: Terminé.
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentBatchResponse'
'202':
description: En cours.
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentBatchResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/enrichment/wallet:
get:
tags:
- Enrichissement
summary: Solde d'enrichissement, prix et lien de recharge
operationId: enrichmentWallet
responses:
'200':
description: Le solde (gratuit, hors de toute limite).
content:
application/json:
schema:
$ref: '#/components/schemas/EnrichmentWallet'
'401':
$ref: '#/components/responses/Unauthorized'
components:
schemas:
EnrichmentChannel:
type: object
properties:
status:
type: string
enum:
- pending
- found
- not_found
- failed
- skipped
- insufficient_balance
stage:
type: string
enum:
- queued
- searching
description: 'Pour pending : en file, ou recherche en cours.'
value:
type: string
description: La coordonnée, si found.
verified:
type: boolean
description: E-mail vérifié.
already_owned:
type: boolean
description: 'Déjà possédée par le compte : 0 centime.'
reason:
type: string
description: Pour skipped ou failed (ex. linkedin_url_required).
charged_cents:
type: integer
EnrichmentWallet:
type: object
properties:
success:
type: boolean
balance_cents:
type: integer
held_cents:
type: integer
currency:
type: string
example: eur
prices:
type: object
properties:
email_cents:
type: integer
example: 5
phone_cents:
type: integer
example: 50
topup_url:
type: string
Error:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
code:
type: string
description: Le même code que `error`, quand l'API le double (`payment_past_due`).
message:
type: string
messageFr:
type: string
description: Le message en français, quand il existe.
billing_url:
type: string
description: '`payment_past_due` : page Facturation de l''app, où mettre à jour le moyen de paiement.'
EnrichmentFindRequest:
type: object
properties:
full_name:
type: string
maxLength: 120
description: Nom complet (ou first_name + last_name, 60 caractères chacun). Obligatoire.
first_name:
type: string
maxLength: 60
last_name:
type: string
maxLength: 60
domain:
type: string
description: Domaine de la société (une URL ou une adresse e-mail sont réduites au domaine).
company_name:
type: string
maxLength: 150
linkedin_url:
type: string
description: Profil LinkedIn personnel (https://www.linkedin.com/in/…). Obligatoire pour le numéro.
find:
description: 'Ce qu''il faut trouver (défaut : email).'
oneOf:
- type: string
enum:
- email
- phone
- both
- type: array
items:
type: string
enum:
- email
- phone
request_id:
type: string
pattern: ^[A-Za-z0-9._:-]{1,100}$
description: Identifiant d'idempotence (recommandé).
person_id:
type: string
description: 'L''_id d''une personne de /people/find : ce qui est possédé est resservi, ce qui est trouvé est rangé sur la fiche.'
country_code:
type: string
description: Pays de la société, code ISO à deux lettres.
wait:
type: boolean
default: false
description: Attendre le verdict côté serveur (~25 s au plus).
EnrichmentBatchResponse:
type: object
properties:
success:
type: boolean
batch_id:
type: string
status:
type: string
enum:
- pending
- completed
count:
type: integer
not_started:
type: integer
description: 'Personnes non lancées (plafond de sécurité) : rejouer avec le même request_id.'
counts:
type: object
additionalProperties:
type: integer
description: Canaux par statut.
charged_cents:
type: integer
balance_cents:
type: integer
held_cents:
type: integer
currency:
type: string
replayed:
type: boolean
retry_after_seconds:
type: integer
items:
type: array
items:
allOf:
- $ref: '#/components/schemas/EnrichmentFindResponse'
- type: object
properties:
index:
type: integer
ref:
type: string
EnrichmentBatchRequest:
type: object
required:
- people
properties:
people:
type: array
maxItems: 100
items:
allOf:
- $ref: '#/components/schemas/EnrichmentFindRequest'
- type: object
properties:
ref:
type: string
maxLength: 100
description: Votre identifiant, rendu avec le résultat.
find:
type: string
enum:
- email
- phone
- both
description: Pour tout le lot (ou par personne).
request_id:
type: string
pattern: ^[A-Za-z0-9._:-]{1,100}$
EnrichmentFindResponse:
type: object
properties:
success:
type: boolean
id:
type: string
status:
type: string
enum:
- pending
- completed
email:
$ref: '#/components/schemas/EnrichmentChannel'
phone:
$ref: '#/components/schemas/EnrichmentChannel'
charged_cents:
type: integer
description: Total débité pour cette demande.
balance_cents:
type: integer
description: Solde restant.
held_cents:
type: integer
description: Réservé pour les recherches en cours.
currency:
type: string
example: eur
replayed:
type: boolean
description: Demande rejouée par son request_id.
retry_after_seconds:
type: integer
description: Délai conseillé avant de relire (si pending).
responses:
Unauthorized:
description: Clé API absente ou invalide.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Unauthorized
NotFound:
description: Ressource introuvable.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
success: false
error: Not found
WalletInsufficient:
description: 'Solde d''enrichissement insuffisant : rien n''est lancé.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
success: false
error: wallet_insufficient
required_cents: 55
balance_cents: 20
missing_cents: 35
currency: eur
topup_url: https://app.basile.cc/settings/enrichment
EnrichmentSafetyCap:
description: Plafond de sécurité (50 000 recherches en file par compte, ou 50 requêtes/s). Attendre Retry-After.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
success: false
error: enrichment_queue_full
scope: queue
limit: 50000
retry_after_seconds: 60
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: Authorization
description: 'Clé API en clair (sans `Bearer`). Ex : `Authorization: sk_live_xxxx`.'