JB Product
← Retour

De 0 à 500 prospects BTP en 1 clic : comment on a branché l'API SIRENE dans notre CRM

Publié le 5 février 2026·4 min de lecture·Read in English

Partager :X / TwitterLinkedIn

Le problème

Notre CRM ezlead est conçu pour les commerciaux terrain. Ils gèrent leurs prospects, leurs relances, leurs leads. Mais il fallait encore leur donner les prospects.

Manuellement, ça veut dire des heures sur Pages Jaunes, des exports Excel, des copier-coller. Pas terrible pour une équipe qui veut scaler.

L'idée : automatiser l'import d'entreprises depuis les registres officiels, filtré par département et secteur d'activité. Résultat : une interface où l'utilisateur choisit "BTP, département 06", clique, et retrouve 500 fiches prospect dans son CRM.

L'architecture : pourquoi deux systèmes séparés

On aurait pu tout faire dans Next.js. On ne l'a pas fait.

Le problème avec le "tout-en-un" : l'API SIRENE retourne parfois des centaines de résultats par code NAF. Avec pagination, enrichissement, déduplication, une seule recherche peut prendre 30 à 60 secondes. Un Server Action Next.js avec timeout à 60s, c'est risqué et l'UX est mauvaise.

Notre architecture :

  • Next.js (interface) : l'utilisateur configure sa recherche (départements, codes NAF, taille d'entreprise) et clique "Lancer". Ça crée juste un job en base avec le statut pending. L'interface se rafraîchit toutes les 8 secondes.
  • Script Python (traitement) : un script local (cron) lit les jobs pending, interroge SIRENE, et insère les entreprises directement en base via l'API REST Supabase.

Ce découplage permet de relancer le script sans toucher à l'interface, de gérer les erreurs proprement, et de faire tourner le traitement depuis n'importe quelle machine.

L'API SIRENE

L'API Sirene V3 utilise une clé API directe dans le header X-INSEE-Api-Key-Integration. Pas d'OAuth2, pas de token à rafraîchir.

headers = {
    "X-INSEE-Api-Key-Integration": API_KEY,
    "Accept": "application/json"
}

Simple et stable — c'est agréable.

La requête Lucene : itérations

L'API SIRENE utilise des requêtes Lucene pour le filtrage multicritère. Simple en théorie. En pratique, on a eu plusieurs surprises.

Ce qui ne marche pas :

  • activitePrincipaleEtablissement:4120A → 400 (mauvais champ)
  • codePostalEtablissement:06* → 400 (wildcard non supporté)
  • La notation pointée dans les champs : uniteLegale.denominationUniteLegale → 400

Ce qui marche :

  • activitePrincipaleUniteLegale:41.20A (champ UniteLegale, avec le point dans le code NAF)
  • codePostalEtablissement:[06000 TO 06999] (range query)
  • etatAdministratifUniteLegale:A (champ UniteLegale)

La requête finale pour "maçons actifs dans les Alpes-Maritimes" :

activitePrincipaleUniteLegale:43.99C
AND etatAdministratifUniteLegale:A
AND codePostalEtablissement:[06000 TO 06999]

Le problème des doublons

Première vraie run : 500 résultats pour "maçonnerie, 06". Sauf que dans la liste, le même artisan apparaissait 3 fois.

En France, un artisan peut avoir plusieurs SIRET sous le même SIREN (numéro d'entreprise). Il crée un établissement, le ferme, en crée un nouveau. L'API filtre bien les entreprises actives (etatAdministratifUniteLegale:A) mais retourne tous leurs établissements, y compris les fermés.

Deux corrections :

1. Vérifier la période courante de l'établissement. Chaque établissement a un historique de périodes. La période courante (celle avec dateFin = null) doit avoir etatAdministratifEtablissement = A.

def etab_actif(etab):
    for p in etab.get("periodesEtablissement", []):
        if p.get("dateFin") is None:
            return p.get("etatAdministratifEtablissement") == "A"
    return False

2. Dédupliquer par SIREN. On garde un seul établissement par entreprise légale, en priorisant le siège (etablissementSiege: true).

L'enrichissement : les limites des données officielles

SIRENE donne le nom, l'adresse, le SIRET, la taille d'entreprise. Pas de téléphone, pas de site web. C'est un registre légal, pas un annuaire.

Pipeline d'enrichissement :

  • Pappers.fr (100 crédits offerts automatiquement à la création d'un compte API avec une adresse email professionnelle) : requête par SIRET, retourne parfois téléphone et site web depuis les données INPI.
  • Google Places API (optionnel, payant) : recherche par nom + ville, retourne les données de la fiche Google Maps.

Résultat honnête : pour les PME et ETI, l'enrichissement fonctionne bien. Pour les micro-artisans — la majorité du BTP — les données de contact sont souvent absentes des registres officiels. On enrichit environ 20 à 30 % des fiches.

Ce qu'on a livré

Interface :

ezlead seed

  • Sélection multi-départements (101 départements)
  • 80+ codes NAF organisés par secteur
  • Filtre par catégorie d'entreprise (PME / ETI / GE)
  • Filtre par tranche d'effectifs (16 tranches)
  • Historique des jobs avec statut temps réel (polling auto)
  • Bouton "Ré-enrichir" pour relancer l'enrichissement sur des fiches existantes

Script Python :

  • Lit les jobs en attente depuis Supabase
  • Appelle SIRENE avec pagination profonde (curseur)
  • Post-filtre les établissements fermés
  • Déduplique par SIREN
  • Enrichit via Pappers
  • Insère en batch de 100 avec détection des doublons par SIRET

Résultat : ~500 fiches BTP qualifiées dans le 06 en moins de 2 minutes.

C'est la différence entre un commercial qui commence sa journée avec une liste prête, et un commercial qui passe sa matinée à la construire.

Commentaires