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 :
https://geo.api.gouv.fr/communespar
https://2mnservices.alwaysdata.net/communeset tout fonctionnera de la même manière.
🚀 Fonctionnalités
- ✅ Recherche de communes par nom, code postal, coordonnées géographiques
- ✅ Récupération des détails d'une commune
- ✅ Liste des communes par département, région ou EPCI
- ✅ Liste des départements français
- ✅ Détails d'un département
- ✅ Liste des régions françaises
- ✅ Détails d'une région
- ✅ Support du format GeoJSON
- ✅ Cache intelligent pour des performances optimales
- ✅ Aucune limite d'appel (contrairement à l'API officielle)
- ✅ Données complètes : codes postaux, SIREN, population, surface, etc.
📋 Données
- Nombre de communes : ~35 000
- Source : API Geo Gouv officielle (synchronisées)
- Mise à jour : Données complètes importées (codes postaux, SIREN, géométries, etc.)
🛣️ 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ètre | Type | Description |
|---|---|---|
nom | string | Recherche par nom de commune (recherche partielle, insensible à la casse) |
codePostal | string | Code postal de la commune |
lat | number | Latitude pour recherche géographique |
lon | number | Longitude pour recherche géographique |
fields | string | Champs à retourner (séparés par des virgules) |
format | string | Format de sortie : json ou geojson |
geometry | string | Type de géométrie : centre, mairie, contour, bbox |
boost | string | Tri par population : population |
limit | number | Nombre maximum de résultats — non plafonné, voir la section Performance |
Champs disponibles :
-
code: Code INSEE de la commune -
nom: Nom de la commune -
codesPostaux: Liste des codes postaux -
population: Population municipale -
surface: Surface en km² -
siren: Code SIREN -
centre: Coordonnées du centre (GeoJSON Point) -
mairie: Coordonnées de la mairie (GeoJSON Point) -
contour: Contour géographique (GeoJSON Polygon/MultiPolygon) — 9 054 communes n'en ont pas, voir ci-dessous -
bbox: Boîte englobante -
zone: Zone -
departement: Département (objet avec code et nom) -
region: Région (objet avec code et nom) -
epci: EPCI (objet avec code et nom) -
estArrondissement(extension 2mn) :truesi l'entité est un arrondissement ; expose alors aussicodeCommuneMereetnumeroArrondissement
Disponibilité des contours — à connaître avant de s'appuyer dessus :
| Communes | Détail | |
|---|---|---|
| Contour en base | 25 822 / 34 876 (74 %) | Polygon : 25 561 · MultiPolygon : 261 |
| Sans contour | 9 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 :
# 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 :
curl "https://2mnservices.alwaysdata.net/communes/75056"GET /departements/{code}/communes
Récupère les communes d'un département.
Exemples :
# 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 :
[
{
"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 :
curl "https://2mnservices.alwaysdata.net/epcis/200054781/communes"GET /regions/{code}/communes
Récupère les communes d'une région.
Exemple :
# 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ètre | Type | Description | Défaut |
|---|---|---|---|
nom | string | Recherche par nom (recherche partielle) | - |
codeRegion | string | Filtrer par code région | - |
fields | string | Champs à retourner (séparés par des virgules) | Tous |
limit | number | Nombre maximum de résultats | 1000 |
Exemples :
# 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 :
[
{
"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 :
# Détails du département 92
curl "https://2mnservices.alwaysdata.net/departements/92"Réponse :
{
"code": "92",
"nom": "Hauts-de-Seine",
"codeRegion": "11"
}GET /regions/{code}/departements
Récupère les départements d'une région.
Exemple :
# Départements de l'Île-de-France (11)
curl "https://2mnservices.alwaysdata.net/regions/11/departements"Réponse :
[
{
"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ètre | Type | Description | Défaut |
|---|---|---|---|
nom | string | Recherche par nom (recherche partielle) | - |
fields | string | Champs à retourner (séparés par des virgules) | Tous |
boost | string | Tri par population (population) | Par code |
limit | number | Nombre maximum de résultats | 100 |
Exemples :
# 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 :
[
{"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 :
curl "https://2mnservices.alwaysdata.net/regions/11"Réponse :
{
"code": "11",
"nom": "Île-de-France"
}GET /regions/{code}/communes
Récupère les communes d'une région.
Exemple :
# Île-de-France (11) avec limite
curl "https://2mnservices.alwaysdata.net/regions/11/communes?limit=5"🔧 Installation et Configuration
Prérequis
- Node.js 14+
- MongoDB (local ou Atlas)
Installation
# 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 startScripts disponibles
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
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éseauLe 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 :
✅ 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é :
| Champ | Règle | Pourquoi |
|---|---|---|
centre | ignoré sous 1 km | Nos centres viennent de latitudeCentre/longitudeCentre (centre de la boîte englobante), ceux du gouv d'une autre source. Écart médian mesuré : 266 m. |
population | ignoré 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
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,codesPostauxGarde-fous intégrés :
-
Simulation par défaut. Rien n'est écrit sans
--apply. -
Jamais d'écrasement par
null. Si le gouv ne renvoie pas la valeur,la donnée existante est conservée.
-
Les codes postaux d'arrondissement sont protégés. Le sync refuse
d'écraser
75001-75020/75116/69001-69009/13001-13016par un code
postal « ville », et journalise le refus. -
code,codeInseeetdepCodesont immuables. -
Aucune suppression implicite.
--purgeest requis pour supprimer lescommunes inconnues du gouv : une commune absente de la réponse officielle
n'est pas forcément obsolète (fusion récente, outre-mer). C'est une décision
métier, pas un automatisme.
Workflow recommandé :
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ête | Avant correction | Après |
|---|---|---|
?codePostal=75001 | 81 ms | 32 ms |
?nom=Paris | 412 ms | 249 ms |
?nom=A&limit=100 | 16 420 ms | 79 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é :
sans projection 16 220 ms
projection sans contour 2 594 ms
projection minimale 93 msCe 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.fr | Notre API |
|---|---|
_score de pertinence dans ?nom= | Absent — nous trions par population décroissant |
| 404 en HTML | 404 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éristique | API Geo Gouv | Notre API |
|---|---|---|
| Limite d'appels | 50 req/min | Illimité |
| Disponibilité | En ligne | Locale/Offline |
| Latence | Variable | < 10ms |
| Données | API externe | MongoDB locale |
| Cache | Non | Oui (1h) |
🧪 Tests
Testez que votre API retourne les mêmes résultats :
# 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 :
| Codes | Ce que c'est vraiment |
|---|---|
75001 → 75020 | Aucun 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/69006retourne Amplepuis (population 398), pas le 6e de Lyon.
✅ Utilisation correcte : passer par codePostal
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 :
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 uniquementExtension 2mn : rattacher un arrondissement à sa commune
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
npm run import:fr:arrondissements # idempotent🐛 Dépannage
Problème : "codesPostaux vide"
Solution : Lancer l'enrichissement des données
npm run enrich:communes
# Ou pour une ville spécifique :
node scripts/enrich-communes.js single 75056Problème : "Ville à arrondissements non trouvée"
Solution : Vider le cache après enrichissement
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