Aller au contenu
BusCitoyens
API · RDEX+ v2

Intégrez les trajets Bus Citoyens

Cahier d'intégration de l'API d'interopérabilité covoiturage au standard RDEX+. En lisant cette page, vous pouvez faire votre premier appel et afficher des trajets — sans rien demander à personne.

Présentation

Ce qu'expose l'API RDEX+ de Bus Citoyens et à qui elle s'adresse.

Bus Citoyens expose ses trajets de covoiturage de proximité via une API au standard RDEX+ (Regional Data Exchange), porté par la Fabrique des Mobilités. Objectif : permettre à d'autres opérateurs (PassPass, Mobicoop…) d'afficher les trajets Bus Citoyens dans leurs propres outils de recherche, et inversement.

Cette première version (v2) est en lecture seule : un seul point d'entrée, GET /journeys, qui renvoie des trajets anonymisés. La mise en relation se fait toujours via Bus Citoyens, en suivant l'URL publique du trajet (webUrl).

  • Service non marchand : tous les trajets sont gratuits (price.type = "free").
  • Trajets réguliers (domicile-travail, scolaires) — jamais ponctuels.
  • Aucune donnée personnelle renvoyée (voir la section Anonymisation & RGPD).
Architecture : les opérateurs tiers interrogent l'API RDEX+ ; le sérialiseur anonymise les trajets PostgreSQL avant réponse.

Démarrage rapide

Un premier appel qui renvoie un résultat, en moins de cinq minutes.

Trois étapes pour obtenir votre premier résultat :

  • Demandez une clé d'API à api@buscitoyens.fr.
  • Appelez GET https://buscitoyens.fr/api/rdex/v2/journeys en passant la clé dans le header api_key et les coordonnées de départ / arrivée.
  • Suivez le champ webUrl de chaque trajet pour la mise en relation.
curl
curl -s -G "https://buscitoyens.fr/api/rdex/v2/journeys" \
  -H "api_key: $BUSCITOYENS_API_KEY" \
  --data-urlencode "departureLat=49.8941" \
  --data-urlencode "departureLng=2.2958" \
  --data-urlencode "arrivalLat=49.8463" \
  --data-urlencode "arrivalLng=2.4901" \
  --data-urlencode "departureRadius=10" \
  --data-urlencode "arrivalRadius=10"

Réponse type :

200 OK
{
  "journeys": [
    {
      "id": "cmpx9k2a10001g8ab12cd34ef",
      "operator": "Bus Citoyens",
      "operatorUrl": "https://buscitoyens.fr",
      "webUrl": "https://buscitoyens.fr/trajets/cmpx9k2a10001g8ab12cd34ef",
      "type": "planned",
      "carpoolerType": "driver",
      "availableSeats": 3,
      "from": { "latitude": 49.8463, "longitude": 2.4901, "city": "Marcelcave", "postalCode": "80800", "country": "FR" },
      "to": { "latitude": 49.8941, "longitude": 2.2958, "city": "Amiens", "postalCode": "80000", "country": "FR" },
      "distance": 15230,
      "duration": 1096,
      "frequency": "regular",
      "isRoundTrip": 1,
      "isStopped": 0,
      "outward": {
        "departureDate": 1768464000,
        "regularSchedule": [{ "mondayTime": "07:45:00", "mondayTimeDelta": 900, "tuesdayTime": "07:45:00", "tuesdayTimeDelta": 900 }],
        "timeDelta": 900
      },
      "return": {
        "departureDate": 1768464000,
        "regularSchedule": [{ "mondayTime": "17:30:00", "mondayTimeDelta": 900 }],
        "timeDelta": 900
      },
      "price": { "type": "free" },
      "user": { "id": "u_3f9a1c2b7d4e5f6a8b9c0d1e", "alias": "Membre Bus Citoyens" }
    }
  ],
  "nbJourneys": 1
}

Coordonnées obligatoires

Les quatre coordonnées (departureLat, departureLng, arrivalLat, arrivalLng) sont requises. Sans elles, l'API renvoie une erreur 400.

Authentification

Clé d'API transmise dans le header api_key.

Chaque requête doit présenter une clé d'API valide dans le header HTTP api_key (schéma apiKeyAuth de RDEX+) :

Header
api_key: bcrk_votre_cle_ici
  • Une clé est propre à un opérateur. Ne la partagez pas, ne la committez pas.
  • Côté serveur, seule l'empreinte SHA-256 de la clé est stockée — la clé en clair n'existe qu'une fois, à sa création.
  • 401 access_denied : clé absente ou inconnue. 403 insufficient_permissions : clé révoquée.

Gardez la clé côté serveur

N'exposez jamais la clé dans un front-end public (JavaScript navigateur, application mobile). Faites transiter les appels par votre backend.

Référence API

Documentation interactive (OpenAPI 3) — essayez les requêtes en direct.

La référence ci-dessous est générée depuis la spécification OpenAPI servie sur /api/rdex/v2/openapi.json (également disponible en YAML). Elle est importable telle quelle dans Postman ou Insomnia.

Chargement de la référence interactive…

Modèle de données

L'objet Journey et ses sous-objets (Geopoint, Schedule, WeekSchedule…).

Une réponse renvoie { journeys: Journey[], nbJourneys: number }. Chaque Journey se compose des sous-objets suivants :

Composition de l'objet Journey.
ChampTypeDescription
idstringIdentifiant du trajet chez Bus Citoyens.
webUrlstringFiche publique du trajet (canal de mise en relation).
carpoolerTypeenumdriver | passenger | both.
from / toGeopointlatitude, longitude, city, postalCode, country.
durationint (s)Durée estimée (approximation, voir FAQ).
distanceint (m)Distance estimée à vol d'oiseau (indicatif).
frequencyenumToujours « regular » chez Bus Citoyens.
isRoundTrip0 | 11 si un retour est proposé.
isStopped0 | 11 si le trajet est complet/suspendu.
outward / returnScheduledepartureDate (UNIX UTC), regularSchedule[], timeDelta.
pricePriceToujours { type: "free" }.
userCarpoolerid (pseudonyme opaque) + alias générique.

Les horaires (mondayTimesundayTime) sont des heures locales (Europe/Paris) au format HH:MM:SS ; seuls les jours desservis sont renseignés. Le champ departureDate est un timestamp UNIX UTC marquant le début de validité du trajet.

Flux & gestion d'erreurs

Déroulé nominal et conduite à tenir en cas d'erreur réseau, timeout ou 5xx.

Déroulé nominal d'une intégration côté opérateur tiers :

Du visiteur sur un opérateur tiers à la fiche Bus Citoyens.

Conduite à tenir selon la réponse :

CasConduite
Timeout / erreur réseauRéessayer avec un backoff exponentiel (max 3 fois).
5xxErreur transitoire côté serveur : réessayer avec backoff.
429Quota atteint : attendre la durée indiquée par Retry-After avant de réessayer.
4xx (hors 429)Erreur définitive (clé, paramètre) : corriger la requête, ne pas boucler.
Réessais robustes (JS)
// Réessaie sur 429 (en respectant Retry-After) et sur 5xx (backoff exponentiel).
async function rdexGet(url, apiKey, { retries = 3 } = {}) {
  for (let attempt = 0; attempt <= retries; attempt++) {
    const res = await fetch(url, { headers: { api_key: apiKey } });

    if (res.status === 429) {
      const wait = Number(res.headers.get("Retry-After") ?? 2);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    if (res.status >= 500) {
      await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
      continue;
    }
    return res; // 2xx, ou 4xx définitif (clé invalide, paramètre manquant…)
  }
  throw new Error("RDEX+ indisponible après plusieurs tentatives");
}

Corps d'erreur (toujours la même forme) :

400
{
  "errorCode": "missing_required_query_parameter",
  "errorMessage": "Paramètre obligatoire manquant : arrivalLat"
}

Exemples de code

curl, JavaScript, Python, PHP et TypeScript — copiables tels quels.

Tous les exemples ciblent l'URL de production et lisent la clé dans une variable d'environnement BUSCITOYENS_API_KEY. Remplacez-la par votre clé.

curl
curl -s -G "https://buscitoyens.fr/api/rdex/v2/journeys" \
  -H "api_key: $BUSCITOYENS_API_KEY" \
  --data-urlencode "departureLat=49.8941" \
  --data-urlencode "departureLng=2.2958" \
  --data-urlencode "arrivalLat=49.8463" \
  --data-urlencode "arrivalLng=2.4901" \
  --data-urlencode "departureRadius=10" \
  --data-urlencode "arrivalRadius=10"
JavaScript / Node.js
const params = new URLSearchParams({
  departureLat: "49.8941",
  departureLng: "2.2958",
  arrivalLat: "49.8463",
  arrivalLng: "2.4901",
  departureRadius: "10",
  arrivalRadius: "10",
});

const res = await fetch(
  `https://buscitoyens.fr/api/rdex/v2/journeys?${params}`,
  { headers: { api_key: process.env.BUSCITOYENS_API_KEY } },
);
if (!res.ok) throw new Error(`RDEX+ ${res.status}`);

const { journeys, nbJourneys } = await res.json();
console.log(`${nbJourneys} trajet(s) trouvé(s)`);
Python 3 (requests)
import os
import requests

resp = requests.get(
    "https://buscitoyens.fr/api/rdex/v2/journeys",
    headers={"api_key": os.environ["BUSCITOYENS_API_KEY"]},
    params={
        "departureLat": 49.8941,
        "departureLng": 2.2958,
        "arrivalLat": 49.8463,
        "arrivalLng": 2.4901,
        "departureRadius": 10,
        "arrivalRadius": 10,
    },
    timeout=10,
)
resp.raise_for_status()
data = resp.json()
print(data["nbJourneys"], "trajet(s)")
PHP 8 (cURL)
<?php
$query = http_build_query([
  'departureLat' => 49.8941,
  'departureLng' => 2.2958,
  'arrivalLat' => 49.8463,
  'arrivalLng' => 2.4901,
  'departureRadius' => 10,
  'arrivalRadius' => 10,
]);

$ch = curl_init("https://buscitoyens.fr/api/rdex/v2/journeys?$query");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['api_key: ' . getenv('BUSCITOYENS_API_KEY')],
  CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status !== 200) {
  throw new RuntimeException("RDEX+ erreur $status");
}
$data = json_decode($body, true);
echo $data['nbJourneys'], " trajet(s)\n";
TypeScript (types depuis l'OpenAPI)
// Types alignés sur la spec OpenAPI (générables via openapi-typescript).
interface Geopoint {
  latitude: number;
  longitude: number;
  city?: string;
  postalCode?: string;
  country?: string;
}
interface Journey {
  id: string;
  operator: string;
  webUrl?: string;
  carpoolerType: "driver" | "passenger" | "both";
  from: Geopoint;
  to: Geopoint;
  duration: number;
  frequency: "punctual" | "regular" | "both";
  price: { type: "free" | "fixed" | "variable" | "unknown" };
  user: { id: string; alias: string };
}
interface JourneysResponse {
  journeys: Journey[];
  nbJourneys: number;
}

export async function searchJourneys(apiKey: string): Promise<JourneysResponse> {
  const params = new URLSearchParams({
    departureLat: "49.8941",
    departureLng: "2.2958",
    arrivalLat: "49.8463",
    arrivalLng: "2.4901",
  });
  const res = await fetch(
    `https://buscitoyens.fr/api/rdex/v2/journeys?${params}`,
    { headers: { api_key: apiKey } },
  );
  if (!res.ok) throw new Error(`RDEX+ ${res.status}`);
  return (await res.json()) as JourneysResponse;
}

Rate limiting

Quota par clé, en-têtes de quota et comportement en 429.

Chaque clé dispose d'un quota par minute (120 req/min par défaut, ajustable par clé). Toutes les réponses portent les en-têtes de quota suivants :

En-têteSignification
X-RateLimit-LimitQuota maximal sur la fenêtre courante.
X-RateLimit-RemainingRequêtes restantes sur la fenêtre.
X-RateLimit-ResetTimestamp UNIX (s) de réinitialisation.
Retry-AfterPrésent en 429 : délai conseillé (s) avant un nouvel essai.

Au-delà du quota, l'API renvoie 429 too_many_queries. Respectez Retry-After plutôt que de boucler immédiatement. Pour relever votre quota, écrivez à api@buscitoyens.fr en précisant votre volume d'appels estimé.

Anonymisation & RGPD

Champs renvoyés et jamais renvoyés, choix d'anonymisation, droit à l'oubli.

La réponse RDEX+ est construite par énumération explicite de champs non identifiants : un nouveau champ du modèle interne ne peut pas fuiter par accident.

RenvoyéJamais renvoyé
Coordonnées (lat / lng), ville, code postalLibellé d'adresse précis (n° et rue)
Rôle, places, fréquence, horairesNom, prénom
Commentaire court (≤ 140 caractères)E-mail, téléphone
user.id (pseudonyme opaque non réversible)Identifiant utilisateur réel
user.alias générique « Membre Bus Citoyens »Photo, biographie
webUrl (fiche publique)Plaque, véhicule, données de profil

Pourquoi des coordonnées exactes mais pas l'adresse ?

Le géo-matching exige des coordonnées précises. En revanche, le libellé d'adresse exact (numéro, rue) n'est jamais exposé : un point GPS sans adresse ne permet pas d'identifier nominativement une personne, et la mise en relation passe toujours par Bus Citoyens.
  • Droit à l'oubli : un usager qui passe son trajet en statut PAUSED ou supprime son compte disparaît immédiatement de l'index RDEX+.
  • Journalisation : nous ne stockons que les paramètres de requête (publics) — jamais les réponses renvoyées.
  • Registre de preuve de covoiturage : compatibilité prévue dans une version ultérieure (identifiants de trajet conformes au décret).

Bonnes pratiques d'intégration

Cache, pagination, webUrl, affichage de l'opérateur, pas de scraping.

  • Cache côté client : mettez en cache les réponses quelques minutes ; les trajets réguliers évoluent lentement.
  • Pagination : bornez le nombre de résultats avec count (max 100) et resserrez les rayons (departureRadius / arrivalRadius).
  • Mise en relation : redirigez toujours l'utilisateur vers webUrl — c'est le seul canal de contact (pas de téléphone exposé).
  • Affichez l'opérateur source : indiquez « via Bus Citoyens » (champ operator) à côté de chaque trajet.
  • Pas de scraping : utilisez l'API, pas l'extraction du site web.

Changelog API

Versions et politique de compatibilité.

v2.0.0 — version initiale. Point d'entrée GET /journeys en lecture seule, authentification par clé d'API, sortie anonymisée.

  • Versioning : tout changement cassant donnera une nouvelle version d'URL (/api/rdex/v3/…).
  • Maintenance : chaque version majeure est maintenue au moins 12 mois après la sortie de la suivante.
  • À venir (non disponible) : POST /messages, POST /bookings, intégration du Registre de preuve de covoiturage.

FAQ

Questions fréquentes des intégrateurs.

Mes trajets renvoient des coordonnées mais pas l'adresse, pourquoi ? Pour des raisons RGPD : on expose un point GPS exploitable pour le matching, jamais le libellé d'adresse exact.

Puis-je récupérer le téléphone du conducteur ? Non. Utilisez webUrl : la mise en relation passe par Bus Citoyens.

Combien coûte l'accès ? Gratuit, conformément à l'esprit non marchand de Bus Citoyens.

Un usager veut être retiré de l'index ? Il passe son trajet en PAUSED ou supprime son compte ; il disparaît aussitôt.

Puis-je publier mes trajets sur Bus Citoyens via l'API ? Pas en v2 (ce sera adressé en v3 via POST /journeys / POST /bookings).

Contact

Où poser vos questions et obtenir une clé.

  • Clé d'API & support : api@buscitoyens.fr
  • Discussions autour du standard : forum RDEX+ de la Fabrique des Mobilités.
  • Politique de données : Protection des données.