Accueil / Référence / Migration
Ancien GeoService

Migrer de /services vers l'API v2

Chaque route de l'ancienne API a son équivalent dans /v2. Ce guide donne, route par route, les paramètres à transposer, les réponses à relire et un exemple avant / après.

De /services/… à /v2/… Référence de l'ancienne API ↗
Contexte

Pourquoi migrer

L'ancienne API /services est dépréciée. Elle continue de répondre au même format qu'avant, mais elle n'évoluera plus : aucune nouvelle route, aucun nouveau paramètre. Seuls les correctifs de sécurité et de justesse y sont encore apportés.

Elle sera retirée le 1er avril 2027 : le 31 mars 2027 est le dernier jour où elle répond. Migrez dès maintenant pour ne pas avoir à le faire dans l'urgence.

L'API v2 renvoie les réponses des moteurs dans leur format d'origine, documenté par chacun d'eux, et ouvre des fonctions que l'ancienne API n'offre pas :

Étape préalable

Obtenir votre clé d'API

/services s'appelle sans clé. /v2 en exige une sur chaque appel, transmise dans l'en-tête X-API-Key (ou, à défaut, dans le paramètre api_key). Sans elle, la réponse est 401 unauthorized.

Demandez votre clé à Galigeo, puis suivez la prise en main pour l'envoyer et la protéger : une clé se garde côté serveur, jamais dans le code d'une page web.

Vue d'ensemble

Correspondance des routes

Ancienne routeRoute v2Ce qui change
GET /services/ban/searchGET /v2/geocoding/ban/searchLe chemin et la clé : la réponse est la même
GET /services/ban/reverseGET /v2/geocoding/ban/reverseLe chemin et la clé : la réponse est la même
GET /services/geocodeGET /v2/geocoding/osm/searchParamètres renommés, propriétés réorganisées
GET /services/reverseGET /v2/geocoding/osm/reverseParamètres renommés, propriétés réorganisées
GET /services/directions/routePOST /v2/routing/routeRequête JSON, unités, géométrie encodée
GET /services/directions/isochronePOST /v2/routing/isochroneRequête JSON, durées en minutes
POST /services/directions/matrixPOST /v2/routing/sources_to_targetsPoints {lat, lon}, une cellule par couple

Les exemples ci-dessous envoient la même requête aux deux API ; les réponses sont réelles, abrégées quand elles sont longues (…).

Géocodage BAN

Adresses BAN : changer le chemin, c'est tout

C'est la migration la plus simple. /services/ban/* et /v2/geocoding/ban/* interrogent le même moteur, avec les mêmes paramètres (q, limit, type, postcode, citycode, lat, lon, autocomplete…) et renvoient la même réponse. Seuls changent le préfixe du chemin et la clé d'API.

Avant · /services
curl -G "https://geoservice.example.com/services/ban/search" \
  --data-urlencode "q=87 avenue d'italie, 75013" \
  -d limit=1
Après · /v2
curl -G "https://geoservice.example.com/v2/geocoding/ban/search" \
  -H "X-API-Key: $GEOSERVICE_KEY" \
  --data-urlencode "q=87 avenue d'italie, 75013" \
  -d limit=1

Les deux réponses portent les mêmes champs, aux mêmes valeurs : elles sont décrites dans Géocodage BAN. La v2 y ajoute le géocodage de fichiers CSV, bien plus rapide que des milliers d'appels unitaires.

Géocodage OSM

Géocodage OSM : geocode et reverse

L'ancienne API répondait au format de photon ; la v2 transmet celui de Nominatim. Le plus proche est format=geojson : une FeatureCollection, coordonnées [lon, lat], comme avant. Ajoutez addressdetails=1 pour recevoir l'adresse détaillée.

Paramètres

AvantAprèsRemarque
qqInchangé.
lang=fraccept-language=frLangue des noms.
limitlimitSans lui, 10 résultats au lieu de 15. Ignoré par reverse, qui en renvoie un seul.
lat, lon (géocodage inverse)lat, lonInchangés.
—format=geojson, addressdetails=1À ajouter : sans format, reverse répond en XML.
bbox, countrycodeviewbox + bounded=1, countrycodesDisponibles à nouveau en v2 (voir paramètres communs).

Propriétés d'un résultat

Avant (properties)Après (properties, format=geojson)
osm_idosm_id
osm_type : N, W, Rosm_type : node, way, relation
osm_key, osm_valuecategory, type
namename (et display_name, l'adresse complète)
housenumber, street, postcodeaddress.house_number, address.road, address.postcode
cityaddress.city, sinon town, village ou municipality
state, countryaddress.state, address.country
countrycode : FRaddress.country_code : fr, en minuscules
extent : [minLon, maxLat, maxLon, minLat]bbox de la Feature : [minLon, minLat, maxLon, maxLat], dans un autre ordre, et présent sur tous les résultats

Exemple

Avant · /services
curl -G "https://geoservice.example.com/services/geocode" \
  --data-urlencode "q=Avenue d'Italie 75013 Paris France" \
  -d lang=fr -d limit=1
200 OK
{
  "features": [{
    "geometry": {"type": "Point", "coordinates": [2.3574814, 48.8254891]},
    "type": "Feature",
    "properties": {
      "osm_id": 685719842,
      "osm_type": "W",
      "osm_key": "highway",
      "osm_value": "primary",
      "name": "Avenue d'Italie",
      "street": "Avenue d'Italie",
      "postcode": "75013",
      "country": "France",
      "city": "Paris",
      "countrycode": "FR",
      "extent": [2.3574144, 48.825715, 2.3575502, 48.8252637]
    }
  }],
  "type": "FeatureCollection"
}
Après · /v2
curl -G "https://geoservice.example.com/v2/geocoding/osm/search" \
  -H "X-API-Key: $GEOSERVICE_KEY" \
  --data-urlencode "q=Avenue d'Italie 75013 Paris France" \
  -d format=geojson -d addressdetails=1 \
  -d accept-language=fr -d limit=1
200 OK
{
  "type": "FeatureCollection",
  "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
  "features": [{
    "type": "Feature",
    "properties": {
      "place_id": 256245,
      "osm_type": "way",
      "osm_id": 685719842,
      "place_rank": 26,
      "category": "highway",
      "type": "primary",
      "importance": 0.0533433333333333,
      "addresstype": "road",
      "name": "Avenue d'Italie",
      "display_name": "Avenue d'Italie, Maison-Blanche, Paris 13e Arrondissement, Paris, 75013, France",
      "address": {
        "road": "Avenue d'Italie",
        "quarter": "Maison-Blanche",
        "suburb": "Paris 13e Arrondissement",
        "city": "Paris",
        "postcode": "75013",
        "country": "France",
        "country_code": "fr"
      }
    },
    "bbox": [2.3574144, 48.8252637, 2.3575502, 48.825715],
    "geometry": {"type": "Point", "coordinates": [2.3574814, 48.8254891]}
  }]
}

Le géocodage inverse suit la même correspondance : /services/reverse?lat=…&lon=…&lang=fr devient /v2/geocoding/osm/reverse?lat=…&lon=…&format=geojson&accept-language=fr, qui renvoie une FeatureCollection d'un seul résultat, avec l'adresse détaillée par défaut. Un point sans résultat n'y renvoie pas une collection vide, mais {"error": "Unable to geocode"}, toujours en 200.

Itinéraires

Itinéraire : directions/route

L'ancienne API répondait au format de GraphHopper ; la v2 transmet celui de Valhalla. La requête devient un objet JSON envoyé en POST.

Paramètres

Avant (paramètres d'URL)Après (corps JSON)Remarque
point=lat,lon, répétélocations: [{"lat", "lon"}, …]Même ordre de passage.
vehicule (ou vehicle) : car, foot, bikecosting : auto, pedestrian, bicycleObligatoire en v2 ; il n'y a plus de profil par défaut.
locale=frdirections_options.language: "fr-FR"Sans elle, les instructions sont en anglais (en-US).
—directions_options.units: "kilometers"Le défaut ; miles possible.
points_encoded—La géométrie est toujours une polyligne encodée, voir ci-dessous.

Réponse

AvantAprèsConversion
paths[0].distancetrip.summary.lengthMètres → kilomètres
paths[0].timetrip.summary.timeMillisecondes → secondes
paths[0].bboxtrip.summary.min_lon, min_lat, max_lon, max_latQuatre champs au lieu d'une liste
paths[0].pointstrip.legs[].shapeUne polyligne de précision 6 par étape, à décoder puis mettre bout à bout ; ou du GeoJSON avec "format": "osrm" et "shape_format": "geojson"
paths[0].instructions[]trip.legs[].maneuvers[]Une liste par étape
textinstructionMême texte
distance (m), time (ms)length (km), time (s)Mêmes conversions que le résumé
interval: [début, fin]begin_shape_index, end_shape_indexIndex dans la géométrie de l'étape
street_namestreet_names[0]Absent quand la voie n'a pas de nom
sign (−7 à 7)type (0 à 43)Autre nomenclature : voir les types de manœuvre de Valhalla
snapped_waypointsPremier et dernier point de chaque shapeLes points recalés sur la route
weight, hints, infotrip.summary.costhints et info n'ont pas d'équivalent

Exemple

Avant · /services
curl -G "https://geoservice.example.com/services/directions/route" \
  -d point=48.855199,2.354035 \
  -d point=48.853138,2.349185 \
  -d vehicule=car -d locale=fr \
  -d points_encoded=false
200 OK · réponse abrégée
{
  "hints": {},
  "paths": [{
    "instructions": [
      {"distance": 15.0, "sign": 0, "interval": [0, 2], "text": "Conduisez vers le nord-ouest sur Rue de l'Hôtel de Ville.", "time": 5663, "street_name": "Rue de l'Hôtel de Ville"},
      {"distance": 79.0, "sign": 2, "interval": [2, 8], "text": "Tournez à droite dans Rue de Brosse.", "time": 34211, "street_name": "Rue de Brosse"},
      …
      {"distance": 0.0, "sign": 4, "interval": [75, 75], "text": "Vous êtes arrivé à votre destination.", "time": 0, "street_name": ""}
    ],
    "distance": 1185.0,
    "time": 321450,
    "bbox": [2.349207, 48.853633, 2.356614, 48.856957],
    "points_encoded": false,
    "points": {"type": "LineString", "coordinates": [[2.353976, 48.855143], [2.353882, 48.855185], …]},
    "snapped_waypoints": {"type": "LineString", "coordinates": [[2.353976, 48.855143], [2.349207, 48.853633]]},
    …
  }],
  "info": {"took": 96, "copyrights": ["Valhalla", "OpenStreetMap contributors"]}
}
Après · /v2
curl "https://geoservice.example.com/v2/routing/route" \
  -H "X-API-Key: $GEOSERVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locations": [
      {"lat": 48.855199, "lon": 2.354035},
      {"lat": 48.853138, "lon": 2.349185}
    ],
    "costing": "auto",
    "directions_options": {"units": "kilometers", "language": "fr-FR"}
  }'
200 OK · réponse abrégée
{
  "trip": {
    "legs": [{
      "maneuvers": [
        {"type": 2, "instruction": "Conduisez vers le nord-ouest sur Rue de l'Hôtel de Ville.", "time": 5.663, "length": 0.015, "begin_shape_index": 0, "end_shape_index": 2, "street_names": ["Rue de l'Hôtel de Ville"], …},
        {"type": 10, "instruction": "Tournez à droite dans Rue de Brosse.", "street_names": ["Rue de Brosse"], …},
        …
        {"type": 4, "instruction": "Vous êtes arrivé à votre destination.", "time": 0.0, "length": 0.0, "begin_shape_index": 75, "end_shape_index": 75, …}
      ],
      "shape": "me{d|AortnCsAzDcA~CaB{AuNaM}BHkBBwIaEyAeEGcDmAyh@…"
    }],
    "summary": {
      "time": 321.45, "length": 1.185, "cost": 731.776,
      "min_lat": 48.853633, "min_lon": 2.349207, "max_lat": 48.856957, "max_lon": 2.356614,
      …
    },
    "units": "kilometers",
    "language": "fr-FR",
    …
  }
}

Le même trajet : 1 185 m en 321 450 ms d'un côté, 1,185 km en 321,45 s de l'autre. Les instructions sont désormais données par étape (legs) : avec trois points ou plus, mettez bout à bout les manœuvres et les géométries de chaque étape.

Itinéraires

Isochrone : directions/isochrone

Paramètres

Avant (paramètres d'URL)Après (corps JSON)Remarque
point=lat,lonlocations: [{"lat", "lon"}]Un seul point.
vehicule (ou vehicle)costingMêmes valeurs que pour l'itinéraire.
time_limit, en secondes (600 par défaut)contours: [{"time": …}], en minutestime_limit=120 devient {"time": 2}. Pas de valeur par défaut.
distance_limit, en mètrescontours: [{"distance": …}], en kilomètresdistance_limit=1500 devient {"distance": 1.5}.
buckets=nn contoursÉcrivez chaque seuil : time_limit=600&buckets=3 devient [{"time": 3.33}, {"time": 6.67}, {"time": 10}].
—polygons: trueÀ ajouter pour recevoir des polygones, comme avant.

Réponse

La liste polygons devient une FeatureCollection GeoJSON standard (features). Chaque contour porte son seuil dans properties.contour (en minutes ou en kilomètres) au lieu d'un rang bucket, et l'ordre s'inverse : le plus grand contour vient en premier, là où bucket: 0 était le plus petit. copyrights disparaît.

Avant · /services
curl -G "https://geoservice.example.com/services/directions/isochrone" \
  -d point=48.853401,2.348792 \
  -d time_limit=120 -d vehicule=foot
200 OK
{
  "polygons": [{
    "geometry": {
      "coordinates": [[
        [2.349792, 48.855144], [2.347792, 48.854954], [2.346638, 48.853554],
        [2.348792, 48.851993], [2.350097, 48.852401], [2.351211, 48.853401],
        [2.349792, 48.855144]
      ]],
      "type": "Polygon"
    },
    "type": "Feature",
    "properties": {"bucket": 0}
  }],
  "copyrights": ["Valhalla", "OpenStreetMap contributors"]
}
Après · /v2
curl "https://geoservice.example.com/v2/routing/isochrone" \
  -H "X-API-Key: $GEOSERVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locations": [{"lat": 48.853401, "lon": 2.348792}],
    "costing": "pedestrian",
    "contours": [{"time": 2}],
    "polygons": true
  }'
200 OK
{
  "features": [{
    "properties": {
      "contour": 2.0, "metric": "time",
      "color": "#bf4040", "fill": "#bf4040", "fillColor": "#bf4040",
      "opacity": 0.33, "fill-opacity": 0.33, "fillOpacity": 0.33
    },
    "geometry": {
      "coordinates": [[
        [2.349792, 48.855144], [2.347792, 48.854954], [2.346638, 48.853554],
        [2.348792, 48.851993], [2.350097, 48.852401], [2.351211, 48.853401],
        [2.349792, 48.855144]
      ]],
      "type": "Polygon"
    },
    "type": "Feature"
  }],
  "type": "FeatureCollection"
}

Le polygone est le même ; seule l'enveloppe change. Comme avant, chaque contour n'a qu'un polygone : c'est l'effet de denoise, qui vaut 1 par défaut. Abaissez-le pour garder aussi les îlots.

Itinéraires

Matrice : directions/matrix

Requête

Avant (corps JSON)Après (corps JSON)Remarque
from_points: [[lon, lat], …]sources: [{"lat", "lon"}, …]Attention à l'ordre : la longitude venait en premier, les champs sont désormais nommés.
to_pointstargetsIdem.
points (N×N)sources et targetsDonnez la même liste deux fois.
vehicle : car, foot, bikecosting : auto, pedestrian, bicycleObligatoire en v2.
out_arrays—Durée et distance sont toujours renvoyées.
fail_fast—Une case sans itinéraire est toujours renvoyée, à null.

Réponse

Les tableaux distances et times deviennent une seule grille, sources_to_targets[i][j], dont chaque cellule porte time (secondes, comme avant) et distance (kilomètres, au lieu de mètres entiers). weights n'a pas d'équivalent : utilisez time.

Avant · /services
curl "https://geoservice.example.com/services/directions/matrix" \
  -H "Content-Type: application/json" \
  -d '{
    "from_points": [[2.358052, 48.824101]],
    "to_points": [
      [2.364601, 48.833535],
      [2.374128, 48.815678],
      [2.342628, 48.845737]
    ],
    "vehicle": "car",
    "out_arrays": ["distances", "times"]
  }'
200 OK
{
  "distances": [[1484, 2300, 3028]],
  "times": [[216, 293, 485]],
  "info": {"copyrights": ["Valhalla", "OpenStreetMap contributors"]}
}
Après · /v2
curl "https://geoservice.example.com/v2/routing/sources_to_targets" \
  -H "X-API-Key: $GEOSERVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sources": [{"lat": 48.824101, "lon": 2.358052}],
    "targets": [
      {"lat": 48.833535, "lon": 2.364601},
      {"lat": 48.815678, "lon": 2.374128},
      {"lat": 48.845737, "lon": 2.342628}
    ],
    "costing": "auto"
  }'
200 OK · réponse abrégée
{
  "sources_to_targets": [[
    {"from_index": 0, "to_index": 0, "time": 216, "distance": 1.484, …},
    {"from_index": 0, "to_index": 1, "time": 293, "distance": 2.3, …},
    {"from_index": 0, "to_index": 2, "time": 485, "distance": 3.028, …}
  ]],
  "sources": [{"lat": 48.824082, "lon": 2.357917}],
  "targets": […],
  "units": "kilometers",
  "algorithm": "costmatrix"
}

Mêmes valeurs : 1 484 m deviennent 1,484 km, les durées restent en secondes. Les coordonnées de sources et targets sont celles des points recalés sur la route.

Erreurs

Les erreurs changent de forme

Sur /services, toutes les erreurs arrivent au format du GeoService (application/problem+json), y compris celles du moteur, traduites : un trajet impossible y est un 422 route_impossible. Sur /v2, une erreur du moteur est transmise telle quelle, dans le format du moteur. Un code qui testait route_impossible doit désormais lire error_code.

Avant · /services
422 Unprocessable Content · application/problem+json
{
  "type": "tag:galigeo.com,2026:geoservice:route_impossible",
  "title": "Itinéraire impossible",
  "status": 422,
  "detail": "Un des points est trop loin de toute voie praticable.",
  "instance": "/services/directions/route",
  "code": "route_impossible"
}
Après · /v2
400 Bad Request · format Valhalla
{
  "error_code": 171,
  "error": "No suitable edges near location",
  "status_code": 400,
  "status": "Bad Request"
}

Le même itinéraire, hors des données couvertes, dans les deux API.

Sur /servicesSur /v2
422 route_impossible400 de Valhalla, error_code 170, 171 ou 442 (154 pour des points trop éloignés)
400 invalid_parameter400 du moteur, dans son format (voir erreurs des moteurs)
502 upstream_invalid_responseErreur du moteur, transmise avec son propre code HTTP
404, 405, 413, 503, 504Inchangés, au format du GeoService ; s'y ajoute 401 unauthorized sans clé valide

Pour traiter les deux familles d'erreurs d'un même code, reprenez l'exemple de gestion des erreurs.

Calendrier

Calendrier de la dépréciation

  1. Dépréciation

    /services continue de répondre, au même format, mais n'évolue plus. Ses réponses portent les en-têtes Deprecation, Link et Sunset à partir de leur mise en service : voir les détecter dans votre code.

  2. Annonce du retrait

    La date de retrait est fixée au 1er avril 2027, à 0 h UTC (2 h à Paris). L'en-tête Sunset de chaque réponse la rappelle.

  3. Retrait, le 1er avril 2027

    Les routes /services cessent de répondre. Seule l'API /v2 reste ouverte.

Une question sur votre migration ? Contactez Galigeo, avec l'ancienne requête et l'en-tête X-GGO-Upstream de sa réponse.