{
  "openapi": "3.0.3",
  "info": {
    "title": "API ouverte Estimatiz",
    "version": "1.0.0",
    "description": "Recherche d’adresse et estimation immobilière avec rapport partageable. Sans clé et sans quota applicatif ; capacité et disponibilité du serveur restent finies. Estimatiz est indépendant et non affilié à une administration. MCP est optionnel pour les assistants compatibles. Cette documentation ne garantit aucune découverte automatique par les IA."
  },
  "servers": [
    {
      "url": "https://www.estimatiz.fr",
      "description": "API publique Estimatiz en HTTPS."
    }
  ],
  "security": [],
  "paths": {
    "/api/public/v1/addresses.php": {
      "get": {
        "operationId": "searchAddresses",
        "summary": "Rechercher une adresse",
        "description": "Faire confirmer le résultat par l’utilisateur avant estimation. Une liste vide ne prouve pas l’absence du bien.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 3,
              "maxLength": 200
            },
            "description": "Adresse recherchée, avec commune ou code postal.",
            "example": "146 boulevard Voltaire 75011 Paris"
          }
        ],
        "responses": {
          "200": {
            "description": "Résultats de recherche, éventuellement vides.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddressResponse"
                },
                "examples": {
                  "noResults": {
                    "summary": "Exemple sans résultat ; ne prouve pas l’absence d’un bien.",
                    "value": {
                      "ok": true,
                      "addresses": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "INVALID_JSON : JSON invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "METHOD_NOT_ALLOWED : méthode HTTP non acceptée.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_PARAMETERS, ADDRESS_NOT_FOUND ou NO_COMPARABLES : paramètres invalides, adresse non confirmée ou données insuffisantes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidParameters": {
                    "summary": "Exemple de structure ; le message dépend du champ invalide.",
                    "value": {
                      "ok": false,
                      "error": {
                        "code": "INVALID_PARAMETERS",
                        "message": "Paramètres invalides."
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "SERVER_ERROR : échec interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "DEPENDENCY_UNAVAILABLE ou REPORT_CREATION_FAILED : dépendance indisponible ou échec de création du rapport. Ne pas annoncer un succès complet ni inventer une URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "PAYLOAD_TOO_LARGE : requête trop volumineuse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/estimate.php": {
      "post": {
        "operationId": "estimateProperty",
        "summary": "Calculer une estimation et enregistrer son rapport",
        "description": "Effet d’écriture : crée ou réutilise un rapport partageable non indexable. Aucun nom, e-mail ou téléphone requis. Restituer median, la fourchette low/high, les avertissements et report.url. Le rapport contient les informations complémentaires. En cas d’échec d’enregistrement, ne pas annoncer un succès complet.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estimation calculée et rapport disponible.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstimateResponse"
                }
              }
            }
          },
          "400": {
            "description": "INVALID_JSON : JSON invalide.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "METHOD_NOT_ALLOWED : méthode HTTP non acceptée.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "INVALID_PARAMETERS, ADDRESS_NOT_FOUND ou NO_COMPARABLES : paramètres invalides, adresse non confirmée ou données insuffisantes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidParameters": {
                    "summary": "Exemple de structure ; le message dépend du champ invalide.",
                    "value": {
                      "ok": false,
                      "error": {
                        "code": "INVALID_PARAMETERS",
                        "message": "Paramètres invalides."
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "SERVER_ERROR : échec interne.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "DEPENDENCY_UNAVAILABLE ou REPORT_CREATION_FAILED : dépendance indisponible ou échec de création du rapport. Ne pas annoncer un succès complet ni inventer une URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "reportFailure": {
                    "summary": "Exemple de structure, sans URL inventée.",
                    "value": {
                      "ok": false,
                      "error": {
                        "code": "REPORT_CREATION_FAILED",
                        "message": "Le rapport n’a pas pu être enregistré."
                      }
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "PAYLOAD_TOO_LARGE : requête trop volumineuse.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Address": {
        "type": "object",
        "required": [
          "label",
          "code_voie",
          "voie",
          "commune",
          "cp",
          "no_voie",
          "btq"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "code_voie": {
            "type": "string"
          },
          "voie": {
            "type": "string"
          },
          "commune": {
            "type": "string"
          },
          "cp": {
            "type": "string"
          },
          "no_voie": {
            "type": "string",
            "description": "Numéro sous forme de chaîne ; peut être vide à la recherche, obligatoire pour estimer."
          },
          "btq": {
            "type": "string",
            "description": "Suffixe éventuel : vide, A, B, C, D, E, T, Q, bis, ter ou quater."
          }
        },
        "description": "Adresse retournée par la recherche et confirmée par l’utilisateur. Ne pas inventer les identifiants ni choisir arbitrairement un résultat."
      },
      "EstimateRequest": {
        "type": "object",
        "required": [
          "address",
          "property_type",
          "surface"
        ],
        "properties": {
          "address": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Address"
              },
              {
                "type": "object",
                "properties": {
                  "no_voie": {
                    "type": "string",
                    "pattern": "^[1-9][0-9]{0,4}$"
                  }
                }
              }
            ],
            "description": "Adresse numérotée obligatoire ; demander le numéro si absent de la recherche."
          },
          "property_type": {
            "type": "string",
            "enum": [
              "Appartement",
              "Maison",
              "Local",
              "Autres biens"
            ]
          },
          "surface": {
            "type": "number",
            "minimum": 5,
            "maximum": 500,
            "description": "Surface du bien en m²."
          },
          "rooms": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30
          },
          "rooms_mode": {
            "type": "string",
            "enum": [
              "exact",
              "minimum"
            ],
            "default": "exact",
            "description": "minimum exige un nombre de pièces rooms."
          },
          "location_fallback": {
            "type": "string",
            "enum": [
              "none",
              "nearby_number"
            ],
            "default": "nearby_number",
            "description": "none désactive le recours au numéro voisin ; des ventes exactes peuvent encore permettre une estimation sans géolocalisation confirmée."
          }
        },
        "additionalProperties": false,
        "anyOf": [
          {
            "not": {
              "required": [
                "rooms_mode"
              ],
              "properties": {
                "rooms_mode": {
                  "enum": [
                    "minimum"
                  ]
                }
              }
            }
          },
          {
            "required": [
              "rooms"
            ]
          }
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Code stable exploitable par le client."
              },
              "message": {
                "type": "string",
                "description": "Explication en français, sans détails internes."
              }
            }
          }
        }
      },
      "AddressResponse": {
        "type": "object",
        "required": [
          "ok",
          "addresses"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "addresses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Address"
            }
          }
        }
      },
      "EstimateResponse": {
        "type": "object",
        "required": [
          "ok",
          "estimation",
          "report",
          "address",
          "comparables",
          "location",
          "warnings",
          "sources",
          "methodology_url",
          "generated_at"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "estimation": {
            "type": "object",
            "required": [
              "low",
              "median",
              "high",
              "price_m2",
              "currency",
              "surface"
            ],
            "properties": {
              "low": {
                "type": "number",
                "minimum": 0
              },
              "median": {
                "type": "number",
                "minimum": 0
              },
              "high": {
                "type": "number",
                "minimum": 0
              },
              "price_m2": {
                "type": "number",
                "minimum": 0
              },
              "currency": {
                "type": "string",
                "enum": [
                  "EUR"
                ]
              },
              "surface": {
                "type": "number",
                "minimum": 5,
                "maximum": 500
              }
            },
            "description": "Montants totaux en euros : low, median, high. price_m2 est le prix au m² médian. Même sélection et même calcul que le rapport."
          },
          "report": {
            "type": "object",
            "required": [
              "url"
            ],
            "properties": {
              "url": {
                "type": "string",
                "format": "uri",
                "description": "Rapport enregistré, non indexable et accessible à toute personne possédant le lien. Présenter ce lien avec les montants."
              }
            }
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "comparables": {
            "type": "object",
            "required": [
              "count",
              "date_min",
              "date_max"
            ],
            "properties": {
              "count": {
                "type": "integer",
                "minimum": 1
              },
              "date_min": {
                "type": "string",
                "format": "date"
              },
              "date_max": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "location": {
            "type": "object",
            "required": [
              "status",
              "note"
            ],
            "properties": {
              "status": {
                "type": "string",
                "description": "État de localisation renvoyé par Estimatiz.",
                "enum": [
                  "confirmed",
                  "approximate",
                  "unavailable",
                  "candidate_limit"
                ]
              },
              "note": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "url"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "methodology_url": {
            "type": "string",
            "format": "uri"
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
