Aller au contenu
Référence technique

Documentation API v1

Base URL : https://cherchertrouver.immo/api/v1

Des chapitres réservés s’affichent ici une fois connecté au compte auquel nous les avons ouverts.

Donner cette documentation à un assistant

Ces exports contiennent toute la référence en un seul fichier : points d’accès, paramètres, bornes et codes de réponse. Un assistant les ingère d’un bloc et sait ensuite appeler l’API sans inventer de paramètre.

Version publique permanente, sans compte : https://cherchertrouver.immo/llms.txt

Authentification

Toutes les requêtes doivent inclure votre clé API. Deux méthodes acceptées :

En-tête X-Api-Key Recommandé
X-Api-Key: imk_votre_cle
Authorization Bearer
Authorization: Bearer imk_votre_cle
Ne jamais exposer votre clé côté client (navigateur, app mobile). Toujours appeler l’API depuis votre serveur.

Limites & quotas

Chaque réponse acceptée inclut ces en-têtes pour surveiller votre consommation. Une réponse 429 porte Retry-After et X-Annonces-From, mais pas les compteurs de quota :

En-têteDescription
X-RateLimit-LimitNombre max de requêtes par minute
X-RateLimit-RemainingRequêtes restantes dans la fenêtre courante
X-RateLimit-ResetHorodatage UNIX de réinitialisation de la fenêtre
X-Quota-Items-LimitQuota journalier d’annonces retournées
X-Quota-Items-UsedAnnonces déjà consommées sur les dernières 24h
X-Quota-Mois-LimitQuota d’annonces de la période (offre souscrite)
X-Quota-Mois-UsedAnnonces déjà découvertes sur la période
X-Quota-Mois-RemainingCe qu’il vous reste sur la période
X-Quota-Mois-ResetDate de remise à zéro, sur un abonnement (absent sur la fenêtre glissante de l’offre gratuite)
X-Annonces-FromDate à partir de laquelle votre clé reçoit les annonces (cf. Périmètre du catalogue)
Les quotas comptent le nombre d'annonces uniques découvertes, pas les requêtes. Si vous récupérez la même annonce plusieurs fois (interrogations répétées, parcours des pages), elle ne compte qu'une seule fois dans votre quota. Le point d’accès /ping ne consomme pas de quota. Les quatre en-têtes X-Quota-Mois-* sont absents si votre compte n’a pas de quota mensuel : c’est le cas des comptes ouverts avant la mise en place des paliers, qui conservent leur régime d’origine et son seul plafond journalier. Un plafond distinct limite le nombre total de requêtes/jour (anti-interrogation abusive).
Deux quotas, et ils ne se remplacent pas : le journalier protège le service, le mensuel délimite l'offre souscrite. Le mensuel se compte sur votre période de facturation ; sur l'offre gratuite, qui n'a pas d'abonnement, il se compte sur une fenêtre glissante de 30 jours. Au-delà, les annonces neuves reçoivent un 402 de code ITEMS_QUOTA : le corps porte quota, consomme, la date de reprise (repart_le sur un abonnement, se_libere_le sur la fenêtre glissante) et le palier_suivant. Pour distinguer un quota atteint d'une panne : /ping continue de répondre. Les autres points d'accès d'annonces, eux, renvoient tous 402, y compris pour une annonce que vous connaissez déjà : le refus est posé avant la recherche.
OffreAnnonces / moisAnnonces uniques / jourRequêtes / jourReq / minReq / s
Découverte 50 200 300 60 1
Secteur 1 000 200 600 60 1
Ville 5 000 500 1 500 60 1
Département 20 000 2 000 6 000 60 1
Région 60 000 6 000 18 000 120 2
National 150 000 15 000 45 000 300 5
Plateforme 400 000 40 000 120 000 600 10
Sur mesure Nous consulter Nous consulter Sur mesure Sur mesure Sur mesure
C'est la colonne Annonces / mois qui mord en premier : dimensionnez dessus, pas sur le plafond journalier. Une annonce déjà reçue ne consomme rien, une requête si : les plafonds de requêtes laissent trois appels par annonce, de quoi lire une page puis ouvrir chaque fiche. Les tarifs de chaque palier sont sur la page API.

Périmètre du catalogue

Deux règles distinguent ce que renvoie l’API de ce qu’affiche la page publique. Elles jouent en sens inverse : la première réduit le périmètre, la seconde l’élargit.

1. Date de début d’accès

Une clé ne restitue que les annonces entrées dans notre catalogue à partir d’une date donnée, renvoyée dans l’en-tête X-Annonces-From de chaque réponse. Par défaut, cette date est celle de la création de votre compte : l’abonnement porte sur le flux courant, l’historique constitué avant votre arrivée étant proposé en option.

  • Elle dépend du compte, pas de la clé : en révoquer une et en créer une autre ne déplace pas cette date.
  • Elle porte sur la date d’entrée dans notre base, et non sur la date de publication sur le site d’origine ni sur updated_at. Le paramètre updated_since ne permet donc pas de remonter au-delà.
  • Elle s’applique à toutes les offres, gratuite comme payantes.
Un compte tout juste créé ne voit donc que quelques heures de collecte. Si vous évaluez l’API et obtenez un volume sans rapport avec le marché, c’est presque toujours cela : demandez-nous une fenêtre d’historique, nous l’ouvrons pour la durée de votre évaluation.

2. Aucun filtre de fraîcheur

La page publique n’affiche que les annonces revues dans les deux dernières semaines. L’API lève cette contrainte : elle vous sert aussi les annonces que nous n’avons pas revues depuis, sur la période à laquelle vous avez accès. C’est délibéré : une veille incrémentale a besoin de revoir une annonce même quand elle cesse d’être rafraîchie.

En revanche, une annonce explicitement passée à vendue, sous compromis ou archivée sort du flux, sur l’API comme sur le site. Une annonce qui disparaît de vos résultats est donc soit vendue, soit hors de votre période d’accès : ne déduisez pas de sa présence qu’un bien est toujours disponible, ni de son absence qu’il a été vendu.

Pour vous rapprocher de ce qu’affiche le site, filtrez avec updated_since : il porte sur la date de notre dernier passage de collecte, exposée dans le champ updated_at. Une annonce que nous n’avons pas revue depuis deux semaines a de fortes chances de ne plus être en ligne.

Format des erreurs

Toutes les erreurs retournent un JSON avec error (message lisible) et code (identifiant machine) :

{
  "error": "Limite de requêtes dépassée (60 req/min max)",
  "code": "RATE_LIMIT_PER_MIN",
  "retry_after_seconds": 42
}
HTTPCodeCause
401API_KEY_MISSINGAucune clé fournie
401API_KEY_INVALIDClé invalide, révoquée ou bloquée
400BAD_REQUESTParamètres invalides (détails dans details[])
404NOT_FOUNDRessource introuvable
429RATE_LIMIT_PER_SECLimite par seconde dépassée
429RATE_LIMIT_PER_MINLimite par minute dépassée
402QUOTA_EXCEEDEDQuota journalier d’annonces atteint
402ITEMS_QUOTAQuota d’annonces de la période atteint : le corps porte la date de reprise et le palier suivant
402PAID_PLAN_REQUIREDFiltre par site (sources, exclude_sources, /sources), réservé aux offres à partir de 118,80 € TTC par mois
GET

/api/v1/ping

Health check, vérifie que votre clé est valide et retourne les limites configurées pour votre offre. Utile pour tester l’authentification sans consommer de quota.

items_per_day est le plafond d’annonces distinctes par jour, distinct du volume mensuel de votre offre. Il vaut le dixième du volume mensuel, avec un minimum de 200. L’offre Secteur (1 000 annonces par mois pour 12 € TTC) est ainsi bornée à 200 annonces par jour. Le plafond de chaque offre figure aussi sur la page des tarifs.
Réponse
{
  "ok": true,
  "api_key_name": "Clé personnelle",
  "tier": "free",
  "rate_limit_per_sec": 1,
  "rate_limit_per_min": 60,
  "items_per_day": 200,
  "server_time": "2026-04-18T10:00:00.000Z"
}
GET

/api/v1/annonces

Recherche d’annonces, rendue par pages. Tous les filtres sont optionnels. Les partenaires API accèdent au catalogue complet, sans filtre de récence.

Filtre implicite : seules les annonces avec au moins une photo sont renvoyées (filtre implicite, non désactivable). Les annonces sans photo sont systématiquement filtrées côté base avant le découpage en pages.
Paramètres de requête
ParamètreTypeDescription
qstringRecherche plein texte (titre, description)
typestringType de bien : Appartement, Maison, Terrain…
transactionenumvente ou location
villestringNom de ville (ex : Paris, Lyon)
cpstringCode postal (ex : 75015)
deptstringNuméro de département (ex : 75, 2A)
regionstringNom de région (ex : Bretagne)
Prix
prix_minintegerPrix minimum (€)
prix_maxintegerPrix maximum (€)
prix_m2_minintegerPrix au m² minimum
prix_m2_maxintegerPrix au m² maximum
Surface & caractéristiques
surface_minnumberSurface minimale (m²)
surface_maxnumberSurface maximale (m²)
pieces_minintegerNombre de pièces minimum
chambres_minintegerNombre de chambres minimum
annee_mininteger Année de construction minimale. Renseignée sur environ 35 % des annonces : ce filtre écarte donc aussi les biens dont nous ne connaissons pas l’année. À utiliser pour trouver du récent à coup sûr, pas pour affirmer qu’un secteur n’en a pas.
annee_maxinteger Année de construction maximale. Même réserve de couverture.
DPE / GES
dpestring ou arrayClasses DPE : A,B,C ou dpe=A&dpe=B
gesstring ou arrayClasses GES (même format)
Veille incrémentale (date)
updated_since ISO 8601 Annonces (re-)collectées depuis cette date (= colonne updated_at de la réponse). À combiner avec sort=recent pour faire de la veille incrémentale fiable (« donne-moi ce qui a bougé depuis ma dernière poll »). Ex : updated_since=2026-06-09T00:00:00Z.
created_since ISO 8601 Annonces dont la mise en ligne sur le site source est postérieure à cette date (= colonne published_at). Moins fiable que updated_since pour la veille (certaines annonces sont ré-éditées sans changer leur date pub).
Pages & tri
pageintegerPage demandée (défaut : 1, max : 1000)
page_sizeintegerRésultats par page (défaut : 25, max : 100)
cursor string opaque Parcours par repère : chaque réponse porte le point de reprise de la suivante. À préférer dès qu’on descend loin dans les résultats. Au lieu d'incrémenter page, passer la valeur de next_cursor reçue dans la réponse précédente. O(1) par page quelle que soit la profondeur (vs page=N qui devient lent au-delà de 50 pages). Compatible avec tous les autres filtres ; ne pas envoyer page en même temps (cursor prend le pas).
sort enum recent (tri par updated_at DESC : date de collecte, idéal veille incrémentale) · price_asc · price_desc · surface_desc · pricem2_asc
sourceenumall (défaut) · interne · scraped
sourcesliste Ne garder que ces sites : site_a,site_b. Identifiants exacts servis par GET /sources. offre payante
exclude_sourcesliste Tout garder sauf ces sites : site_a. Plus pratique que sources quand on veut presque tout, et plus robuste : une liste blanche se périme dès qu’un site est ajouté, une liste noire non. Combinable avec sources : la différence est appliquée. offre payante
Pattern veille incrémentale recommandé : à chaque poll, stocker côté client le updated_at de la plus récente annonce reçue. Au poll suivant, requêter avec updated_since=<cette_date>&sort=recent et itérer via next_cursor jusqu'à has_more=false. Pour une fenêtre de 20 000 annonces, page_size=50 donne 400 pages, toutes accessibles en O(1) par page via le cursor. Aucun re-scan complet du catalogue n'est nécessaire.
Pour simplement tout aspirer et rester à jour sans gérer de date, utilisez plutôt le flux complet /annonces/feed : une seule boucle, un curseur, zéro trou.
Exemple : itération par cursor
$ # Première page
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/annonces?updated_since=2026-06-09&sort=recent&page_size=50"
# → réponse contient "next_cursor": "eyJ1IjoiMjAyNi0wNi0...="

$ # Page suivante : passer next_cursor reçu
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/annonces?cursor=eyJ1IjoiMjAyNi0wNi0...="
Exemple de requête
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/annonces?ville=Nantes&type=Appartement&prix_max=350000&page_size=5"
Réponse
{
  "page": 1,
  "page_size": 5,
  "has_more": true,
  "next_cursor": "eyJ1IjoiMjAyNi0wNi0wOVQwMjozMjowMC4wMDBaIiwiciI6ImFiYzEyMyIsInMiOiJzZWxvZ2VyIn0",
  "items": [
    {
      "source": "site_a",
      "reference": "abc123",
      "title": "Appartement 3 pièces 68m² Nantes Centre",
      "type": "Appartement",
      "transaction_type": "vente",
      "price": 312000,
      "price_per_m2": 4588,
      "price_per_m2_color": "#f59e0b",
      "price_label": null,
      "surface": 68,
      "land_surface": null,
      "living_room_surface": 28,
      "rooms": 3,
      "bedrooms": 2,
      "bathrooms": 1,
      "shower_rooms": 1,
      "toilets": 1,
      "kitchen": "équipée",
      "year_built": 1975,
      "elevator": true,
      "parking": false,
      "parking_interior": null,
      "parking_exterior": null,
      "cellar": true,
      "garden": false,
      "seller_type": "Pro",
      "seller_name": "Agence Centre Immo",
      "real_estate_network": "Réseau Exemple",
      "exclusive": true,
      "city": "Nantes",
      "postal_code": "44000",
      "department": "44",
      "region": "Pays de la Loire",
      "latitude": 47.2184,
      "longitude": -1.5536,
      "dpe": "D",
      "ges": "D",
      "dpe_value": 210,
      "ges_value": 42,
      "dpe_chart_url": "https://...",
      "ges_chart_url": null,
      "legal_info": "Copropriété de 24 lots...",
      "images": ["https://..."],
      "images_count": 8,
      "description": "Bel appartement lumineux...",
      "external_url": "https://site-a.example/...",
      "video_url": null,
      "virtual_tour_url": "https://...",
      "published_at": "2026-04-15T08:23:00.000Z",
      "updated_at": "2026-09-05T04:12:00.000Z",
      "seller_contact": null,
      "price_history": {
        "current": 312000,
        "previous": 325000,
        "changed_at": "2026-08-14T03:12:00.000Z",
        "changes": 2,
        "tracked_since": "2026-06-23T02:10:00.000Z",
        "depth": 2
      },
      "is_internal": false,
      "dedup_key": "a1b2c3d4e5f60718",
      "sources": [
        { "source": "site_a", "reference": "123456", "url": "https://site-a.example/...", "price": 415000, "is_main": true },
        { "source": "site_b", "reference": "789012", "url": "https://site-b.example/...", "price": 418000, "is_main": false }
      ]
    }
  ]
}
Suivi de prix (price_history) : les deux derniers prix relevés sur l’annonce, dans toutes les offres. null tant que nous n’avons aucun relevé.
  • current et previous : le dernier prix et celui d’avant. previous vaut null quand le prix n’a pas bougé depuis que nous suivons l’annonce : ce n’est pas une donnée manquante, c’est un prix stable.
  • tracked_since : la date de notre PREMIER relevé. Toute variation se compte à partir de là, jamais depuis la mise en vente : nous ne connaissons pas le prix d’une annonce avant de l’avoir vue. Un relevé par heure, sur les annonces actives, au seuil de 1 %.
  • depth : le nombre de prix réellement renvoyés. Il vaut 2 sans la série, sinon la longueur de points. Lisez-le plutôt que de déduire votre offre de l’absence d’un champ.
  • Profondeur attendue : le suivi a commencé le 23 juin 2026, et aucune annonce n’a donc davantage d’antériorité. Un bien mis en vente avant cette date arrive chez nous avec le prix qu’il avait ce jour-là, pas avec son historique complet.
  • points : la série complète des relevés, réservée aux offres à partir de 118,80 € TTC par mois et servie uniquement sur la fiche d’une annonce (/annonces/{source}/{reference}). Sur /annonces et le flux, aucune offre ne la reçoit.
Déduplication (activée par défaut) : un même bien listé sur plusieurs sites n'est renvoyé qu'une seule fois, sous son annonce de référence (le champ source = le site principal).
  • dedup_key : identifiant stable du groupe de doublons (même bien = même clé sur tous les sites). null si l'annonce n'a pas de doublon connu.
  • sources : la liste de TOUS les sites du groupe (le principal + les doublons), chacun avec source, reference, url, price et is_main. null s'il n'y a pas de doublon.
  • <champ>_source : quand une info manquante a été complétée depuis un doublon d'un autre site, ce champ en indique la provenance. Exemple : une annonce sans description qui récupère celle d'un doublon publié sur un autre site renvoie "description_source": "site_b". S'applique à la description, au titre, aux photos et aux caractéristiques (ex : bedrooms_source, dpe_source). Absent si le champ provient de l'annonce elle-même.
  • Pour recevoir tous les doublons non regroupés (comportement historique), ajouter ?dedup=0.
Champs null = information non disponible pour cette annonce. seller_type vaut "Pro", "Particulier" ou null. Les booléens d'équipement (elevator, parking, parking_interior, parking_exterior, cellar, garden, exclusive) valent true, false ou null (inconnu). dpe_value / ges_value sont les valeurs numériques exactes (kWh/m²/an et kg CO₂/m²/an) quand l'annonce les fournit, en complément des lettres dpe / ges.
GET

/api/v1/annonces/feed

Flux complet du catalogue. Conçu pour un seul besoin : récupérer toutes les annonces qui entrent en base, puis rester à jour en continu, sans jamais en manquer ni gérer de pages. Une seule boucle : on rappelle avec le next_cursor reçu, indéfiniment.

Aucun plafond de profondeur. Contrairement au parcours par pages page (limitée à 1000 pages, soit ~100 000 annonces), le flux parcourt l’intégralité du catalogue puis suit les nouveautés sans limite.
Paramètres de requête
ParamètreTypeDescription
cursor string opaque Position de reprise. Absent au 1er appel (démarre au plus ancien). Ensuite, réinjecter la valeur next_cursor de la réponse précédente. À conserver côté client pour reprendre après un redémarrage.
page_sizeintegerAnnonces par page (défaut : 100, max : 100)
Filtres divers Tous les filtres de /annonces (type, ville, dept, prix_max…) sont acceptés pour ne suivre qu’un sous-flux. Sans filtre : tout le catalogue.
La boucle (une seule) :
  1. Appeler sans cursor au démarrage.
  2. Traiter items, enregistrer next_cursor, rappeler avec ?cursor=<next_cursor>.
  3. Répéter tant que items n’est pas vide : vous parcourez tout le catalogue (rattrapage initial).
  4. items vide (has_more=false) : vous êtes à jour. Attendre (1 à 5 min) puis rappeler avec le même cursor  : les nouvelles annonces arrivent au fil de l’eau.
Zéro trou garanti. Une annonce apparaît dans le flux ~2 min après son entrée en base (délai de sécurité qui garantit qu’aucune n’est sautée pendant les insertions). Négligeable pour une synchronisation.
Dédupliquez par (source, reference). Le flux est un change-feed : une annonce ré-analysée réapparaît (données à jour). Les sites d’un même bien sont livrés séparément (pas de regroupement).
Exemple
$ # Premier appel (sans cursor)
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/annonces/feed?page_size=100"
# → réponse : "next_cursor": "eyJ1IjoiMjAyNi0wNy0wMS...="

$ # Appels suivants : réinjecter next_cursor. Page vide = à jour → re-poller plus tard.
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/annonces/feed?cursor=eyJ1IjoiMjAyNi0wNy0wMS...="
Réponse
{
  "count": 100,
  "has_more": true,
  "next_cursor": "eyJ1IjoiMjAyNi0wNy0wMVQxMDowMDowMFoiLCJyIjoiYWJjMTIzIiwicyI6ImxlYm9uY29pbiJ9",
  "items": [
    {
      "source": "site_b",
      "reference": "abc123",
      "title": "Studio 30m² Paris 15e",
      "price": 385000,
      "surface": 30,
      "city": "Paris",
      "postal_code": "75015",
      // ... mêmes champs que /annonces
    }
  ]
}
next_cursor est toujours présent (même quand items est vide) pour permettre la reprise. Il vaut null uniquement si la base est momentanément indisponible. Le quota ne compte que les annonces nouvelles : un renvoi ne consomme rien.
GET

/api/v1/annonces/map

Retourne les points géographiques d’une zone définie par son rectangle englobant. Format léger, taillé pour alimenter une carte Leaflet ou Mapbox. Accepte les mêmes filtres que /annonces.

Paramètre spécifique
ParamètreRequisDescription
bbox Oui Rectangle englobant, au format sud,ouest,nord,est (degrés décimaux)
Exemple
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/annonces/map?bbox=47.15,-1.65,47.30,-1.45&type=Maison"
Réponse
{
  "bbox": { "south": 47.15, "west": -1.65, "north": 47.30, "east": -1.45 },
  "total": 142,
  "capped": false,
  "items": [
    {
      "source": "site_b",
      "reference": "xyz789",
      "latitude": 47.2184,
      "longitude": -1.5536,
      "price": 285000,
      "price_per_m2": 2375,
      "price_per_m2_color": "#16a34a",
      "surface": 120,
      "type": "Maison",
      "title": "Maison 5 pièces 120m²",
      "city": "Nantes",
      "postal_code": "44000",
      "rooms": 5,
      "image": "https://...",
      "is_internal": false
    }
  ]
}
capped: true indique que la bbox contient plus d’annonces que la limite retournée (≤ 2 000 points par requête). Zoomez ou affinez les filtres.
GET

/api/v1/annonces/:source/:reference

Retourne le détail complet d’une annonce identifiée par sa source et sa référence. Consomme 1 annonce sur le quota journalier.

Paramètres de chemin
ParamètreDescription
:sourceIdentifiant de la source, tel que servi par /annonces/sources
:referenceRéférence unique de l’annonce sur la source (max 128 chars)
Exemple
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/annonces/site_a/abc123"
Réponse
{
  "annonce": {
    "source": "site_a",
    "reference": "abc123",
    // ... tous les champs (même structure que /annonces)
  }
}
GET

/api/v1/ptz/zone

Lookup officiel d’une commune française vers sa zone du prêt à taux zéro (PTZ) (A bis / A / B1 / B2 / C) selon l’arrêté du 5 septembre 2025 du Ministère du Logement. Zonage utilisé par tous les dispositifs aidés : Prêt à Taux Zéro, Pinel, Logement Locatif Intermédiaire, Bail Réel Solidaire, PSLA. Coût : 1 token par lookup réussi (200). Les erreurs (400/404) ne consomment pas.

Paramètres de requête
ParamètreDescription
cp requisCode postal français à 5 chiffres (ex : 75011)
Exemple
$ curl -H "X-Api-Key: imk_xxx" \
  "https://cherchertrouver.immo/api/v1/ptz/zone?cp=75011"
Réponse (200 OK)
{
  "zone": "Abis",
  "codePostal": "75011",
  "source": "Arrêté du 5 septembre 2025, Ministère du Logement"
}
Réponse (404, code postal inconnu)
{
  "error": "Code postal inconnu"
}
Les valeurs de zone possibles sont : "Abis" (Paris, ultra-tendu), "A" (grandes métropoles), "B1" (agglos tendues), "B2" (villes moyennes), "C" (rural & petites villes, ~81 % du territoire). Couvre 34 875 communes françaises. Cache HTTP : 24h (la zone d’une commune ne change que par arrêté ministériel, ~1× par an).
Prêt à démarrer ?

Obtenez votre clé Free en 30 secondes