Aller au contenu principal

Recherche avancée de parcelles avec scoring

L'API Infoparcelle propose un système de scoring intelligent pour rechercher les parcelles les plus pertinentes selon vos critères. Cette recherche avancée utilise la méthode POST sur le même endpoint que la recherche classique, mais offre des capacités beaucoup plus puissantes.

Vue d'ensemble

L'endpoint POST /api/v1/parcelles/recherche permet de combiner :

  • 🎯 Critères stricts : Contraintes obligatoires (filtres WHERE SQL)
  • Critères indicatifs : Préférences qui influencent le score (système de ranking)
  • 📊 Score de pertinence : Classement de 0 à 100 pour chaque parcelle

Avantages du scoring

  • Résultats plus pertinents : Les meilleures correspondances en premier
  • Flexibilité : Combinez contraintes obligatoires et préférences
  • Intelligence : Le système calcule automatiquement la pertinence
  • Personnalisation : Ajustez les poids en fonction de vos besoins

Différence entre mode strict et indicatif

Mode STRICT (indicatif: false)

Filtre WHERE SQL classique. Seules les parcelles correspondant exactement au critère sont retournées.

{
"annee_construction": {
"min": 1990,
"max": 2020,
"indicatif": false // ❌ STRICT : Exclut toute parcelle hors 1990-2020
}
}

Mode INDICATIF (indicatif: true)

Système de scoring. Les parcelles sont classées par pertinence selon leur correspondance au critère.

{
"annee_construction": {
"min": 1990,
"max": 2020,
"indicatif": true // ⭐ SCORING : Favorise 1990-2020, mais inclut les autres
}
}

Résultat : Une parcelle de 2018 aura un score élevé, une de 1985 ou 2025 aura un score plus faible, mais sera quand même retournée.

Recherche basique avec scoring

curl -X POST \
"https://app.infoparcelle.fr/api/v1/parcelles/recherche" \
-H "Authorization: Bearer VOTRE_CLE_API" \
-H "Content-Type: application/json" \
-d '{
"filtres": {
"code_postal": {
"value": "75008"
},
"superficie": {
"min": 300,
"max": 800,
"indicatif": true
},
"dpe_energie": {
"value": "C,D",
"indicatif": true
}
},
"pagination": {
"limite": 20
},
"options": {
"champ_tri": "score_pertinence",
"ordre_tri": "desc",
"champs": ["cadastre_id", "superficie", "score_pertinence"]
}
}'

Structure de la requête POST

1. Section filtres

Tous vos critères de recherche avec mode strict ou indicatif.

{
"filtres": {
"code_municipalite": {
"value": "75056"
// Filtre géographique (STRICT uniquement, pas de champ indicatif)
},
"superficie": {
"min": 300,
"max": 800,
"indicatif": true // Mode scoring
}
}
}

2. Section pagination

{
"pagination": {
"limite": 20, // Nombre de résultats (max 50)
"curseur": "" // Curseur de pagination
}
}

3. Section options

{
"options": {
"format": "json", // json ou geojson
"geometrie": "centre", // centre, contour, bbox
"champs": [ // Champs à retourner
"cadastre_id",
"superficie",
"score_pertinence" // ⭐ Score de pertinence
],
"champ_tri": "score_pertinence", // Trier par score
"ordre_tri": "desc" // Descendant (meilleurs en premier)
}
}

Types de filtres avec scoring

Filtres de plage (FiltreRange)

Pour les valeurs min/max :

{
superficie: {
min: 300,
max: 800,
indicatif: true // Mode scoring
},
annee_construction: {
min: 1990,
max: 2020,
indicatif: false // Mode strict
}
}

Champs supportés :

  • superficie, jardin, emprise_sol, surface_habitable
  • altitude, hauteur
  • annee_construction
  • piscine_surface
  • dpe_date_reception, dpe_date_etablissement
  • elec_tertiaire, elec_residentielle, gaz_tertiaire, gaz_residentielle
  • dvf_prix, dvf_date
  • estimation, estimation_ppm

Filtres à valeurs multiples (FiltreSimpleMultiple)

Pour sélectionner plusieurs valeurs :

{
dpe_energie: {
value: 'A,B,C', // Plusieurs étiquettes
indicatif: true // Mode scoring
},
bati_type: {
value: '1,3,5', // Plusieurs types de bâti
indicatif: false // Mode strict
}
}

Champs supportés :

  • bati_type (0-17)
  • dpe_energie, dpe_ges (A-G)
  • etages, mitoyen, batiment_dur, batiment_leger
  • forme_piscine (0-4)
  • dvf_code_type_bien
  • permis_type, permis_etat, permis_nature, permis_cat_dem
  • plu_zone, plu_zone_type
  • risque_argile, risque_radon (1-3)

Filtres booléens (FiltreBoolean)

Pour les valeurs true/false :

{
arpente: {
value: true,
indicatif: true // Mode scoring
},
piscine: {
value: true,
indicatif: false // Mode strict
}
}

Champs supportés :

  • arpente, isole, piscine, construction
  • personne_morale_presente, opportunite

Filtres stricts uniquement

Certains filtres ne supportent QUE le mode strict (pas de scoring) :

{
code_postal: {
value: '75008'
// Pas de champ "indicatif" - toujours strict
},
dvf_existe: {
value: true
// Pas de champ "indicatif" - toujours strict
}
}

Filtres géographiques (stricts uniquement) :

  • lat, lon, code_departement, code_municipalite, code_postal, code_iris, code_section

Cas d'usage avancés

1. Recherche de maison idéale avec scoring

async function rechercherMaisonIdeale() {
/**
* Rechercher une maison avec critères obligatoires et préférences
*/
const apiKey = process.env.INFOPARCELLE_API_KEY;

const data = {
filtres: {
// STRICT : Contraintes obligatoires
code_postal: {
value: '69000,69001,69002,69003' // Localisation stricte
},
bati_type: {
value: '1,2,3,4,5,6', // Maisons uniquement
indicatif: false // STRICT
},
piscine: {
value: true,
indicatif: false // STRICT : Doit avoir une piscine
},

// INDICATIF : Préférences (scoring)
superficie: {
min: 400,
max: 800,
indicatif: true // Idéal 400-800m², mais accepte autres
},
surface_habitable: {
min: 150,
max: 300,
indicatif: true // Préférence 150-300m²
},
annee_construction: {
min: 2000,
max: 2023,
indicatif: true // Préférence construction récente
},
dpe_energie: {
value: 'A,B,C',
indicatif: true // Préférence bon DPE
},
jardin: {
min: 100,
indicatif: true // Préférence jardin > 100m²
}
},
pagination: {
limite: 30
},
options: {
champ_tri: 'score_pertinence',
ordre_tri: 'desc',
champs: [
'cadastre_id',
'superficie',
'surface_habitable',
'annee_construction',
'dpes',
'score_pertinence'
]
}
};

const response = await fetch(
'https://app.infoparcelle.fr/api/v1/parcelles/recherche',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}
);

const parcelles = await response.json();

// Analyser les résultats
console.log(`${parcelles.length} maisons trouvées\n`);

parcelles.slice(0, 10).forEach((p, i) => {
const dpe = p.dpes?.[0];
console.log(`${i + 1}. Score: ${p.score_pertinence.toFixed(1)}/100`);
console.log(` ${p.cadastre_id}`);
console.log(` Superficie: ${p.superficie}`);
console.log(` Habitable: ${p.surface_habitable || 'N/A'}`);
console.log(` Construction: ${p.annee_construction || 'N/A'}`);
console.log(` DPE: ${dpe?.etiquette_dpe || 'N/A'}\n`);
});

return parcelles;
}

2. Recherche d'opportunité d'investissement

async function rechercherOpportunitesInvestissement(codePostal) {
/**
* Recherche des opportunités d'investissement avec scoring
*/
const apiKey = process.env.INFOPARCELLE_API_KEY;

const data = {
filtres: {
// STRICT : Localisation et type
code_postal: {
value: codePostal
},
bati_type: {
value: '7,8,9,10,11,12', // Appartements
indicatif: false // STRICT
},

// INDICATIF : Opportunités (scoring)
dpe_energie: {
value: 'F,G', // Passoires thermiques
indicatif: true // Préférence
},
annee_construction: {
max: 1980, // Vieux bâtiments
indicatif: true // Préférence
},
dvf_prix: {
max: 300000, // Prix abordable
indicatif: true // Préférence
},
surface_habitable: {
min: 50,
max: 100,
indicatif: true // Taille idéale
}
},
pagination: {
limite: 50
},
options: {
champ_tri: 'score_pertinence',
ordre_tri: 'desc',
champs: [
'cadastre_id',
'superficie',
'surface_habitable',
'annee_construction',
'dpes',
'dvfs',
'score_pertinence'
]
}
};

const response = await fetch(
'https://app.infoparcelle.fr/api/v1/parcelles/recherche',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}
);

const parcelles = await response.json();

// Analyser le potentiel
const opportunites = [];
for (const p of parcelles) {
const dpe = p.dpes?.[0] || {};
const dvf = p.dvfs?.[0] || {};

// Calculer le potentiel de rénovation
if (['F', 'G'].includes(dpe.etiquette_dpe)) {
const economieEnergie = (dpe.cout_total || 0) * 0.6; // 60% d'économie estimée

opportunites.push({
cadastre_id: p.cadastre_id,
score: p.score_pertinence,
prix_achat: dvf.valeur_fonciere,
annee: p.annee_construction,
dpe: dpe.etiquette_dpe,
economie_annuelle: Math.round(economieEnergie),
potentiel: p.score_pertinence > 80 ? 'ÉLEVÉ' : 'MOYEN'
});
}
}

return opportunites.sort((a, b) => b.score - a.score);
}

// Utilisation
const opportunites = await rechercherOpportunitesInvestissement('75008');
console.log('Top 10 opportunités trouvées:\n');
opportunites.slice(0, 10).forEach((opp, i) => {
console.log(`${i + 1}. Score: ${opp.score.toFixed(1)}/100 - Potentiel: ${opp.potentiel}`);
console.log(` ${opp.cadastre_id}`);
console.log(` Prix: ${opp.prix_achat?.toLocaleString()} € - DPE: ${opp.dpe}`);
console.log(` Économie énergie: ${opp.economie_annuelle} €/an\n`);
});

3. Scoring personnalisé avec pondération manuelle

// Créer son propre système de scoring en combinant le score API
async function rechercheAvecPonderationPersonnalisee(preferences) {
/**
* Recherche avec système de pondération personnalisé
*/
const apiKey = process.env.INFOPARCELLE_API_KEY;

const data = {
filtres: {
code_municipalite: {
value: preferences.commune
},
// Tous les critères en mode indicatif
superficie: {
min: preferences.superficie.min,
max: preferences.superficie.max,
indicatif: true
},
dpe_energie: {
value: preferences.dpe,
indicatif: true
},
annee_construction: {
min: preferences.annee.min,
max: preferences.annee.max,
indicatif: true
},
piscine: {
value: preferences.piscine,
indicatif: true
}
},
pagination: { limite: 50 },
options: {
champ_tri: 'score_pertinence',
ordre_tri: 'desc',
champs: [
'cadastre_id',
'superficie',
'dpes',
'annee_construction',
'detail',
'score_pertinence'
]
}
};

const response = await fetch(
'https://app.infoparcelle.fr/api/v1/parcelles/recherche',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}
);

const parcelles = await response.json();

// Pondération personnalisée
const poids = {
scoreApi: 0.4, // 40% du score de l'API
dpe: 0.3, // 30% pour le DPE
piscine: 0.2, // 20% pour la piscine
jardin: 0.1 // 10% pour le jardin
};

const parcellesAvecScorePerso = parcelles.map(p => {
let scorePerso = 0;

// Score de l'API (0-100)
scorePerso += (p.score_pertinence / 100) * poids.scoreApi * 100;

// Score DPE
const dpe = p.dpes?.[0];
const scoresDpe = { A: 100, B: 85, C: 70, D: 55, E: 40, F: 25, G: 10 };
const scoreDpe = scoresDpe[dpe?.etiquette_dpe] || 0;
scorePerso += (scoreDpe / 100) * poids.dpe * 100;

// Score piscine
const aPiscine = p.detail?.piscines?.nombre > 0;
scorePerso += (aPiscine ? 1 : 0) * poids.piscine * 100;

// Score jardin
const jardin = p.detail?.surfaces?.jardin || 0;
const scoreJardin = Math.min(jardin / 200, 1); // Max à 200m²
scorePerso += scoreJardin * poids.jardin * 100;

return {
...p,
score_personnalise: Math.round(scorePerso)
};
});

// Trier par score personnalisé
return parcellesAvecScorePerso.sort((a, b) =>
b.score_personnalise - a.score_personnalise
);
}

// Utilisation
const resultats = await rechercheAvecPonderationPersonnalisee({
commune: '69123',
superficie: { min: 300, max: 800 },
dpe: 'A,B,C',
annee: { min: 1990, max: 2023 },
piscine: true
});

console.log('Top 10 avec scoring personnalisé:\n');
resultats.slice(0, 10).forEach((p, i) => {
console.log(`${i + 1}. Score perso: ${p.score_personnalise}/100 (API: ${p.score_pertinence}/100)`);
console.log(` ${p.cadastre_id} - ${p.superficie} m²\n`);
});

4. Comparaison de zones avec scoring

async function comparerZonesAvecScoring(codesPostaux, criteres) {
/**
* Comparer plusieurs zones géographiques avec scoring
*/
const apiKey = process.env.INFOPARCELLE_API_KEY;
const resultatsParZone = {};

for (const cp of codesPostaux) {
const data = {
filtres: {
code_postal: { value: cp },
superficie: {
min: criteres.superficieMin,
max: criteres.superficieMax,
indicatif: true
},
dpe_energie: {
value: criteres.dpe,
indicatif: true
}
},
pagination: { limite: 30 },
options: {
champ_tri: 'score_pertinence',
ordre_tri: 'desc',
champs: ['cadastre_id', 'superficie', 'score_pertinence']
}
};

const response = await fetch(
'https://app.infoparcelle.fr/api/v1/parcelles/recherche',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}
);

const parcelles = await response.json();

// Calculer les statistiques
const scores = parcelles.map(p => p.score_pertinence);
resultatsParZone[cp] = {
total: parcelles.length,
scoreMoyen: scores.reduce((a, b) => a + b, 0) / scores.length,
scoreMax: Math.max(...scores),
scoreMin: Math.min(...scores),
topParcelles: parcelles.slice(0, 5)
};
}

return resultatsParZone;
}

// Utilisation
const comparaison = await comparerZonesAvecScoring(
['75008', '75016', '75017'],
{
superficieMin: 300,
superficieMax: 800,
dpe: 'A,B,C'
}
);

for (const [cp, stats] of Object.entries(comparaison)) {
console.log(`Code postal ${cp}:`);
console.log(` Total: ${stats.total} parcelles`);
console.log(` Score moyen: ${stats.scoreMoyen.toFixed(1)}/100`);
console.log(` Meilleur score: ${stats.scoreMax}/100\n`);
}

Interprétation du score

Plages de score

  • 90-100 : Correspondance excellente (tous les critères indicatifs respectés)
  • 75-89 : Très bonne correspondance (la plupart des critères respectés)
  • 60-74 : Bonne correspondance (critères principaux respectés)
  • 40-59 : Correspondance moyenne (certains critères non respectés)
  • 0-39 : Faible correspondance (peu de critères respectés)

Conseils d'utilisation

  1. Seuil minimal : Filtrer les résultats avec score_pertinence >= 60 pour ne garder que les bonnes correspondances

  2. Analyse du top : Se concentrer sur les 10-20 premiers résultats (scores les plus élevés)

  3. Ajustement des critères : Si tous les scores sont faibles, assouplir certains critères indicatifs

Bonnes pratiques

✅ À faire

  1. Combiner strict et indicatif : Filtres géographiques en strict, préférences en indicatif
  2. Trier par score : Utiliser champ_tri: "score_pertinence" pour avoir les meilleurs résultats en premier
  3. Limiter les résultats : Commencer avec limite: 20-30 pour tester
  4. Analyser les scores : Examiner la distribution des scores pour ajuster les critères
  5. Créer des profils : Sauvegarder vos combinaisons de critères efficaces

❌ À éviter

  1. ❌ Trop de critères indicatifs : Limite à 5-7 critères pour une bonne pertinence
  2. ❌ Ignorer les scores faibles : Un score < 40 signifie une mauvaise correspondance
  3. ❌ Oublier le filtre géographique : TOUJOURS requis (au moins un)
  4. ❌ Utiliser uniquement le mode strict : Vous perdez l'avantage du scoring
  5. ❌ Ne pas paginer : Traiter les résultats par lots de 20-50

Limites et contraintes

  • Au moins UN filtre géographique requis (code_postal, code_municipalite, etc.)
  • Maximum 50 résultats par requête (utilisez la pagination)
  • Filtres géographiques STRICT uniquement (pas de mode indicatif)
  • Score calculé automatiquement (pas de pondération manuelle via l'API)
  • Rate limit : 1200 requêtes/minute

Performances

  • Temps de réponse moyen : < 300ms (légèrement plus lent que GET en raison du calcul de score)
  • Cache côté serveur : 15 minutes
  • Complexité de calcul : Proportionnelle au nombre de critères indicatifs

Voir aussi

Scoring intelligent

Le système de scoring vous permet de créer des recherches flexibles et intelligentes. Combinez des contraintes obligatoires (mode strict) avec des préférences (mode indicatif) pour trouver les parcelles les plus pertinentes !