● En production Statut API JSON

API Géographique - Clone de Geo API Gouv

Cette API est un clone 100% compatible de geo.api.gouv.fr pour le découpage administratif des communes en France.

✅ Compatibilité Totale

Cette API est interchangeable avec l'API officielle Geo Gouv. Vous pouvez remplacer :

texte
https://geo.api.gouv.fr/communes

par

texte
https://2mnservices.alwaysdata.net/communes

et tout fonctionnera de la même manière.

🚀 Fonctionnalités

📋 Données

🛣️ Endpoints

Tous les endpoints sont identiques à l'API Geo Gouv officielle :

GET /communes

Recherche des communes par nom, code postal ou coordonnées.

Paramètres :

ParamètreTypeDescription
nomstringRecherche par nom de commune (recherche partielle, insensible à la casse)
codePostalstringCode postal de la commune
latnumberLatitude pour recherche géographique
lonnumberLongitude pour recherche géographique
fieldsstringChamps à retourner (séparés par des virgules)
formatstringFormat de sortie : json ou geojson
geometrystringType de géométrie : centre, mairie, contour, bbox
booststringTri par population : population
limitnumberNombre maximum de résultats — non plafonné, voir la section Performance

Champs disponibles :

Disponibilité des contours — à connaître avant de s'appuyer dessus :

CommunesDétail
Contour en base25 822 / 34 876 (74 %)Polygon : 25 561 · MultiPolygon : 261
Sans contour9 054 (26 %)dont Paris (75056), Antoni… : ?geometry=contour répond {"error":"Géométrie non disponible"}

Neutraliser ce manque en important les contours manquants coûterait plusieurs centaines de Mo sur un Atlas M0 (512 Mo) — les contours stockés pèsent déjà ~370 Mo. C'est pourquoi ils ne sont pas projetés par défaut : ?fields=contour ou ?geometry=contour les chargent sur demande explicite uniquement.

Exemples :

bash
# Recherche par nom (tri population par défaut)
curl "https://2mnservices.alwaysdata.net/communes?nom=Paris&limit=5"

# Réponse :
# [
#   {
#     "nom": "Paris",
#     "code": "75056",
#     "codeDepartement": "75",
#     "siren": "217500016",
#     "codeEpci": "200054781",
#     "codeRegion": "11",
#     "codesPostaux": ["75001", "75002", ..., "75116"],
#     "population": 2103778
#   }
# ]

# Recherche par code postal
curl "https://2mnservices.alwaysdata.net/communes?codePostal=75001"

# Recherche par code postal spécifique (arrondissements)
curl "https://2mnservices.alwaysdata.net/communes?codePostal=75011"
# Retourne : Paris avec tous ses arrondissements (75001-75020, 75116)

curl "https://2mnservices.alwaysdata.net/communes?codePostal=69005"
# Retourne : Lyon avec tous ses arrondissements (69001-69009)

curl "https://2mnservices.alwaysdata.net/communes?codePostal=13008"
# Retourne : Marseille avec tous ses arrondissements (13001-13016)

# Recherche avec champs spécifiques
curl "https://2mnservices.alwaysdata.net/communes?nom=Paris&fields=nom,population,surface,centre"

# Format GeoJSON avec géométrie centre
curl "https://2mnservices.alwaysdata.net/communes/92002?format=geojson&fields=centre"

# Réponse :
# {
#   "type": "Feature",
#   "geometry": {
#     "type": "Point",
#     "coordinates": [2.299, 48.75]
#   },
#   "properties": {
#     "nom": "Antony",
#     "code": "92002",
#     ...
#   }
# }

GET /communes/{code}

Récupère les informations d'une commune par son code INSEE.

Exemple :

bash
curl "https://2mnservices.alwaysdata.net/communes/75056"

GET /departements/{code}/communes

Récupère les communes d'un département.

Exemples :

bash
# Communes de Paris (75)
curl "https://2mnservices.alwaysdata.net/departements/75/communes"

# Communes des Hauts-de-Seine (92) avec limite
curl "https://2mnservices.alwaysdata.net/departements/92/communes?limit=10"

Réponse pour le département 92 :

json
[
  {
    "nom": "Antony",
    "code": "92002",
    "codeDepartement": "92",
    "siren": "219200029",
    "codeEpci": "200054781",
    "codeRegion": "11",
    "codesPostaux": ["92160"],
    "population": 64263
  },
  ...
]

GET /epcis/{code}/communes

Récupère les communes d'un EPCI.

Exemple :

bash
curl "https://2mnservices.alwaysdata.net/epcis/200054781/communes"

GET /regions/{code}/communes

Récupère les communes d'une région.

Exemple :

bash
# Communes de l'Île-de-France (11)
curl "https://2mnservices.alwaysdata.net/regions/11/communes?limit=5"

🏛️ Départements

GET /departements

Récupère la liste de tous les départements français.

Paramètres :

ParamètreTypeDescriptionDéfaut
nomstringRecherche par nom (recherche partielle)-
codeRegionstringFiltrer par code région-
fieldsstringChamps à retourner (séparés par des virgules)Tous
limitnumberNombre maximum de résultats1000

Exemples :

bash
# Liste tous les départements
curl "https://2mnservices.alwaysdata.net/departements"

# Recherche par nom
curl "https://2mnservices.alwaysdata.net/departements?nom=seine&limit=3"

# Départements d'une région spécifique
curl "https://2mnservices.alwaysdata.net/departements?codeRegion=11"

# Champs spécifiques
curl "https://2mnservices.alwaysdata.net/departements?fields=code,nom,codeRegion"

Réponse :

json
[
  {
    "code": "01",
    "nom": "Ain",
    "codeRegion": "84"
  },
  {
    "code": "92",
    "nom": "Hauts-de-Seine",
    "codeRegion": "11"
  },
  ...
]

GET /departements/{code}

Récupère les informations d'un département par son code.

Exemple :

bash
# Détails du département 92
curl "https://2mnservices.alwaysdata.net/departements/92"

Réponse :

json
{
  "code": "92",
  "nom": "Hauts-de-Seine",
  "codeRegion": "11"
}

GET /regions/{code}/departements

Récupère les départements d'une région.

Exemple :

bash
# Départements de l'Île-de-France (11)
curl "https://2mnservices.alwaysdata.net/regions/11/departements"

Réponse :

json
[
  {
    "code": "75",
    "nom": "Paris",
    "codeRegion": "11"
  },
  {
    "code": "77",
    "nom": "Seine-et-Marne",
    "codeRegion": "11"
  },
  {
    "code": "78",
    "nom": "Yvelines",
    "codeRegion": "11"
  },
  {
    "code": "91",
    "nom": "Essonne",
    "codeRegion": "11"
  },
  {
    "code": "92",
    "nom": "Hauts-de-Seine",
    "codeRegion": "11"
  },
  {
    "code": "93",
    "nom": "Seine-Saint-Denis",
    "codeRegion": "11"
  },
  {
    "code": "94",
    "nom": "Val-de-Marne",
    "codeRegion": "11"
  },
  {
    "code": "95",
    "nom": "Val-d'Oise",
    "codeRegion": "11"
  }
]

🏛️ Régions

GET /regions

Récupère la liste de toutes les régions françaises.

Paramètres :

ParamètreTypeDescriptionDéfaut
nomstringRecherche par nom (recherche partielle)-
fieldsstringChamps à retourner (séparés par des virgules)Tous
booststringTri par population (population)Par code
limitnumberNombre maximum de résultats100

Exemples :

bash
# Liste toutes les régions
curl "https://2mnservices.alwaysdata.net/regions"

# Recherche par nom
curl "https://2mnservices.alwaysdata.net/regions?nom=ile&limit=3"

# Régions triées par population
curl "https://2mnservices.alwaysdata.net/regions?boost=population&limit=5"

Réponse :

json
[
  {"code": "01", "nom": "Guadeloupe"},
  {"code": "11", "nom": "Île-de-France"},
  {"code": "24", "nom": "Centre-Val de Loire"},
  ...
]

GET /regions/{code}

Récupère les détails d'une région par son code.

Exemple :

bash
curl "https://2mnservices.alwaysdata.net/regions/11"

Réponse :

json
{
  "code": "11",
  "nom": "Île-de-France"
}

GET /regions/{code}/communes

Récupère les communes d'une région.

Exemple :

bash
# Île-de-France (11) avec limite
curl "https://2mnservices.alwaysdata.net/regions/11/communes?limit=5"

🔧 Installation et Configuration

Prérequis

Installation

bash
# Cloner et installer
git clone <repository-url>
cd api-entreprises
npm install

# Configuration
cp .env.example .env
# Éditer .env avec vos paramètres MongoDB

# Importer les données des communes
npm run import:communes

# Enrichir avec les données complètes (codes postaux, SIREN, etc.)
npm run enrich:communes

# Démarrer
npm start

Scripts disponibles

bash
npm run import:communes     # Import initial depuis data.gouv.fr
npm run enrich:communes     # Enrichissement depuis API Geo Gouv
npm run enrich:communes single 75056  # Enrichir une commune spécifique
npm run import:fr:arrondissements  # (Ré)importe 75116 — idempotent
npm run audit:geo           # Audit lecture seule des 101 départements
npm run sync:geo            # Simulation des corrections

🔍 keeping nos données à jour

L'INSEE publie un nouveau millésime|population chaque année et la liste des
communes évolue. L'API Geo Gouv suit ces publications ; une base figée, non.

Audit — lecture seule, aucun risque

bash
npm run audit:geo                    # les 101 départements
npm run audit:geo -- --dep=92        # un département
npm run audit:geo -- --details       # avec exemples de divergence
npm run audit:geo -- --json=audit.json
npm run audit:geo -- --offline       # rejoue le cache sans_APPEL réseau

Le script télécharge le référentiel officiel département par département
(101 requêtes, pas 35 000), le met en cache disque, puis compare champ par
champ avec notre base. Sortie type :

texte
✅ Conformes           : 34 118
Manquantes            : 12
En trop               : 7
⚠️  Divergentes         : 799
Conformité            : 97.7 %

Ce qui est comparé : nom, codesPostaux, population, centre, siren,
codeEpci, codeRegion, codeDepartement.

ℹ️ Le cache .audit-cache-officiel.json (ignoré par git) permet de rejouer
l'audit hors ligne, et de relancer la comparaison sans consommer de quota
auprès du gouv.
ℹ️ L'audit tourne sur Atlas M0, gratuit, qui coupe les connexions après
~1 min. Le script referme et rouvre le lien toutes les 15 unités — sans ça
il échoue vers le 55ᵉ département.

Tolérances assumées

Toutes les divergences ne se corrigent pas, et c'est délibéré :

ChampRèglePourquoi
centreignoré sous 1 kmNos centres viennent de latitudeCentre/longitudeCentre (centre de la boîte englobante), ceux du gouv d'une autre source. Écart médian mesuré : 266 m.
populationignoré sous 50 hab.Millésime INSEE différent de celui en base.

npm run audit:geo:centre détaille cette distribution par distance réelle.

Synchronisation

bash
npm run sync:geo -- --dep=10            # simulation : n'écrit rien
npm run sync:geo -- --dep=10 --apply    # écrit
npm run sync:geo -- --apply --champs=population,codesPostaux

Garde-fous intégrés :

Workflow recommandé :

bash
npm run audit:geo -- --json=avant.json   # état des lieux
npm run sync:geo                          # voir ce qui serait corrigé
npm run sync:geo -- --apply              # appliquer
npm run audit:geo -- --json=apres.json   # vérifier

📊 Performance

Temps de réponse mesurés (34 876 communes, production Alwaysdata) :

RequêteAvant correctionAprès
?codePostal=7500181 ms32 ms
?nom=Paris412 ms249 ms
?nom=A&limit=10016 420 ms79 ms
?nom=A (tous les résultats)abandon (>60 s)13,7 s

Pourquoi ?nom= était catastrophique

Un regex insensible à la casse ({ $regex: 'A', $options: 'i' }) **ne peut pas
utiliser l'index** sur nom : MongoDB fait alors un scan complet de la
collection.

Le coût réel venait d'un champ contour qui était **stocké en base sans être
déclaré dans le schéma Mongoose** : Mongoose le lisait, le transportait, puis
le jetait sans jamais le servir. Documents de 10,5 Ko en moyenne, 241 Ko au
maximum, soit ~370 Mo parcourus à chaque ?nom= (dont 154 Ko pour la seule
commune de Marseille).

La correction : déclarer contour/bbox dans le schéma, et **projeter
explicitement** les champs renvoyés. Le gain mesuré :

texte
sans projection          16 220 ms
projection sans contour   2 594 ms
projection minimale          93 ms

Ce qui reste lent, et pourquoi

?nom=A sans limit renvoie ~20 000 communes : la sérialisation JSON de
plusieurs Mo prend ~14 s. C'est le même comportement que l'API officielle.
Utilisez toujours ?limit= et ?fields= pour les recherches larges.

Cache

TTL de 1 heure (cacheMiddleware(3600)), par URL. Le cache ne s'applique
qu'aux réponses abouties.

Différences de confort assumées avec l'officiel

Point geo.api.gouv.frNotre API
_score de pertinence dans ?nom=Absent — nous trions par population décroissant
404 en HTML404 en JSON {"error": "Commune non trouvé"}
?nom=Paris classe Parisot (0,51) devant Paris (0,43)Paris en premier

🔄 Différences avec l'API Geo Gouv

CaractéristiqueAPI Geo GouvNotre API
Limite d'appels50 req/minIllimité
DisponibilitéEn ligneLocale/Offline
LatenceVariable< 10ms
DonnéesAPI externeMongoDB locale
CacheNonOui (1h)

🧪 Tests

Testez que votre API retourne les mêmes résultats :

bash
# Test 1 : Recherche par nom
curl "https://geo.api.gouv.fr/communes?nom=Paris&limit=1" > official.json
curl "https://2mnservices.alwaysdata.net/communes?nom=Paris&limit=1" > ours.json
diff official.json ours.json

# Test 2 : Départment 92
curl "https://geo.api.gouv.fr/departements/92/communes?limit=3" > official.json
curl "https://2mnservices.alwaysdata.net/departements/92/communes?limit=3" > ours.json
diff official.json ours.json

# Test 3 : Villes à arrondissements (Paris, Lyon, Marseille)
curl "https://geo.api.gouv.fr/communes?codePostal=75011" > official.json
curl "https://2mnservices.alwaysdata.net/communes?codePostal=75011" > ours.json
diff official.json ours.json

🏙️ Villes à arrondissements

⚠️ Les arrondissements n'ont PAS de code INSEE

C'est le point le plus important à connaître avant d'intégrer cette API.
Les arrondissements de Paris, Lyon et Marseille **n'existent que comme code
postal**. Vérifié sur les 46 codes concernés auprès de geo.api.gouv.fr :

CodesCe que c'est vraiment
75001 → 75020Aucun enregistrement (HTTP 404) — pas de 11e arrondissement
75116✅ Seule entité réelle : « Paris 16e Arrondissement »
69001 → 69009❌ De vraies communes du Rhône : 69001=Affoux, 69006=Amplepuis
13001 → 13016❌ De vraies communes : 13001=Aix-en-Provence, 13008=Aurons

La similitude de chiffres entre codes postaux d'arrondissement et codes INSEE
de commune est une coïncidence numérique.

🚫 Ne jamais utiliser un code postal d'arrondissement comme code INSEE.
GET /communes/69006 retourne Amplepuis (population 398), pas le 6e de Lyon.

✅ Utilisation correcte : passer par codePostal

bash
curl "https://2mnservices.alwaysdata.net/communes?codePostal=75011"  # → Paris (75056)
curl "https://2mnservices.alwaysdata.net/communes?codePostal=69006"  # → Lyon (69123)
curl "https://2mnservices.alwaysdata.net/communes?codePostal=13008"  # → Marseille (13055)

La réponse contient la commune avec la liste complète de ses codes postaux
(Paris : 21 codes, Lyon : 9, Marseille : 16). C'est exactement le
comportement de geo.api.gouv.fr.

Le cas particulier 75116

75116 est le seul arrondissement disposant d'un code INSEE propre.
Comme sur l'API officielle, il est servi par code mais absent des listes :

bash
curl "https://2mnservices.alwaysdata.net/communes/75116"
# → {"nom":"Paris 16e Arrondissement","code":"75116",...,"population":159386}

curl "https://2mnservices.alwaysdata.net/communes?codePostal=75116"   # → Paris (75056) uniquement
curl "https://2mnservices.alwaysdata.net/departements/75/communes"     # → Paris uniquement
curl "https://2mnservices.alwaysdata.net/communes?nom=Paris"           # → Paris uniquement

Extension 2mn : rattacher un arrondissement à sa commune

bash
curl "https://2mnservices.alwaysdata.net/communes/75116?fields=nom,code,estArrondissement,codeCommuneMere,numeroArrondissement"
# → {"nom":"Paris 16e Arrondissement","estArrondissement":true,
#    "codeCommuneMere":"75056","numeroArrondissement":16,"code":"75116"}

curl "https://2mnservices.alwaysdata.net/communes/75056?fields=nom,code,estArrondissement"
# → {"nom":"Paris","estArrondissement":false,"code":"75056"}

Ces champs n'existent pas sur l'API officielle : c'est un ajout 2mn, absent
par défaut pour ne pas rompre la compatibilité.

Mise à jour des données

bash
npm run import:fr:arrondissements   # idempotent

🐛 Dépannage

Problème : "codesPostaux vide"

Solution : Lancer l'enrichissement des données

bash
npm run enrich:communes
# Ou pour une ville spécifique :
node scripts/enrich-communes.js single 75056

Problème : "Ville à arrondissements non trouvée"

Solution : Vider le cache après enrichissement

bash
curl "https://2mnservices.alwaysdata.net/api/cache/clear"

Problème : "centre vide"

Cause : Certaines communes n'ont pas de coordonnées dans data.gouv.fr
Solution : Le script d'enrichissement complète automatiquement depuis l'API Geo Gouv


📚 Ressources


📝 Licence

Licence Ouverte / Open Licence version 2.0


🤝 Support

Pour toute question : support@2mn.services