← Retour au site
Étude de cas

Pipeline de prospection B2B : quand l'IA révèle l'absurdité du copier-coller

Comment on a transformé 7 heures de travail répétitif en 47 minutes d'exécution automatique. Pas pour remplacer l'humain, mais pour lui rendre son temps de réflexion.

⚠️ Note : ce case study est technique

On va te montrer du code, des APIs, des systèmes. Mais on explique tout en langage clair. Si tu comprends "copier-coller dans Excel", tu vas comprendre ce qu'on a construit ici. Chaque concept technique a son explication. Promis.

47min
Temps machine
7h
Temps humain libéré
200
Prospects qualifiés

Le contexte : l'automatisation comme miroir

Chercher des prospects B2B à la main, c'est un rituel absurde : 7 heures pour trouver 100 entreprises, copier-coller leurs infos dans un tableur, vérifier manuellement si les emails sont valides, chercher les profils LinkedIn un par un.

Ce que ça révèle : Cette tâche n'a jamais été du travail intellectuel. C'est de la répétition pure. Une logique figée qu'un humain exécute en mode automatique.

La vraie question : Pourquoi un humain passe-t-il 7 heures à faire ce qu'une machine peut faire en 47 minutes ?

L'automatisation révèle l'absurdité

Quand on automatise, on voit enfin ce qui, dans notre travail, n'était plus vraiment du travail. Juste de la répétition. L'IA ne fait qu'amplifier la logique qu'on lui donne : si tu lui donnes du chaos, elle reproduit le chaos. Si tu lui donnes de la clarté, elle libère du temps.

Le problème concret

  • 7h de copier-coller pour collecter 100 prospects (nom, entreprise, secteur, contacts)
  • 50% d'emails faux ou obsolètes parce qu'on ne vérifie rien
  • Mental saturé : impossible de réfléchir stratégiquement après 7h de répétition
  • Aucune valeur créée : juste du transport de données

On voulait construire un système qui fait ce travail à notre place, automatiquement, en respectant les limites des outils et la législation (RGPD).

Mais avant de coder quoi que ce soit, on observe. Toujours. Parce qu'automatiser un process qu'on ne comprend pas, c'est juste reproduire le chaos plus vite.

Observer le réel

Pendant 3 jours, on a suivi le process manuel de A à Z. Pas pour juger, mais pour comprendre : où ça bloque, ce qui se répète, comment circule l'information.

Ce qu'on a observé, chrono en main

On a suivi la collecte manuelle de 10 prospects pour comprendre le temps réel passé sur chaque étape :

  • 15 min sur LinkedIn : Chercher des entreprises par secteur, vérifier les profils
  • 12 min sur Google : Trouver les sites web, vérifier qu'ils sont actifs
  • 8 min sur Excel : Copier-coller nom, secteur, ville, URL dans le tableur
  • 10 min à deviner les emails : Tester prénom.nom@, contact@, info@... sans certitude
  • 5 min de vérification : Retours sur LinkedIn pour compléter les infos manquantes

Total : 50 minutes pour 10 prospects. Extrapolé sur 100 prospects, ça fait 8h+ de travail répétitif.

Les patterns identifiés

Le constat : 90% de ce process est de la logique pure. Pas de créativité, pas de décision stratégique. Juste des règles répétées en boucle :

  • Chercher "agence de communication Paris" → Toujours la même requête, juste les mots qui changent
  • Copier nom entreprise → Coller dans Excel colonne B → Répété 100 fois
  • Deviner format email → Tester 3-4 variantes → Sans jamais être sûr
  • Vérifier site web → Cliquer → Regarder 10 secondes → Revenir

Si un humain peut décrire une tâche en 3 étapes claires et répétables, une machine peut la faire. C'est exactement ce qu'on a vu ici.

Terrain > théorie

On n'automatise jamais un process qu'on ne comprend pas. Observer le réel, c'est identifier ce qui se répète, ce qui bloque, ce qui pourrait tourner tout seul. Clarifier avant d'optimiser.

Cette observation nous a révélé 4 étapes clés à automatiser : recherche d'entreprises, enrichissement social, vérification des contacts, export structuré.

Cartographier le système : du chaos à la clarté

Maintenant qu'on a observé le réel, on peut cartographier. Rendre visible ce qui était invisible. Structurer ce qui était chaotique.

Le parcours des données visualisé :

Critères
Secteur, localisation, taille
Pappers
Données officielles entreprises
Apify
Profils Instagram
Dropcontact
Emails vérifiés
IA Claude
Scoring & qualification
Export CSV
200 prospects prêts

Une fois la carte établie, on peut expliquer les outils qu'on utilise.

La stack technique : lisible, utile, élégante

On ne code pas pour impressionner, on code pour simplifier. Voici les outils qu'on a utilisés et pourquoi on les a choisis eux plutôt que d'autres.

C'est quoi une API ?

Une API, c'est comme une prise électrique : tu te branches dessus pour accéder aux données d'un service sans tout recoder toi-même. Pappers expose une API qui te donne les infos des entreprises françaises directement. Tu envoies une requête, tu reçois du JSON.

Python + Pandas
Orchestration du pipeline. Simple, lisible, efficace. Pandas pour manipuler les données comme des tableurs Excel. On ne code pas pour faire du code, on code pour simplifier.
Pappers API
Pappers > Apollo : Apollo a de bonnes données internationales, mais sur les entreprises françaises c'était moyen (50% de complétude). Pappers est connecté aux données officielles de l'INSEE, donc fiabilité à 95%+. On a switché après 2 jours de tests.
Dropcontact
Dropcontact > Hunter : Hunter est moins cher (0.01€/email vs 0.05€), mais le taux de vérification est à 70% contre 90%+ pour Dropcontact. On préfère payer plus et avoir des emails qui marchent vraiment. Moins de bounces = meilleure délivrabilité.
Apify
Apify pour Instagram : On voulait savoir si les entreprises sont actives sur les réseaux. Apify permet de scraper Instagram proprement (respect des rate limits) et d'extraire followers, posts récents, bio. Utile pour qualifier la maturité digitale.
Google Maps API
Google Maps API : Gratuit jusqu'à 28 000 requêtes/mois. Parfait pour les commerces locaux (restaurants, boutiques) qui n'ont pas forcément de présence LinkedIn mais sont sur Google. On complète Pappers avec ça.
Claude API
Claude pour la qualification : L'IA analyse et score chaque prospect selon des critères métier. Elle ne crée rien d'humain, elle prolonge ta logique. On ne lui demande pas de créer, juste de qualifier selon les règles qu'on lui donne.
Simplicité > sophistication

Chaque outil a été choisi pour une raison précise. Pas parce que c'est trendy, mais parce que ça résout un problème réel. On n'a pas choisi au hasard. On a testé, comparé, mesuré. Et on continue d'ajuster selon ce qu'on apprend.

Maintenant qu'on connaît les outils, voyons comment on les connecte ensemble.

Construire le système : 3 piliers

Une fois l'architecture clarifiée, on construit. Trois piliers : le pipeline (orchestration), la résilience (gestion des crashs), l'enrichissement intelligent (respect des APIs).

1. Le pipeline : chef d'orchestre invisible

Le pipeline, c'est le chef d'orchestre. Il coordonne tous les outils, gère le flux de données, assure que chaque étape se fait dans le bon ordre. Voici le code qui orchestre tout ça :

C'est quoi un pipeline ?

Un pipeline, c'est une suite d'étapes automatiques. Comme une chaîne de production : chaque étape transforme les données et les passe à l'étape suivante. Ici, ça part d'une liste d'entreprises et ça finit par un fichier de prospects qualifiés, sans intervention humaine.

Voir le code complet — pipeline.py
pipeline.py
def run_pipeline(output_csv: str = "prospects_unifies.csv") -> pd.DataFrame:
    """Fonction principale qui orchestre la collecte multi-segments"""
    logger.info(f"=== LANCEMENT PIPELINE (Target: {TARGET_TOTAL}) ===")

    try:
        unified_results: List[UnifiedProspect] = []
        completed_segments = []

        # Reprise automatique si crash détecté
        if _checkpoint_manager.has_checkpoint():
            logger.warning("⚠️ Checkpoint détecté - reprise en cours...")
            checkpoint_data = _checkpoint_manager.load_last_checkpoint()
            if checkpoint_data:
                unified_results = checkpoint_data.get("prospects", [])
                completed_segments = checkpoint_data.get("completed_segments", [])

        # Collecte par segment (PME, Studios, Locales)
        for segment_name, collector in SEGMENT_COLLECTORS:
            if segment_name in completed_segments:
                logger.info(f"⏭️ Segment {segment_name} déjà terminé")
                continue

            target = TARGETS_CONFIG.get(segment_name, 0)
            if target > 0:
                segment_prospects = collector(target)
                unified_results.extend(segment_prospects)
                completed_segments.append(segment_name)

                # Checkpoint après chaque segment (sécurité)
                _checkpoint_manager.save_checkpoint(
                    prospects=unified_results,
                    segment_name=segment_name
                )

        # Déduplication intelligente
        df = unify_dicts(unified_results)
        df = df.drop_duplicates(subset=["email", "linkedin", "company_name"])

        # Export CSV final
        df.to_csv(output_csv, index=False, encoding="utf-8")
        logger.info(f"💾 CSV généré → {output_csv} ({len(df)} lignes)")

        _checkpoint_manager.clear_checkpoints()
        return df

    except Exception as e:
        logger.error(f"❌ Erreur pipeline: {e}")
        logger.error("💡 Les checkpoints conservés - relancez pour reprendre")
        raise

Ce que ce code fait en langage clair :

  • Il lance la collecte sur chaque segment (PME, Studios, Locales) l'un après l'autre
  • Après chaque segment collecté, il sauvegarde l'état (checkpoint) au cas où ça plante
  • Il vérifie s'il y a un checkpoint existant au démarrage : si oui, il reprend là où ça s'était arrêté
  • À la fin, il déduplique les prospects (pas de doublons) et exporte tout dans un CSV propre

En image : ce qui se passe quand tu lances le script à 9h00

Tu tapes python pipeline.py dans ton terminal avec les critères "Agences de communication, Paris, 10-50 employés". Voici ce qui se passe en temps réel :

Voir l'exécution complète — Terminal
Terminal — python pipeline.py
# 9h00:00 - Démarrage
=== LANCEMENT PIPELINE (Target total: 200) ===
Répartition: {'pme': 200, 'studios': 0, 'locales': 0}

# 9h00:15 - Pappers trouve les entreprises
🏢 [PME] Collecte de 200 prospects via Pappers + Dropcontact
📊 Pappers: 47 entreprises trouvées pour "agence de communication"
📊 Pappers: 38 entreprises pour "marketing digital"
📊 Pappers: 52 entreprises pour "conseil en communication"
✅ Total Pappers: 137 entreprises collectées

# 9h12:30 - Apify scrape Instagram
📸 [Instagram] Scraping de 137 profils...
✅ 89 profils Instagram actifs récupérés

# 9h25:45 - Dropcontact enrichit les emails
🔍 [DROPCONTACT] Batch 1/14 envoyé (10 prospects)
⏳ Tentative 1/10 - Attente 10s (total: 0s)
⏳ Tentative 2/10 - Attente 15s (total: 10s)
✅ [DROPCONTACT] Prêt en 25s!
📧 Emails vérifiés: 8/10 valides (80%)

# 9h42:20 - Qualification IA
🤖 [IA] Claude analyse et score les prospects...
✅ 200 prospects qualifiés et scorés

# 9h47:00 - Export final
🔄 Déduplication: 12 doublons supprimés
💾 CSV généré → prospects_unifies.csv (188 lignes)
✅ Pipeline terminé avec succès

Résultat : En 47 minutes, tu as un fichier CSV avec 188 prospects qualifiés, emails vérifiés, profils Instagram, scoring IA. Pendant ce temps, tu as pu faire autre chose : réfléchir à ta stratégie, préparer tes approches, boire un café.

⏱️ Timeline d'exécution : 47 minutes

De la requête initiale au CSV final

0min
Démarrage
15min
Pappers collecte
27min
Apify scraping
42min
Dropcontact enrichit
47min
Export CSV ✓

2. La résilience : construire pour la durée

Le système peut crasher. C'est normal. Une API qui plante, un quota épuisé, une coupure réseau : ça arrive. L'important, c'est de savoir reprendre.

Le système de checkpoint : Après chaque segment collecté (PME, Studios, Locales), on sauvegarde l'état complet. Si ça plante au milieu du segment 2, on ne recommence pas tout : on reprend là où on s'est arrêté.

Voir le code — checkpoint_manager.py
checkpoint_manager.py
def save_checkpoint(self, prospects: List, segment_name: str) -> None:
    """Sauvegarde l'état complet après chaque segment"""
    checkpoint_file = os.path.join(
        self.checkpoint_dir,
        f"checkpoint_{segment_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    )

    checkpoint_data = {
        "timestamp": datetime.now().isoformat(),
        "segment": segment_name,
        "prospects_count": len(prospects),
        "prospects": prospects,
        "progress_info": {
            "completed_segments": completed_segments
        }
    }

    with open(checkpoint_file, 'w', encoding='utf-8') as f:
        json.dump(checkpoint_data, f, ensure_ascii=False, indent=2)

    self.logger.info(f"💾 Checkpoint sauvegardé: {len(prospects)} prospects")

Ce que ce code fait en langage clair :

  • À chaque fin de segment (PME, Studios, Locales), il sauvegarde tout dans un fichier JSON avec timestamp
  • Ce fichier contient : tous les prospects collectés jusqu'ici + la liste des segments terminés
  • Si le système crash, au redémarrage il charge ce fichier et reprend au segment suivant
  • Résultat : on ne perd jamais plus d'un segment de travail, même en cas de crash

🛡️ Système de reprise automatique

En cas de crash, on reprend là où on s'est arrêté

Segment 1
Sauvegardé
Segment 2
Sauvegardé
Segment 3
Crash !
Redémarrage
Reprend au Segment 3

Philosophie : L'IA amplifie ce qu'on lui donne. Si on lui donne de la fragilité, elle plante. Si on lui donne de la résilience, elle tient.

3. L'enrichissement intelligent : respecter les outils

Dropcontact vérifie et enrichit les emails. Mais leur API utilise un système de polling : tu envoies une requête, tu attends qu'elle soit traitée, tu récupères le résultat.

C'est quoi le polling ?

Le polling, c'est comme attendre un colis : tu envoies ta commande, puis tu reviens régulièrement voir si c'est prêt. L'API Dropcontact ne te répond pas immédiatement : elle te donne un numéro de suivi, et tu dois revenir vérifier si le traitement est terminé.

Le problème : Si tu demandes trop vite ("C'est prêt ? C'est prêt ? C'est prêt ?"), tu surcharges l'API et elle te bloque. Si tu attends trop longtemps, tu perds du temps.

La solution : Exponential backoff. On commence par attendre 10 secondes, puis 15s, 20s, 30s... jusqu'à 60s max. On s'adapte progressivement à la vitesse de traitement de l'API.

Voir le code — dropcontact_enrich.py
dropcontact_enrich.py
def dropcontact_enrich_batch_polling(batch_prospects: List[Dict]) -> List[Dict]:
    """Enrichissement avec polling et exponential backoff"""

    # Préparer le batch pour Dropcontact
    dropcontact_batch = [
        {
            "first_name": p.get("prenom", ""),
            "last_name": p.get("nom", ""),
            "company": p.get("entreprise_nom", ""),
            "num_siren": p.get("siren", ""),
        }
        for p in batch_prospects
    ]

    # Envoyer la requête initiale
    resp = requests.post(
        "https://api.dropcontact.com/v1/enrich/all",
        headers=dropcontact_headers,
        json={"data": dropcontact_batch, "siren": True, "language": "fr"}
    )

    request_id = resp.json().get("request_id")

    # Exponential backoff: 10s, 15s, 20s, 30s, 45s, 60s...
    delays = [10, 15, 20, 30, 30, 45, 45, 60, 60, 60]
    total_time = 0

    for attempt, delay in enumerate(delays, 1):
        logger.info(f"⏳ Tentative {attempt} - Attente {delay}s (total: {total_time}s)")
        time.sleep(delay)
        total_time += delay

        # Vérifier si le résultat est prêt
        result_url = f"https://api.dropcontact.com/v1/enrich/all/{request_id}"
        result_resp = requests.get(result_url, headers=dropcontact_headers)

        if result_resp.status_code == 200:
            result_data = result_resp.json()
            if result_data.get("success"):
                logger.info(f"✅ Enrichissement terminé en {total_time}s")
                return result_data.get("data", [])

    return []

Ce que ce code fait en langage clair :

  • On envoie une requête avec 10 prospects à Dropcontact → on reçoit un numéro de suivi (request_id)
  • On attend 10 secondes, puis on vérifie si c'est prêt. Pas prêt ? On attend 15s. Toujours pas ? 20s. Puis 30s...
  • On augmente progressivement l'attente jusqu'à 60s max, pour ne pas spammer l'API
  • Dès que l'API répond "success: true", on récupère les données enrichies et on passe au batch suivant

⏳ Exponential Backoff : attente progressive

On augmente progressivement le temps d'attente pour ne pas surcharger l'API

0s 15s 30s 45s 60s T1 T2 T3 T4 T5 T6

T = tentative (ex. T3 = 3ᵉ relance)

Respecter les limites des outils

On ne force pas les APIs. On s'adapte. Le rate limiting, l'exponential backoff, les retries : c'est du respect pour les outils qu'on utilise. Une bonne automation, c'est celle qui dure, pas celle qui impressionne pendant 5 minutes avant de planter.

Et après ? Le document final

Une fois toutes ces étapes terminées (collecte, enrichissement, qualification), on se retrouve avec un document structuré. Voici à quoi ressemble le fichier CSV final :

prospects_unifies.csv
# Entreprise Secteur Email LinkedIn Score IA Instagram
1 Agence Horizon Communication contact@agence-horizon.fr /company/agence-horizon 92 @agencehorizon
2 Studio Pixel Design Graphique hello@studiopixel.com /company/studio-pixel 88 @studiopixel
3 Digital Makers Marketing Digital contact@digitalmakers.fr /company/digital-makers 76 @digitalmakers
4 Creative Lab Paris Agence Créative info@creativelab-paris.fr /company/creativelab-paris 94 @creativelabparis
5 BrandForge Branding team@brandforge.io /company/brandforge 71 @brandforge_io
6 Lumière Studio Production Vidéo production@lumiere-studio.fr /company/lumiere-studio 85 @lumierestudio

188 lignes de prospects qualifiés, prêts à être contactés. Chaque ligne contient tout ce dont tu as besoin pour démarrer une vraie conversation : email vérifié, profil LinkedIn, score de qualification. Pas de doublons, pas d'emails pourris, pas de vérifications manuelles à faire.

Ce qui change vraiment : l'humain libéré

❌ Avant
7h de copier-coller par session de prospection
Mental saturé : impossible de réfléchir stratégiquement après ça
50% d'emails faux parce qu'on ne vérifie rien
Process différent selon qui le fait (inconsistance)
Aucune valeur créée : juste du transport de données
✓ Après
47 min d'exécution machine pendant que l'humain fait autre chose
Temps humain retrouvé pour analyser, créer, décider
Emails vérifiés à 90%+ grâce à Dropcontact
Process identique à chaque exécution (fiabilité)
Valeur créée : l'humain se concentre sur la stratégie
47min
Temps machine
7h
Temps humain libéré
200
Prospects qualifiés
Le but : libérer le temps humain

On n'automatise pas pour remplacer les gens. On automatise pour leur rendre leur capacité à penser, créer, décider. Une bonne automation se fait oublier : elle tourne en silence pendant que tu te concentres sur ce qui compte vraiment.

Ce qu'on a appris : transparence totale

1. L'architecture modulaire est critique

Chaque segment (PME, Studios, Locales) est indépendant. Si un segment plante, les autres continuent. On isole les erreurs, on ne les laisse pas contaminer tout le système.

2. Le cache = respect des ressources

On a implémenté un cache persistant de 90 jours pour les données Apify. Pourquoi repayer pour scraper les mêmes profils Instagram ? On garde en mémoire, on économise les crédits API, on respecte les outils qu'on utilise.

3. La gestion d'erreurs > le code parfait

Le système peut crasher. C'est normal. L'important, c'est qu'il sache reprendre là où il s'est arrêté. Les checkpoints, les logs détaillés, les retry logic : c'est du respect pour l'utilisateur.

Voir le code — error_handling.py
error_handling.py
try:
    # Tentative d'enrichissement Dropcontact
    enriched_data = dropcontact_enrich_batch(prospects)
except Exception as e:
    # Logger l'erreur mais continuer le process
    logger.error(f"❌ Erreur Dropcontact: {e}")

    # Sauvegarder les prospects non enrichis quand même
    save_prospects_csv_common(prospects, "prospects_partiels.csv")

    # Le pipeline continue avec les autres segments
    pass

4. Observer avant de coder, toujours

On a passé plusieurs jours à regarder le process manuel avant d'écrire une ligne de code. Cette phase d'observation nous a évité de construire un système qui ne correspond pas au réel. Terrain > théorie.

Transparence > perfection

On te montre les erreurs, les choix, le réel. Pas de bullshit marketing. Le code n'est pas parfait, le système peut crasher. Mais il est résilient, et il libère du temps humain. C'est ça qui compte.

bigxbang

Human Intelligence

On automatise les tâches répétitives pour libérer ce qui fait de toi un humain : penser, créer, décider.