Aller au contenu

Gares ferroviaires

L'API publie le référentiel des gares de voyageurs SNCF : 2782 gares, chacune avec son code UIC, sa position et son rattachement administratif. C'est la seule partie de l'API qui n'appartient à personne — pas de groupe, pas de clé, pas de droits.

Ce qui distingue cet endpoint

Reste de l'API Gares ferroviaires
Périmètre /groups/<group_id>/… aucun groupe
Authentification clé d'API ou session requise aucune
Origine de la donnée le GTFS de votre réseau et les remontées terrain le jeu de données ouvert SNCF gares-de-voyageurs
Change quand en continu, pendant le service quand la SNCF révise son jeu de données, et que le référentiel est rechargé à la main

La conséquence à garder en tête : rien ici n'est temps réel, et rien ici n'est propre à votre réseau. C'est un index, fait pour être lu avant tout le reste — typiquement pour désigner par code UIC la gare à laquelle correspond un arrêt de votre réseau.

Les deux endpoints

Endpoint Rôle
GET /api/v4/rail-stations Cherche dans le référentiel par nom ou par proximité
GET /api/v4/rail-stations/<uic> Lit une gare à partir d'un de ses codes UIC

Chercher par nom

q est cherché n'importe où dans le nom de la gare, sans tenir compte de la casse, des accents ni des séparateurs. chateauroux trouve Châteauroux, et saint germain trouve Saint-Germain-en-Laye Bel-Air – Fourqueux. Il n'y a rien à normaliser de votre côté.

const url = new URL("https://api.pysae.com/api/v4/rail-stations")
url.searchParams.set("q", "Châteauroux")

const response = await fetch(url)
if (!response.ok) {
  throw new Error(`Search failed: ${response.status}`)
}

const { items } = await response.json()
for (const station of items) {
  console.log(station.uic, station.name)
}

La réponse porte la page et son total, comme tout endpoint paginé de l'API :

{
  "items": [
    {
      "uic": ["87597005"],
      "name": "Châteauroux",
      "short_label": "CTX",
      "segments_drg": ["A"],
      "lat": 46.809737,
      "lon": 1.699536,
      "insee_code": "36044",
      "source_ref": "0054eed7-a134-4fca-b217-d65e4d2ea153"
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 50
}

Chercher par proximité

near=<latitude>,<longitude> ordonne les résultats par distance croissante et ajoute distance_meters à chacun. radius, en mètres, borne la recherche ; il vaut 10 km par défaut et est ignoré si near est absent.

GET /api/v4/rail-stations?near=48.880185,2.355151&radius=3000 renvoie les gares à moins de 3 km de Paris Gare du Nord, la plus proche d'abord.

Les deux filtres se combinent : q et near ensemble renvoient les gares dont le nom correspond, dans le rayon, ordonnées par distance.

Une gare, plusieurs codes UIC

uic est une liste, et non une valeur unique : c'est le point qui surprend à l'intégration. La SNCF attribue plusieurs codes à une même gare quand plusieurs réseaux la desservent : Paris Gare du Nord en porte trois, Avignon TGV deux. Les conséquences :

  • N'importe lequel des codes d'une gare la résout. GET /api/v4/rail-stations/87271007 et GET /api/v4/rail-stations/87271031 renvoient tous deux Paris Gare du Nord.
  • Un code appartient à une seule gare. Aucun code UIC n'est partagé, donc en résoudre un n'est jamais ambigu.
  • segments_drg est une liste pour la même raison. Le segment de fréquentation DRG (A le plus fréquenté, C le moins) est donné par code, donc une gare à plusieurs codes peut porter plusieurs segments.

Stocker un seul code de votre côté suffit à désigner une gare, à condition de lire uic comme une liste au moment de comparer.

Rattacher un arrêt de votre réseau à une gare

Le rattachement transforme l'index ci-dessus en donnée de service : il désigne, pour un arrêt de votre GTFS, la gare dont il affiche les départs, et il délimite les gares dont les données ferroviaires sont ingérées pour votre réseau. Contrairement au référentiel, cette partie est propre à chaque groupe et portée par la fonctionnalité payante rail_data : sans elle, les endpoints répondent 403 avec le code LOCKED_FEATURE.

GET /api/v4/groups/<group_id>/rail/station-links liste les rattachements du groupe. Chaque lien porte l'arrêt (stop_id, avec stop_name en libellé), la gare (uic) et un indicateur is_orphan : true quand l'arrêt n'existe pas — ou plus — dans le GTFS actuellement publié du groupe. Un lien orphelin n'est jamais rejeté ni supprimé d'office : vos arrêts sont réimportés à chaque GTFS, et le lien doit survivre à un réimport qui fait momentanément disparaître son arrêt. À vous d'afficher l'indicateur et de corriger ou retirer le lien.

Chaque lien porte aussi un ingestion_status, qui dit où en sont les données ferroviaires de la gare : ready quand ses départs sont interrogeables, pending pendant que la gare est en cours d'ingestion, not_found quand aucun train du feed national courant ne la dessert. Rattacher une gare encore absente du magasin ferroviaire répond immédiatement avec pending et prépare ses données en asynchrone — l'affaire de quelques minutes en général ; re-listez les liens jusqu'à ce qu'il passe ready, et affichez « préparation en cours » plutôt qu'un tableau des départs vide entre-temps. not_found n'est pas une erreur : le lien est conservé, et le rafraîchissement quotidien le réévalue à chaque nouveau feed SNCF.

Lire le tableau des départs d'une gare rattachée

C'est ce à quoi sert le rattachement : GET /api/v4/groups/<group_id>/rail/departures?stop_id=<stop_id> renvoie les trains au départ des gares rattachées à cet arrêt de votre réseau, horaires théoriques et temps réel confondus. Même fonctionnalité payante rail_data que les rattachements : sans elle, 403 LOCKED_FEATURE.

L'arrêt est désigné par son stop_id dans votre GTFS — pas par un code UIC. Un arrêt rattaché à plusieurs gares renvoie les départs de toutes, dans une seule liste.

La fenêtre

from et to sont des instants absolus, en ISO 8601 ou en epoch secondes, comme partout ailleurs dans l'API. Les deux bornes sont incluses.

Ce que vous passez Fenêtre appliquée
rien maintenantmaintenant + 1 h
from seul fromfrom + 1 h
to seul to - 1 hto
les deux tel quel, 24 h au maximum

Une fenêtre inversée ou plus large que 24 h est refusée avec un 400 INVALID_RAIL_DEPARTURES_WINDOW : au-delà, vous demandez un catalogue horaire, pas un tableau des départs.

La réponse

Une liste triée par scheduled_departure croissant — l'horaire théorique, celui sur lequel la fenêtre filtre, pour que l'ordre ne bouge pas quand les estimations évoluent.

Champ Rôle
scheduled_departure L'horaire théorique. Toujours présent
estimated_departure L'horaire estimé par le temps réel, null en son absence
delay_seconds L'écart signé entre les deux, négatif pour un train en avance
status scheduled, on_time, delayed, early, canceled, skipped, added
destination Ce qu'il faut afficher : stop_headsign, sinon trip_headsign, sinon le nom du dernier arrêt du train
route_type Le mode, route_type GTFS
platform La voie, si la source la publie
disruptions Les alertes temps réel qui couvrent ce train, sa ligne, la gare ou tout le réseau
service_date Le jour de service (YYYYMMDD), qui n'est pas le jour calendaire pour un train partant après minuit

Deux pièges à connaître :

  • status: scheduled signifie « pas de temps réel », pas « à l'heure ». Quand aucune entité temps réel ne couvre le train, le tableau est servi sur ses seuls horaires théoriques : estimated_departure, delay_seconds restent null et disruptions est vide. L'absence de temps réel dégrade l'affichage, elle ne fait jamais échouer l'appel — et elle se fait train par train, pas tableau par tableau.
  • Aucun filtre par mode. Tous les modes desservant la gare sont renvoyés, autocars de substitution Car TER (route_type 3) compris : ils font partie du service que le voyageur doit voir. Filtrez côté client sur route_type si vous ne voulez que les trains.

platform est optionnel de bout en bout : il vient du platform_code du GTFS de la source, que le feed national SNCF ne publie pas. Attendez-vous à null sur les données SNCF, et n'en faites pas une colonne obligatoire de votre affichage.

Un arrêt rattaché à aucune gare répond 200 avec une liste vide — c'est un état de configuration, pas une erreur. Idem pour une gare dont les données ne sont pas encore ingérées : lisez ingestion_status sur le rattachement pour distinguer « en préparation » de « aucun train ».

Bonnes pratiques

  • Mettez en cache ce que vous lisez. Le référentiel est statique entre deux rechargements manuels ; le réinterroger à chaque action utilisateur n'apporte rien.
  • Indexez sur le code UIC, pas sur le nom. Les noms sont retouchés par la SNCF ; les codes sont l'identifiant stable. source_ref permet de retrouver l'enregistrement d'origine dans le jeu de données source, pour audit.
  • Un code inconnu répond 404, avec le code RAIL_STATION_NOT_FOUND — à distinguer d'une recherche vide, qui est un 200 avec total: 0.
  • Ne mettez pas le tableau des départs en cache comme le référentiel. Les flux temps réel ferroviaires se rafraîchissent toutes les deux minutes environ : interroger departures plus souvent que ça renvoie les mêmes estimations.