Guides / Due diligence

Due diligence immobilière

Vérifiez l'historique complet d'un bien avant acquisition.

Avant d'acquérir un bien, il est essentiel de vérifier son historique de transactions : fréquence des reventes, variations de prix, ventes en lot. Ces données, disponibles dans DVF depuis 2014, permettent de détecter des signaux d'alerte avant de s'engager.

🔄

Revente rapide

Un bien revendu en moins de 6 mois peut indiquer un problème caché (vices, litige de copropriété).

📈

Hausse de prix anormale

+30% en moins de 2 ans sans rénovation connue mérite investigation.

📦

Vente en lot

is_bulk_sale=true : le prix affiché est le total du lot, pas la valeur individuelle.

📉

Baisse de prix

Une baisse significative peut signaler des travaux importants ou un marché en difficulté.

Récupérer l'historique

L'outil MCP get_property_history (20 crédits) retourne toutes les transactions DVF pour une adresse et un code postal. Données disponibles depuis 2014.

import requests
from datetime import datetime

API_KEY = "normi_votre_token"
BASE_URL = "https://mcp.normi.fr/v1"

# Rechercher l'historique d'un bien par adresse
def get_property_history(address: str, code_postal: str):
    # Utiliser l'API MCP via tool call, ou search_properties avec adresse exacte
    r = requests.get(
        f"{BASE_URL}/transactions",
        params={
            "commune": address.split(",")[-1].strip().upper() if "," in address else None,
            "code_postal": code_postal,
            "limit": 50,
        },
        headers={"X-API-Key": API_KEY},
    )
    r.raise_for_status()
    return r.json()

# Pour un historique précis par adresse, utiliser le MCP via Claude
# ou l'outil get_property_history directement
Outil MCP recommandé
Pour une recherche par adresse précise, utilisez get_property_history via Claude — l'outil normalise automatiquement l'adresse et retourne uniquement les transactions pertinentes.

Analyser les signaux d'alerte

Détectez automatiquement les reventes rapides, hausses anormales et baisses de prix.

def analyze_history(transactions: list) -> dict:
    """Analyse l'historique pour détecter des signaux d'alerte."""
    if len(transactions) < 2:
        return {"status": "ok", "transactions": len(transactions), "alerts": []}

    alerts = []
    transactions_sorted = sorted(transactions, key=lambda t: t["date_mutation"])

    for i in range(1, len(transactions_sorted)):
        prev = transactions_sorted[i - 1]
        curr = transactions_sorted[i]

        # Calculer la durée entre deux ventes
        d1 = datetime.fromisoformat(prev["date_mutation"])
        d2 = datetime.fromisoformat(curr["date_mutation"])
        months_between = (d2 - d1).days / 30.44

        # Calculer la variation de prix
        if prev["valeur_fonciere"] > 0:
            price_change_pct = (
                (curr["valeur_fonciere"] - prev["valeur_fonciere"])
                / prev["valeur_fonciere"]
                * 100
            )
        else:
            price_change_pct = 0

        # Signaux d'alerte
        if months_between < 6:
            alerts.append({
                "type": "revente_rapide",
                "severity": "high",
                "message": f"Revendu en {months_between:.1f} mois",
                "from": prev["valeur_fonciere"],
                "to": curr["valeur_fonciere"],
                "change_pct": price_change_pct,
            })
        elif price_change_pct > 30 and months_between < 24:
            alerts.append({
                "type": "hausse_rapide",
                "severity": "medium",
                "message": f"+{price_change_pct:.1f}% en {months_between:.1f} mois",
                "from": prev["valeur_fonciere"],
                "to": curr["valeur_fonciere"],
                "change_pct": price_change_pct,
            })
        elif price_change_pct < -20:
            alerts.append({
                "type": "baisse_prix",
                "severity": "low",
                "message": f"{price_change_pct:.1f}% de baisse",
                "from": prev["valeur_fonciere"],
                "to": curr["valeur_fonciere"],
                "change_pct": price_change_pct,
            })

    return {
        "status": "alerts" if alerts else "ok",
        "transactions": len(transactions),
        "alerts": alerts,
    }

# Exemple d'utilisation
result = analyze_history(transactions)
print(f"Statut : {result['status']}")
for alert in result["alerts"]:
    print(f"  [{alert['severity'].upper()}] {alert['type']}: {alert['message']}")
    print(f"    {alert['from']:,} € → {alert['to']:,} €")

Ventes en lot

Les ventes en lot concernent plusieurs biens dans une seule transaction (successions, opérations immobilières groupées). Le champ is_bulk_sale permet de les identifier.

# Détecter si un bien fait partie d'une vente en lot
def check_bulk_sale(transaction: dict) -> bool:
    """
    is_bulk_sale=True indique que la transaction incluait plusieurs biens.
    Dans ce cas, valeur_fonciere est le prix TOTAL du lot, pas du bien seul.
    """
    return transaction.get("is_bulk_sale", False)

# Pour les ventes en lot, le prix est à diviser par le nombre de lots
# (non disponible directement — utiliser avec précaution)
if check_bulk_sale(transaction):
    print("⚠ Vente en lot — le prix affiché est le total du lot")
    print("  La valeur individuelle du bien peut différer significativement")
Prix en lot = prix total
Pour une vente en lot, valeur_fonciere représente le prix total de l'ensemble du lot, pas du bien individuel. Ces transactions sont exclues par défaut (exclude_bulk_sales=true) des stats et comparables pour éviter les distorsions.

Limites des données DVF

Transactions depuis 2014 uniquement
Les transactions antérieures à 2014 ne sont pas disponibles dans DVF. Pour les biens plus anciens, d'autres sources (SAFER, notaires) peuvent être nécessaires.
Délai de publication semi-annuel
DVF est mis à jour deux fois par an. Les ventes des 6 derniers mois peuvent être absentes de la base.
Pas de motif de vente
DVF ne contient pas les raisons d'une vente (succession, divorce, saisie) — uniquement les données factuelles de la transaction.
Adresses parfois imprécises
Certaines adresses DVF manquent de numéro de rue ou contiennent des abréviations. Le géocodage peut être approximatif dans ces cas.

Avec Claude (MCP)

Claude peut effectuer toute la due diligence en une instruction :

"Pour le bien situé au 42 rue de Rivoli, Paris 1er (75001) : 1. Montre-moi tout l'historique des transactions DVF disponible 2. Identifie les éventuelles reventes rapides (moins de 2 ans entre deux ventes) 3. Calcule la variation de prix entre chaque transaction 4. Signale si une transaction est une vente en lot 5. Compare le dernier prix de vente avec le marché actuel pour cette zone"