{"info":{"name":"LogiScan — API publique (/v1)","description":"L'API LogiScan : adresse probable d'une annonce, Deal Score par rayon, ventes DVF, diagnostic ADEME, complétude.\n\nRenseignez `cle_api` dans l'environnement — jamais dans la collection. Une clé `lsk_test_…` répond gratuitement avec des données fictives ; une clé `lsk_live_…` consomme des crédits. Documentation : {{base_url}}/developpeurs ; schéma : {{base_url}}/v1/openapi.json.\n\nErreurs : toujours `{\"error\": {\"code\", \"message\", \"request_id\"}}` — 401 clé invalide, 402 crédit insuffisant, 422 corps invalide, 429 plafond, 503 API éteinte ou source en panne (crédit rendu).","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json"},"variable":[{"key":"dernier_id","value":""}],"item":[{"name":"Compte","item":[{"name":"Le compte derrière la clé","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"}],"url":{"raw":"{{base_url}}/v1/account","host":["{{base_url}}"],"path":["v1","account"]},"description":"Gratuit. Le solde en crédits, l'usage sur quatre fenêtres (24 h, 7 j, 30 j, 90 j — la dernière est aussi la durée pendant laquelle l'usage est gardé), et les tarifs en vigueur."},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));","const c = pm.response.json(); pm.test('solde et tarifs', () => { pm.expect(c).to.have.property('balance_credits'); pm.expect(c.tarifs).to.have.property('analyses.listing_url'); });"]}}]}]},{"name":"Analyses","item":[{"name":"Analyser une annonce par son lien","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/analyses?wait=20","host":["{{base_url}}"],"path":["v1","analyses"],"query":[{"key":"wait","value":"20"}]},"description":"`wait` attend le résultat jusqu'à vingt secondes ; sinon la réponse est un 202 avec l'adresse où le relire (`GET /v1/analyses/{id}`). Un même lien demandé deux fois dans l'heure est servi d'une mémoire, au même tarif. Une réponse vide mais honnête — aucun candidat, pas assez de ventes — est facturée : le travail a été fait. Une source en panne ne l'est pas : `billed` vaut `false` et le crédit revient.","body":{"mode":"raw","raw":"{\n  \"source\": {\n    \"listing_url\": \"https://www.exemple.fr/annonces/12345\"\n  },\n  \"radii\": [\n    300,\n    500,\n    800,\n    1000\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));","const c = pm.response.json();","pm.test('analyse terminée', () => pm.expect(c.statut).to.eql('termine'));","pm.test('candidats scorés', () => pm.expect(c.resolution.candidates).to.be.an('array'));","pm.collectionVariables.set('dernier_id', c.id);"]}}]},{"name":"Analyser le texte d'une annonce","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/analyses?wait=20","host":["{{base_url}}"],"path":["v1","analyses"],"query":[{"key":"wait","value":"20"}]},"description":"Le texte est lu par le même lecteur que le site, puis **jeté** : il n'est ni journalisé, ni mis en mémoire, ni écrit en base.","body":{"mode":"raw","raw":"{\n  \"source\": {\n    \"listing_text\": \"Appartement T3 65 m² au 2e étage, Villeneuve-sur-Fixture (99999). DPE C. 189 000 €.\"\n  }\n}","options":{"raw":{"language":"json"}}}},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));","const c = pm.response.json();","pm.test('analyse terminée', () => pm.expect(c.statut).to.eql('termine'));","pm.test('candidats scorés', () => pm.expect(c.resolution.candidates).to.be.an('array'));","pm.collectionVariables.set('dernier_id', c.id);"]}}]},{"name":"Analyser une adresse, sans annonce","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/analyses?wait=20","host":["{{base_url}}"],"path":["v1","analyses"],"query":[{"key":"wait","value":"20"}]},"description":"Sans prix, le Deal Score ne note pas : il rend une **fourchette** (estimation basse, médiane, haute) à partir des ventes voisines, avec le statut `annonce_incomplete`. Avec `prix_eur`, il note.","body":{"mode":"raw","raw":"{\n  \"source\": {\n    \"address\": \"12 rue de l'Exemple 99999 Villeneuve-sur-Fixture\",\n    \"facts\": {\n      \"type_bien\": \"appartement\",\n      \"surface_m2\": 65\n    }\n  },\n  \"radii\": [\n    500,\n    1000\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));","const c = pm.response.json();","pm.test('analyse terminée', () => pm.expect(c.statut).to.eql('termine'));","pm.test('candidats scorés', () => pm.expect(c.resolution.candidates).to.be.an('array'));","pm.collectionVariables.set('dernier_id', c.id);"]}}]},{"name":"Rejouer avec Idempotency-Key (le second appel n'est pas facturé)","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"},{"key":"Idempotency-Key","value":"{{$guid}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/analyses?wait=20","host":["{{base_url}}"],"path":["v1","analyses"],"query":[{"key":"wait","value":"20"}]},"description":"Envoyez deux fois la même requête avec la même Idempotency-Key : le second appel rend la réponse du premier, avec `billed: false`. Changez la clé pour un nouvel appel.","body":{"mode":"raw","raw":"{\n  \"source\": {\n    \"listing_url\": \"https://www.exemple.fr/annonces/12345\"\n  },\n  \"radii\": [\n    300,\n    500,\n    800,\n    1000\n  ]\n}","options":{"raw":{"language":"json"}}}},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));"]}}]},{"name":"Relire une analyse","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"}],"url":{"raw":"{{base_url}}/v1/analyses/{{dernier_id}}?wait=15","host":["{{base_url}}"],"path":["v1","analyses","{{dernier_id}}"],"query":[{"key":"wait","value":"15"}]},"description":"L'identifiant est posé par les requêtes d'analyse précédentes (variable `dernier_id` de la collection). Un résultat reste lisible une heure."},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));"]}}]}]},{"name":"Diagnostic","item":[{"name":"La fiche d'un diagnostic","request":{"method":"GET","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"}],"url":{"raw":"{{base_url}}/v1/dpe/BACASABLE0001","host":["{{base_url}}"],"path":["v1","dpe","BACASABLE0001"]},"description":"Treize caractères, chiffres ou lettres majuscules. `provenance` dit si la fiche vient du miroir local du fichier ADEME (`local`) ou du flux en ligne (`api`). Un numéro inconnu du référentiel rend 404 — facturé, le référentiel a été consulté. Avec une clé réelle, remplacez BACASABLE0001 par un vrai numéro ADEME."},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));","const c = pm.response.json();","pm.test('billed et mode présents', () => { pm.expect(c).to.have.property('billed'); pm.expect(c).to.have.property('mode'); });","pm.test('provenance', () => pm.expect(['local', 'api']).to.include(pm.response.json().provenance));"]}}]}]},{"name":"Complétude","item":[{"name":"Ce qui manque à une annonce","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/listings/completeness","host":["{{base_url}}"],"path":["v1","listings","completeness"]},"description":"Une annonce structurée (`listing`) ou un texte (`text`). Par défaut, trois champs au plus — ceux que le site demande à un visiteur ; `tous_les_champs` rend la liste entière, pour une agence qui complète ses fiches.","body":{"mode":"raw","raw":"{\n  \"listing\": {\n    \"commune\": {\n      \"nom\": \"Villeneuve-sur-Fixture\",\n      \"code_postal\": \"99999\"\n    },\n    \"property\": {\n      \"type_bien\": \"appartement\",\n      \"surface_m2\": 65,\n      \"nb_pieces\": 3\n    }\n  },\n  \"tous_les_champs\": true\n}","options":{"raw":{"language":"json"}}}},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));","const c = pm.response.json();","pm.test('billed et mode présents', () => { pm.expect(c).to.have.property('billed'); pm.expect(c).to.have.property('mode'); });"]}}]},{"name":"Ce qui manque à un texte d'annonce","request":{"method":"POST","header":[{"key":"Authorization","value":"Bearer {{cle_api}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/listings/completeness","host":["{{base_url}}"],"path":["v1","listings","completeness"]},"description":"Le texte est lu par le parseur puis jeté.","body":{"mode":"raw","raw":"{\n  \"text\": \"Maison 5 pièces 128 m² sur 250 m² de terrain, Chapelle-en-Vaudre (61180). 499 000 €. Année 1930, pompe à chaleur.\",\n  \"source_domain\": \"exemple.fr\"\n}","options":{"raw":{"language":"json"}}}},"response":[],"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('200', () => pm.response.to.have.status(200));","const c = pm.response.json();","pm.test('billed et mode présents', () => { pm.expect(c).to.have.property('billed'); pm.expect(c).to.have.property('mode'); });"]}}]}]},{"name":"Sans clé","item":[{"name":"Sans Authorization → 401 invalid_key","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/v1/account","host":["{{base_url}}"],"path":["v1","account"]},"description":"La porte : toute clé absente, mal formée ou révoquée reçoit la même réponse."},"event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test('401 invalid_key', () => { pm.response.to.have.status(401); pm.expect(pm.response.json().error.code).to.eql('invalid_key'); });"]}}],"response":[]}]}]}