Skip to main content

Documentation Index

Fetch the complete documentation index at: https://doc.fluximmo.io/llms.txt

Use this file to discover all available pages before exploring further.

Goal

Construire un dataset dédupliqué d’appartements à l’achat sur Paris (puis l’Île-de-France), paginer pour l’exhaustivité, et calculer des statistiques prix au m² par département — sans biais lié aux doublons inter-portails.

Scénario

Vous êtes une fintech ou une équipe analytics qui doit cartographier le marché parisien : stock disponible, médianes prix/m², distribution par dpt. Compter les annonces brutes (Adverts) gonfle le volume car chaque bien est publié sur 3-8 portails. Vous travaillez donc sur Properties (entités dédupliquées) avec pagination cursor et un workflow reproductible.

Étapes

1

1. Choisir Properties (et pas Adverts)

Une Property = 1 bien physique unique = N adverts agrégés. C’est l’objet à utiliser pour tout calcul agrégé : compter des biens (pas des publications), médianes prix, stocks, durées en ligne.Détail de l’asymétrie et des anti-patterns : /concepts/property-vs-advert.
2

2. Construire le payload — combo standard prod

Trois bonnes pratiques :
  • meta.isTotallyOffline: false → exclut les biens dont toutes les adverts sont offline (sinon votre stock contient des fantômes).
  • sortBy: "FIRST_SEEN_AT" + orderBy: "DESC" → vous parcourez du plus récent au plus ancien, idéal pour reprendre une exploration à chaud.
  • meta.firstSeenAt.min → borne temporelle pour ignorer le très ancien (typiquement 2025-01-01T00:00:00.000Z).
  • size: 100 → max autorisé sur l’endpoint full search (lite est plafonné à 25).
curl -X POST "https://api.fluximmo.io/v2/protected/properties/search" \
  -H "x-api-key: $FLUXIMMO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "size": 100,
    "sortBy": "FIRST_SEEN_AT",
    "orderBy": "DESC",
    "search": {
      "filterProperty": {
        "location": [{ "postalCode": "75001" }],
        "type": ["CLASS_FLAT", "CLASS_HOUSE", "CLASS_PROGRAM"],
        "offer": [{ "type": "OFFER_BUY" }],
        "price": { "initial": { "value": { "min": 100000, "max": 350000 } } },
        "habitation": {
          "surface":     { "total": { "min": 30, "max": 110 } },
          "bedroomCount":{ "min": 1, "max": 3 }
        },
        "meta": {
          "isTotallyOffline": false,
          "firstSeenAt": { "min": "2025-01-01T00:00:00.000Z" }
        }
      }
    }
  }'
3

3. Lire le `searchAfterHash` retourné

La réponse est wrappée sous data et contient items[] (jusqu’à 100 properties), count, et un champ searchAfterHash. C’est un cursor opaque : ne le décodez pas, contentez-vous de le replacer dans la requête suivante.
{
  "data": {
    "items": [ /* up to 100 Property */ ],
    "count": 1247,
    "searchAfterHash": "eyJzZWFyY2hfYWZ0ZXIiOlsxNzM..."
  }
}
4

4. Paginer pour exhaustivité (boucle cursor)

Boucle : tant que la dernière page ramène size items, replongez avec searchAfterHash. Stoppez quand len(items) < size (fin du dataset) ou quand votre cap user-defined est atteint.
# Pseudocode pagination
all_items = []
cursor = null
HARD_CAP = 5000

while all_items.length < HARD_CAP:
    payload = base_payload
    if cursor: payload.searchAfterHash = cursor

    resp = POST /v2/protected/properties/search (payload)
    items = resp.data.items
    all_items.append_all(items)

    if items.length < SIZE: break        # dernière page
    cursor = resp.data.searchAfterHash
    if !cursor: break
Deuxième page en curl direct :
curl -X POST "https://api.fluximmo.io/v2/protected/properties/search" \
  -H "x-api-key: $FLUXIMMO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "size": 100,
    "sortBy": "FIRST_SEEN_AT",
    "orderBy": "DESC",
    "searchAfterHash": "<cursor de la page 1>",
    "search": { "filterProperty": { "location": [{"postalCode":"75001"}], "type": ["CLASS_FLAT"], "offer": [{"type":"OFFER_BUY"}], "meta": {"isTotallyOffline": false} } }
  }'
5

5. Calculer le prix au m² par département

Une fois le dataset collecté, agrégez par les 2 premiers caractères du postalCode (= département).
# Pseudocode agrégation
by_dpt = {}
foreach p in all_items:
    cp        = p.location.postalCode
    price_psm = p.price.latest.valuePerArea
    if cp and price_psm:
        dpt = cp[0..2]
        by_dpt[dpt].append(price_psm)

foreach dpt, vals in by_dpt:
    sort(vals)
    n   = vals.length
    q1  = vals[floor(0.25 * (n - 1))]
    med = vals[floor(0.50 * (n - 1))]
    q3  = vals[floor(0.75 * (n - 1))]
    print(dpt, n, q1, med, q3)
6

6. Étendre à toute l'Île-de-France (multi-zones OR)

Pour passer de Paris-1er à Paris + petite couronne, remplacez location[] par 4 zones combinées en OR.
{
  "size": 100,
  "sortBy": "FIRST_SEEN_AT",
  "orderBy": "DESC",
  "search": {
    "filterProperty": {
      "location": [
        { "department": "75" },
        { "department": "92" },
        { "department": "93" },
        { "department": "94" }
      ],
      "type": ["CLASS_FLAT"],
      "offer": [{ "type": "OFFER_BUY" }],
      "meta": {
        "isTotallyOffline": false,
        "firstSeenAt": { "min": "2025-01-01T00:00:00.000Z" }
      }
    }
  }
}
Logique multi-zones complète et autres modes (bbox, geoDistance) : /concepts/recherche-geographique.

Architecture / flow

Pièges fréquents

  • Confondre Properties et Adverts → analytics biaisé : un même bien compté 3-8 fois (1 par portail) gonfle le stock et fausse les médianes. Toujours Properties pour les KPIs marché.
  • Oublier meta.isTotallyOffline: false → vous comptez des biens dont toutes les annonces sont offline (fantômes du catalogue).
  • Tenter une pagination par offset : ça n’existe pas. Le seul mode est cursor searchAfterHash. Ne décodez pas le cursor — c’est opaque.
  • Mixer postalCode et city dans la même entrée : city est ignoré par le moteur. Voir warning city deprecated.
  • Dépasser size max : 100 sur full search, 25 sur lite search. Au-delà, l’API reject ou tronque.
  • Ne pas dédoublonner sur flxId entre pages : un append concurrent peut renvoyer un même item deux fois si le tri change. Conservez un set des flxId vus.

Pour aller plus loin