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).
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/journeysen passant la clé dans le headerapi_keyet les coordonnées de départ / arrivée. - Suivez le champ
webUrlde chaque trajet pour la mise en relation.
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 :
{
"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
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+) :
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
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 :
| Champ | Type | Description |
|---|---|---|
id | string | Identifiant du trajet chez Bus Citoyens. |
webUrl | string | Fiche publique du trajet (canal de mise en relation). |
carpoolerType | enum | driver | passenger | both. |
from / to | Geopoint | latitude, longitude, city, postalCode, country. |
duration | int (s) | Durée estimée (approximation, voir FAQ). |
distance | int (m) | Distance estimée à vol d'oiseau (indicatif). |
frequency | enum | Toujours « regular » chez Bus Citoyens. |
isRoundTrip | 0 | 1 | 1 si un retour est proposé. |
isStopped | 0 | 1 | 1 si le trajet est complet/suspendu. |
outward / return | Schedule | departureDate (UNIX UTC), regularSchedule[], timeDelta. |
price | Price | Toujours { type: "free" }. |
user | Carpooler | id (pseudonyme opaque) + alias générique. |
Les horaires (mondayTime…sundayTime) 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 :
Conduite à tenir selon la réponse :
| Cas | Conduite |
|---|---|
| Timeout / erreur réseau | Réessayer avec un backoff exponentiel (max 3 fois). |
| 5xx | Erreur transitoire côté serveur : réessayer avec backoff. |
| 429 | Quota 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é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) :
{
"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 -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"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)`);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
$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";// 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ête | Signification |
|---|---|
X-RateLimit-Limit | Quota maximal sur la fenêtre courante. |
X-RateLimit-Remaining | Requêtes restantes sur la fenêtre. |
X-RateLimit-Reset | Timestamp UNIX (s) de réinitialisation. |
Retry-After | Pré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 postal | Libellé d'adresse précis (n° et rue) |
| Rôle, places, fréquence, horaires | Nom, 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 ?
- Droit à l'oubli : un usager qui passe son trajet en statut
PAUSEDou 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.