{
  "auth": "header X-API-Key",
  "docs": "GET /v1/docs : documentation complète (Markdown) avec règles de traduction des demandes et exemples",
  "endpoints": {
    "GET /v1/leads": "Recherche rapide de plusieurs entreprises dans une zone (données Google Maps : localisation, catégories, site, note, avis ; SIRENE en option avec enrich_sirene=true). Chaque lead contient detail_url.",
    "GET /v1/leads/{id}": "Fiche complète d'une entreprise : emails (site officiel puis annuaires et sites locaux, chacun justifié dans email_sources), téléphones, réseaux sociaux, finances et contacts (nom, prénom, rôle, email, téléphone). id = place_id Google (suivre detail_url), SIREN ou SIRET. Paramètre web_search=false pour ne pas chercher hors du site officiel."
  },
  "examples": [
    "/v1/leads?q=plombier&city=Dunkerque&has_website=false",
    "/v1/leads?q=restaurant&city=Malo-les-Bains&radius_km=3&limit=5&has_website=false&min_reviews=80&min_rating=4.4&has_email=true&has_phone=true",
    "/v1/leads?q=maçon,couvreur,plaquiste,carreleur&city=Dunkerque;Calais&limit=8&has_website=false&min_reviews=10&max_reviews=40&min_rating=4.6&phone_type=mobile",
    "/v1/leads?q=traiteur&city=Dunkerque&has_website=false&sort=rating&limit=5"
  ],
  "params": [
    {
      "doc": "Mot(s)-clé(s) Google Maps séparés par des virgules. Ex : 'plombier' ou 'maçon,couvreur,plaquiste,carreleur'. 10 max.",
      "name": "q",
      "required": "oui",
      "type": "string"
    },
    {
      "doc": "Ville ou code postal. Plusieurs villes séparées par ';' = zone entre ces villes (ex. 'Dunkerque;Calais'). Obligatoire sauf si lat+lng.",
      "name": "city",
      "type": "string"
    },
    {
      "doc": "Centre de la recherche, alternative à city.",
      "name": "lat, lng",
      "type": "number"
    },
    {
      "doc": "Rayon en km. Défaut : 10 pour une ville, distance entre les villes + 5 km pour une zone. Max 50.",
      "name": "radius_km",
      "type": "number"
    },
    {
      "doc": "Nombre de leads voulus. Recommandé : l'API s'arrête dès que la limite est atteinte (beaucoup plus rapide).",
      "name": "limit",
      "type": "int"
    },
    {
      "doc": "Pagination : nombre de résultats à sauter (défaut 0). Pour la page suivante, appeler pagination.next de la réponse (même recherche, offset + limit).",
      "name": "offset",
      "type": "int"
    },
    {
      "doc": "Tri : distance au centre (défaut), meilleure note, plus grand nombre d'avis.",
      "name": "sort",
      "type": "distance|rating|reviews"
    },
    {
      "doc": "false = sans site (une simple page Facebook/Instagram compte comme 'sans site'). true = avec un vrai site.",
      "name": "has_website",
      "type": "bool"
    },
    {
      "doc": "Nombre d'avis Google min / max (bornes incluses).",
      "name": "min_reviews, max_reviews",
      "type": "int"
    },
    {
      "doc": "Note Google min / max sur 5 (bornes incluses).",
      "name": "min_rating, max_rating",
      "type": "number"
    },
    {
      "doc": "true = email trouvé, false = aucun email trouvé. Déclenche le crawl des sites (plus lent). Les emails viennent du site web : sans site, il n'y en a quasiment jamais.",
      "name": "has_email",
      "type": "bool"
    },
    {
      "doc": "free = au moins un email webmail (Gmail, Orange, Free, Hotmail…), domain = au moins un email à un nom de domaine propre.",
      "name": "email_type",
      "type": "free|domain"
    },
    {
      "doc": "true = au moins un téléphone, false = aucun.",
      "name": "has_phone",
      "type": "bool"
    },
    {
      "doc": "mobile = au moins un 06/07, landline = au moins un fixe (01-05, 09). Les numéros du type demandé sont listés en premier.",
      "name": "phone_type",
      "type": "mobile|landline"
    },
    {
      "doc": "true = entreprise retrouvée dans SIRENE (raison sociale, NAF, effectif).",
      "name": "has_siren",
      "type": "bool"
    },
    {
      "doc": "Ne garder que les fiches dont une catégorie Google contient l'un de ces termes (séparés par des virgules, sans tenir compte des majuscules ni des accents). Ex : category=pilates garde 'Studio Pilates' et écarte 'Centre de yoga'. Filtre instantané.",
      "name": "category",
      "type": "string"
    },
    {
      "doc": "Ne garder que les fiches qui mentionnent l'un de ces mots (séparés par des virgules) dans leur nom, leur catégorie ou leur site web. Ex : keyword=reformer. Tous les mots du mot-clé doivent apparaître à 10 mots d'écart au plus, dans n'importe quel ordre (pluriel toléré, petits mots ignorés). Seule la page d'accueil des sites est lue ; une fiche sans site et sans le mot dans son nom est écartée. Chaque lead indique où le mot a été trouvé (keyword_matches).",
      "name": "keyword",
      "type": "string"
    },
    {
      "doc": "Exclut chaînes et franchises (enseignes connues, 50+ salariés, 5+ établissements, même nom plusieurs fois dans la zone, URL de type /agences/).",
      "name": "exclude_chains",
      "type": "bool"
    },
    {
      "doc": "Crawler les sites pour les emails (défaut false : la liste reste rapide ; les emails et contacts sont dans GET /v1/leads/{id}). Forcé à true si has_email ou email_type est utilisé.",
      "name": "enrich_emails",
      "type": "bool"
    },
    {
      "doc": "Rapprochement SIRENE dans la liste (défaut false, pour la rapidité). true = remplit legal_name, siren, siret, naf_code et employees, mais nettement plus lent. Forcé à true par has_siren et exclude_chains. La fiche détaillée GET /v1/leads/{id} contient toujours ces données.",
      "name": "enrich_sirene",
      "type": "bool"
    },
    {
      "doc": "Format de sortie (défaut json). Ignoré avec stream=true.",
      "name": "format",
      "type": "json|csv"
    },
    {
      "doc": "true = réponse en Server-Sent Events : événements 'progress' (phase, percent, eta_seconds…) toutes les 500 ms, puis 'result' (même contenu que la réponse JSON) ou 'error'.",
      "name": "stream",
      "type": "bool"
    }
  ],
  "service": "leadscraper"
}
