Démarrage rapide
Lancez-vous en 3 étapes :
1. Obtenir une clé API
Inscrivez-vous sur portal.napspan.com pour obtenir une clé API gratuite (essai de 14 jours, 1 000 requêtes/jour).
2. Effectuer votre première requête
curl -H "X-API-Key: YOUR_API_KEY" \ "https://api.napspan.com/api/v1/events?jurisdiction=FR&limit=5"
3. Analyser la réponse
{
"data": [
{
"id": "fr-evt-a86-001",
"type": "construction",
"sub_type": "roadwork",
"severity": "minor",
"title": "Travaux sur l'A86 Boulogne",
"description": "Voie de droite fermée entre Sortie 14 et Sortie 15...",
"latitude": 48.8407,
"longitude": 2.2386,
"jurisdiction": "FR",
"start_time": "2026-03-23T07:00:00Z"
}
],
"total": 90,
"limit": 5,
"has_more": true
}Spécification OpenAPI
La spécification API complète et lisible par machine — uniquement les endpoints de données (événements, fonctionnalités, carte, analytique, corridor poids lourds, prix des carburants, métadonnées). Utilisez-la pour générer des clients, l'importer dans Postman ou Insomnia, ou parcourir la référence interactive.
Authentification
Toutes les requêtes API nécessitent une clé API. Transmettez-la via l'en-tête X-API-Key (recommandé) ou le paramètre de requête api_key.
curl -H "X-API-Key: sk_live_abc123" https://api.napspan.com/api/v1/events
curl "https://api.napspan.com/api/v1/events?api_key=sk_live_abc123"
Erreurs & limites de débit
L'API retourne des codes de statut HTTP standard. Les en-têtes de limite de débit sont inclus dans chaque réponse.
| Statut | Signification |
|---|---|
| 200 | Succès |
| 400 | Requête incorrecte (paramètres manquants/invalides) |
| 401 | Clé API manquante ou invalide |
| 403 | Limite du plan dépassée (juridiction, restriction de fonctionnalité, essai expiré) |
| 429 | Limite de débit dépassée — réessayez après Retry-After secondes |
| 500 | Erreur serveur — veuillez réessayer ou contacter le support |
Événements
Les événements de circulation incluent les incidents, travaux, fermetures, alertes météo, restrictions et événements spéciaux dans toutes les juridictions actives.
GET /api/v1/events
| Paramètre | Type | Description |
|---|---|---|
| ids | string | Identifiants d'événements séparés par des virgules pour une requête groupée. Lorsque ids est fourni, tous les autres filtres sont ignorés et la pagination est désactivée — la réponse renvoie toujours l'ensemble complet sur une seule page, quel que soit le status. Limité par le max_batch_size du plan. |
| jurisdiction | string | Filtrer par code ISO pays/région (ex. FR, DE, AT) obligatoire sur Free. Interroger un code primaire étend automatiquement la requête à tous les membres de son groupe, le cas échéant. |
| type | string | Filtrer par type d'événement : incident, construction, closure, weather, special_event, advisory, restriction |
| severity | string | Filtrer : minor, moderate, major, critical |
| lat, lng, radius_km | number | Recherche par rayon (ex. lat=48.86&lng=2.35&radius_km=50) |
| bbox | string | Boîte englobante : minLng,minLat,maxLng,maxLat |
| limit | int | Résultats par page (par défaut 100, max selon le plan) |
| offset | int | Décalage de pagination |
curl -H "X-API-Key: $KEY" \ "https://api.napspan.com/api/v1/events?ids=fr-evt-a86-001,de-evt-a9-002,at-evt-a4-015"
# Événements près de Paris, FR curl -H "X-API-Key: $KEY" \ "https://api.napspan.com/api/v1/events?jurisdiction=FR&lat=48.86&lng=2.35&radius_km=100"
import requests resp = requests.get("https://api.napspan.com/api/v1/events", params={ "jurisdiction": "FR", "lat": 48.86, "lng": 2.35, "radius_km": 100 }, headers={"X-API-Key": KEY}) events = resp.json()["data"]
const res = await fetch( `https://api.napspan.com/api/v1/events?jurisdiction=FR&lat=48.86&lng=2.35&radius_km=100`, { headers: { "X-API-Key": KEY } } ); const { data, total } = await res.json();
req, _ := http.NewRequest("GET", "https://api.napspan.com/api/v1/events?jurisdiction=FR&lat=48.86&lng=2.35&radius_km=100", nil) req.Header.Set("X-API-Key", key) resp, _ := http.DefaultClient.Do(req)
{
"data": [
{
"id": "fr-evt-a86-001",
"source_id": "EVT-2026-04521",
"source": "fr",
"jurisdiction": "FR",
"type": "incident",
"severity": "major",
"status": "active",
"title": "Multi-vehicle collision on A86 westbound near Boulogne",
"description": "Two right lanes blocked. Emergency services on scene.",
"affected_roads": ["A86"],
"direction": "westbound",
"lanes_affected": "2 of 4 lanes blocked",
"start_time": "2026-03-29T14:30:00Z",
"end_time": null,
"estimated_end_time": "2026-03-29T17:00:00Z",
"latitude": 48.8407,
"longitude": 2.2386,
"road_class": "motorway",
"last_updated": "2026-03-29T14:45:00Z",
"created_at": "2026-03-29T14:30:00Z"
}
],
"total": 42,
"limit": 100,
"offset": 0,
"has_more": false
}GET /api/v1/events/{id}
Événement unique par ID. Retourne l'événement complet avec tous les champs de métadonnées renseignés.
{
"id": "fr-evt-a86-001",
"source_id": "EVT-2026-04521",
"source": "fr",
"jurisdiction": "FR",
"type": "incident",
"severity": "major",
"status": "active",
"title": "Multi-vehicle collision on A86 westbound near Boulogne",
"description": "Two right lanes blocked. Emergency services on scene.",
"location": { "type": "Point", "coordinates": [2.2386, 48.8407] },
"affected_roads": ["A86"],
"direction": "westbound",
"lanes_affected": "2 of 4 lanes blocked",
"start_time": "2026-03-29T14:30:00Z",
"end_time": null,
"estimated_start_time": null,
"estimated_end_time": "2026-03-29T17:00:00Z",
"source_created_at": "2026-03-29T14:28:55Z",
"source_updated_at": "2026-03-29T14:45:00Z",
"latitude": 48.8407,
"longitude": 2.2386,
"road_class": "motorway",
"metadata": { "vehicles_involved": 3 },
"last_updated": "2026-03-29T14:45:00Z",
"created_at": "2026-03-29T14:30:00Z",
"archived_at": null,
"archive_reason": null
}archive_reason est null tant que l'événement est actif. Lorsqu'un événement est archivé, il porte l'une de ces deux valeurs :
observed— le flux amont a renvoyé une réponse correcte et l'événement n'y figurait plus, nous l'avons donc archivé normalement. C'est le cas habituel.stale_sweep— la voie d'archivage par sondage n'a pas pu retirer l'événement (réponses amont vides ou en erreur) et un balayage de sécurité l'a forcé en archivé après environpoll_interval × sweep_factor(facteur 15 par défaut) sans nouvelle observation. Traitezarchived_atsur ces lignes comme un horodatage « présumé terminé avant », pas comme une fin précise. Un ratiostale_sweepdurablement élevé sur une source indique que le flux amont émet rarement des enregistrements de clôture.
GET /api/v1/events/geojson Starter+
Mêmes paramètres que /events, retourne une FeatureCollection GeoJSON. Utilisable directement avec Leaflet, Mapbox ou tout outil compatible GeoJSON. Content-Type : application/geo+json.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [2.2386, 48.8407] },
"properties": {
"id": "fr-evt-a86-001",
"source": "fr",
"jurisdiction": "FR",
"type": "incident",
"severity": "major",
"status": "active",
"title": "Multi-vehicle collision on A86 westbound near Boulogne",
"affected_roads": ["A86"],
"direction": "westbound",
"start_time": "2026-03-29T14:30:00Z"
}
}
]
}Fonctionnalités
Les fonctionnalités sont des données hors événement : caméras, stations météo, aires de repos, panneaux, stationnement poids lourds, recharge EV, et plus encore. Tous les types partagent le même point de terminaison — spécifiez un type unique, ou utilisez group pour interroger tous les types d'une catégorie en une seule requête (par ex. group=trucking retourne neuf types liés aux poids lourds).
GET /api/v1/features
| Paramètre | Type | Description |
|---|---|---|
| type | string | Type de fonctionnalité (voir Types de fonctionnalités). type ou group est obligatoire. |
| group | string | Identifiant de groupe (voir Groupes de fonctionnalités). Expansion pratique vers tous les types du groupe. |
| jurisdiction | string | Filtrer par code de juridiction. Interroger un code primaire étend automatiquement la requête à tous les membres de son groupe, le cas échéant. |
| active | bool | true pour ne retourner que les fonctionnalités actives |
| lat, lng, radius_km | number | Recherche par rayon |
| bbox | string | Boîte englobante : minLng,minLat,maxLng,maxLat |
| limit, offset | int | Pagination |
Chaque ligne porte un booléen has_details. Si la valeur est true, la source publie davantage de données que la ligne de liste — appelez GET /features/{id}/details (ou groupez via POST /features/details/batch ci-dessous) pour les obtenir. Si false, la ligne de liste contient déjà tout ce que la source expose pour ce couple (source, feature_type).
curl -H "X-API-Key: $KEY" \ "https://api.napspan.com/api/v1/features?type=cameras&jurisdiction=FR&lat=48.86&lng=2.35&radius_km=50"
curl -H "X-API-Key: $KEY" \ "https://api.napspan.com/api/v1/features?group=trucking&jurisdiction=FR"
La forme de properties varie selon le feature_type. Les exemples ci-dessous montrent les champs typiques pour les types les plus courants — choisissez un onglet.
{
"data": [
{
"id": "fr-cam-a86-paris-01",
"source": "fr",
"jurisdiction": "FR",
"feature_type": "cameras",
"name": "A86 Boulogne",
"latitude": 48.8407,
"longitude": 2.2386,
"road_name": "A86",
"direction": "westbound",
"is_active": true,
"properties": {
"url": "https://sytadin.fr/cameras/a86_boulogne.jpg",
"video_url": null
},
"last_updated": "2026-03-29T14:50:00Z",
"has_details": true
}
],
"total": 8120, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "at-wx-a4-vie",
"jurisdiction": "AT",
"feature_type": "weather_stations",
"name": "A4 Vienna Weather Station",
"latitude": 48.2082, "longitude": 16.3738,
"road_name": "A4",
"is_active": true,
"properties": {
"temperature": 12.4,
"wind_speed": 18.2,
"wind_direction": "NW",
"precipitation": "light_rain",
"road_surface": "wet",
"humidity": 78,
"visibility": 12.0
},
"last_updated": "2026-03-29T14:45:00Z",
"has_details": false
}
],
"total": 2150, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "fr-dms-a4-paris-512",
"jurisdiction": "FR",
"feature_type": "signs",
"name": "A4 EB DMS at PR 12",
"latitude": 48.8566, "longitude": 2.4500,
"road_name": "A4",
"direction": "eastbound",
"is_active": true,
"properties": {
"message": "ACCIDENT 5 KM / 2 VOIES FERMÉES / RALENTISSEMENTS",
"posted_at": "2026-03-29T14:35:00Z",
"sign_type": "DMS"
},
"last_updated": "2026-03-29T14:50:00Z",
"has_details": true
}
],
"total": 3120, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "nl-ev-amsterdam-a2-12",
"source": "ndw",
"jurisdiction": "NL",
"feature_type": "ev_charging",
"name": "Fastned Amsterdam Zuid",
"latitude": 52.3399, "longitude": 4.8732,
"is_active": true,
"properties": {
"network": "Fastned",
"connector_types": ["CCS2", "CHAdeMO", "Type2"],
"max_kw": 300,
"port_count": 8,
"access": "public",
"pricing": "€0.69/kWh"
},
"last_updated": "2026-03-15T00:00:00Z",
"has_details": true
}
],
"total": 42100, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "de-bridge-a8-iller",
"source": "bast",
"feature_type": "bridge_clearances",
"name": "A8 Iller Viaduct",
"latitude": 48.21, "longitude": 10.05,
"is_active": true,
"properties": {
"min_vertical_clearance_m": 4.50,
"posting_status": "open",
"weight_limit_tons": null,
"year_built": 1972,
"structure_kind": "prestressed concrete",
"bridge_number": "BAST-7811-A8"
},
"last_updated": "2026-01-15T00:00:00Z",
"has_details": false
}
],
"total": 39800, "limit": 100, "offset": 0, "has_more": true
}{
"data": [
{
"id": "de-tp-a7-soltau",
"jurisdiction": "DE",
"feature_type": "truck_parking",
"name": "A7 Truck Parking Soltau-Ost",
"latitude": 52.99, "longitude": 9.83,
"is_active": true,
"properties": {
"capacity": 62,
"available": 12,
"availability_pct": 19,
"amenities": ["restrooms", "vending", "lighting"],
"observed_at": "2026-03-29T14:30:00Z"
},
"last_updated": "2026-03-29T14:50:00Z",
"has_details": false
}
],
"total": 580, "limit": 100, "offset": 0, "has_more": true
}GET /api/v1/features/types
Retourne chaque feature_type actif avec les compteurs total et actif. Mis en cache 2 minutes. Utile pour limiter une liste déroulante UI aux types réellement disponibles.
[
{ "type": "cameras", "count": 8120, "active_count": 7842 },
{ "type": "weather_stations", "count": 2150, "active_count": 2098 },
{ "type": "signs", "count": 3120, "active_count": 2987 },
{ "type": "ev_charging", "count": 42100, "active_count": 42100 },
{ "type": "bridge_clearances", "count": 39800, "active_count": 39800 }
]GET /api/v1/features/groups
Retourne la taxonomie de 16 groupes de fonctionnalités avec leurs types membres. Utilisez le champ id comme paramètre group sur /features. Mis en cache 1 heure. Voir Groupes de fonctionnalités ci-dessous pour la table complète.
[
{
"id": "imagery",
"name": "Imagery",
"description": "Live traffic camera feeds",
"sort_order": 1,
"feature_types": ["cameras"]
},
{
"id": "trucking",
"name": "Trucking & Commercial Vehicles",
"description": "Truck restrictions, routes, parking, inspections",
"sort_order": 8,
"feature_types": [
"truck_restrictions", "weight_restrictions", "bridge_clearances",
"truck_routes", "freight_corridors", "truck_parking",
"truck_rest_areas", "weigh_stations", "inspection_stations"
]
}
]GET /api/v1/features/{id}/details
Détail chargé à la demande pour une fonctionnalité unique. Certaines sources n'exposent que des champs compacts dans la liste ; ce point de terminaison renvoie l'objet properties complet ainsi qu'un drapeau detail_available et un indicateur cache (hit / miss / none / poll / error). Utilisez le champ has_details de la réponse de liste pour décider si l'appel en vaut la peine.
{
"data": {
"id": "fr-cam-a86-paris-01",
"feature_type": "cameras",
"name": "A86 Boulogne",
"latitude": 48.8407, "longitude": 2.2386,
"properties": {
"url": "https://sytadin.fr/cameras/a86_boulogne.jpg",
"video_url": "https://sytadin.fr/cameras/a86_boulogne.m3u8",
"views": [
{ "name": "Westbound", "url": "https://sytadin.fr/cameras/a86_boulogne_wb.jpg" },
{ "name": "Eastbound", "url": "https://sytadin.fr/cameras/a86_boulogne_eb.jpg" }
]
}
},
"detail_available": true,
"cache": "miss"
}POST /api/v1/features/details/batch
Récupérer les détails de plusieurs fonctionnalités en un seul appel. La mise en cache par ID est identique à celle du point de terminaison unique (basée sur Redis, indexée par ID) — un groupage qui chevauche des appels précédents ne coûte rien pour la portion qui se chevauche. Les succès partiels sont préservés : si une récupération en amont échoue, l'entrée concernée porte cache: "error" sans faire échouer toute la requête ; les ID manquants apparaissent avec not_found: true. La taille du lot est limitée par le max_batch_size du plan.
curl -X POST -H "X-API-Key: $KEY" \ -H "Content-Type: application/json" \ -d '{"ids": ["fr-cam-a86-paris-01", "fr-cam-a4-paris-04", "missing-id"]}' \ "https://api.napspan.com/api/v1/features/details/batch"
{
"count": 3,
"data": [
{
"id": "fr-cam-a86-paris-01",
"data": { "id": "fr-cam-a86-paris-01", "name": "A86 Boulogne", "properties": { "url": "…", "views": ["…"] } },
"detail_available": true,
"cache": "hit"
},
{
"id": "fr-cam-a4-paris-04",
"data": { "id": "fr-cam-a4-paris-04", "name": "A4 Porte de Bercy" },
"detail_available": true,
"cache": "miss"
},
{
"id": "missing-id",
"detail_available": false,
"cache": "",
"not_found": true
}
]
}Corridor poids lourds Pro+
Trouvez toutes les restrictions poids lourds le long d'un itinéraire. Trace un corridor avec marge entre deux points et retourne tous les gabarits de pont, restrictions de poids, routes camions et restrictions qui le croisent. Utilise des requêtes spatiales PostGIS pour un calcul précis sur grand cercle.
GET /api/v1/truck/corridor
| Paramètre | Type | Description |
|---|---|---|
| from_lat obligatoire | number | Latitude d'origine |
| from_lng obligatoire | number | Longitude d'origine |
| to_lat obligatoire | number | Latitude de destination |
| to_lng obligatoire | number | Longitude de destination |
| buffer_km | number | Distance de marge autour du corridor (par défaut 5, max 50 km) |
| height | number | Hauteur du véhicule en mètres — filtre les gabarits inférieurs |
| weight | number | Poids du véhicule en tonnes — filtre les limites de poids inférieures |
| jurisdiction | string | Restreindre à une seule juridiction |
| limit | int | Résultats maximum (par défaut 500, max 2000) |
bridge_clearances, bridges, weight_restrictions, truck_restrictions, truck_routes, freight_corridors, truck_parkingcurl -H "X-API-Key: $KEY" \ "https://api.napspan.com/api/v1/truck/corridor?\ from_lat=48.14&from_lng=11.58&\ to_lat=47.81&to_lng=13.05&\ buffer_km=10"
{
"corridor": {
"from": [48.14, 11.58],
"to": [47.81, 13.05],
"buffer_km": 10,
"distance_km": 142.5
},
"data": [
{
"id": "de-bridge-a8-iller",
"feature_type": "bridge_clearances",
"name": "A8 Iller Viaduct",
"latitude": 48.21,
"longitude": 10.05,
"properties": {
"posting_status": "open",
"year_built": 1972,
"_distance_km": "2.31"
}
},
{
"id": "at-trk-a1-salzburg",
"feature_type": "truck_restrictions",
"name": "Salzburg West",
"properties": {
"restriction": "Section ban for trucks > 7.5 t on weekends",
"_distance_km": "0.45"
}
}
],
"total": 98,
"limit": 500
}GET /api/v1/truck/corridor/geojson Pro+ GeoJSON
Même requête, retourne une FeatureCollection GeoJSON. Utilisez-la pour afficher les résultats du corridor directement sur une carte Leaflet ou Mapbox.
// Afficher le corridor poids lourds sur une carte Leaflet const url = `https://api.napspan.com/api/v1/truck/corridor/geojson` + `?from_lat=48.14&from_lng=11.58&to_lat=47.81&to_lng=13.05&buffer_km=10`; fetch(url, { headers: { "X-API-Key": KEY } }) .then(r => r.json()) .then(geojson => { L.geoJSON(geojson, { pointToLayer: (f, ll) => L.circleMarker(ll, { radius: 6 }), onEachFeature: (f, layer) => { layer.bindPopup(`<b>${f.properties.name}</b><br>${f.properties.feature_type}`); } }).addTo(map); });
import requests resp = requests.get("https://api.napspan.com/api/v1/truck/corridor", params={ "from_lat": 48.14, "from_lng": 11.58, "to_lat": 47.81, "to_lng": 13.05, "buffer_km": 10 }, headers={"X-API-Key": KEY}) for feat in resp.json()["data"]: if feat["feature_type"] == "bridge_clearances": print(f"Pont : {feat['name']} ({feat['properties'].get('posting_status', 'unknown')})")
{
"type": "FeatureCollection",
"corridor": {
"from": [48.14, 11.58],
"to": [47.81, 13.05],
"buffer_km": 10,
"distance_km": 142.5
},
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [10.05, 48.21] },
"properties": {
"id": "de-bridge-a8-iller",
"feature_type": "bridge_clearances",
"name": "A8 Iller Viaduct",
"min_vertical_clearance_m": 4.50,
"posting_status": "open",
"_distance_km": "2.31"
}
}
]
}Analytique Pro+
Analytique historique et tendances pour les événements, les fonctionnalités et la santé des sources. Tous les points de terminaison nécessitent un plan Pro ou Enterprise.
GET /api/v1/analytics/history/{id}
Chronologie du cycle de vie d'un événement unique — création, changements de gravité, changements de voies, changements d'heure de fin, archivage.
[
{ "id": 1, "event_id": "fr-evt-a86-001",
"change_type": "created",
"old_value": null, "new_value": "active",
"changed_at": "2026-03-29T14:30:00Z" },
{ "id": 2, "event_id": "fr-evt-a86-001",
"change_type": "severity_change",
"old_value": "moderate", "new_value": "major",
"changed_at": "2026-03-29T14:35:00Z" }
]GET /api/v1/analytics/changes
Flux des changements récents pour tous les événements. Paginé. Utile pour synchroniser une copie en aval de l'état des événements sans interroger chaque événement individuellement.
{
"data": [
{ "id": 45, "event_id": "fr-evt-a86-001",
"change_type": "severity_change",
"old_value": "moderate", "new_value": "major",
"jurisdiction": "FR",
"event_title": "Multi-vehicle collision on A86 westbound",
"changed_at": "2026-03-29T14:35:00Z" }
],
"total": 128, "limit": 100, "offset": 0, "has_more": true
}GET /api/v1/analytics/clearance
Temps de résolution P50 / P95 / moyens par juridiction et type d'événement. Calculés à partir de start_time → archived_at.
[
{ "jurisdiction": "FR", "event_type": "incident",
"total_events": 320,
"p50_minutes": 42.5, "p95_minutes": 175.0, "avg_minutes": 59.4 },
{ "jurisdiction": "AT", "event_type": "construction",
"total_events": 150,
"p50_minutes": 4320.0, "p95_minutes": 21600.0, "avg_minutes": 7200.0 }
]GET /api/v1/analytics/corridors
Fréquence d'événements et temps moyen de résolution par route. Meilleur signal pour identifier « quel corridor est peu fiable aujourd'hui ».
[
{ "road": "A86", "jurisdiction": "FR",
"total_events": 245,
"avg_clearance_minutes": 52.3,
"incident_count": 180, "construction_count": 65 }
]GET /api/v1/analytics/trends
Totaux journaliers d'événements avec ventilation par type.
[
{ "period": "2026-03-29", "total": 142,
"by_type": { "incident": 58, "construction": 62, "closure": 12, "weather": 10 } },
{ "period": "2026-03-28", "total": 128,
"by_type": { "incident": 45, "construction": 60, "closure": 15, "weather": 8 } }
]GET /api/v1/analytics/hotspots
Clusters géographiques d'événements avec le type et la route dominants par cluster.
[
{ "cluster_id": 1,
"latitude": 48.8407, "longitude": 2.2386,
"event_count": 28, "radius_km": 2.5,
"top_type": "incident", "top_road": "A86" }
]GET /api/v1/analytics/weather
Historique des relevés des stations météo — la base de données utilisée pour la corrélation météo-événement.
[
{ "station_id": "at-wx-a4-vie",
"name": "A4 Vienna Weather Station",
"latitude": 48.2082, "longitude": 16.3738,
"temperature": 12.4, "wind_speed": 18.2, "wind_direction": "NW",
"precipitation": "light_rain", "road_surface": "wet",
"humidity": 78, "visibility": 12.0,
"recorded_at": "2026-03-29T14:45:00Z" }
]Autres points de terminaison analytique
Forme identique aux exemples ci-dessus — schémas complets dans la référence interactive.
| Point de terminaison | Description |
|---|---|
| /analytics/feature-history | Changements du cycle de vie des fonctionnalités (création, désactivation, réactivation). |
| /analytics/event-weather/{id} | Snapshot météo enregistré au moment de la création de l'événement (recherche PostGIS de la station la plus proche). |
| /analytics/weather-correlation | Agrégation de la corrélation entre événements et météo. |
Métadonnées & statut
Points de terminaison légers de catalogue et de statut. /health et /status sont publics (aucune clé API requise) ; les autres sont protégés par clé API mais sans restriction de plan.
GET /api/v1/jurisdictions
Juridictions actives avec code, nom, pays et scope. Par défaut, seules les juridictions géographiques réelles sont renvoyées (scope=state). Utilisez ?scope=federal pour les sources nationales transversales, ?scope=regional pour les flux sous-nationaux, ou ?scope=all pour tout renvoyer.
Lorsque group_id n'est pas null, la juridiction appartient à un groupe (voir /api/v1/jurisdictions/groups ci-dessous). Une requête ?jurisdiction=<primary> sur /events ou /features est transparente : elle est étendue à tous les membres ; pour désactiver, passez un code non principal ou une liste séparée par des virgules. Le déploiement européen n'expose actuellement aucun groupe — toutes les lignes renvoient group_id: null — mais le champ est documenté afin que les clients puissent l'adopter dès que des flux frères apparaîtront.
[
{ "code": "FR", "name": "France", "country": "FR", "scope": "state", "group_id": null, "is_active": true },
{ "code": "DE", "name": "Germany", "country": "DE", "scope": "state", "group_id": null, "is_active": true }
]GET /api/v1/jurisdictions/groups
Faisceaux canoniques pour les juridictions dont le flux est réparti sur plusieurs codes. Le primary_jurisdiction_code est celui à utiliser sur /events et /features : la requête est étendue de manière transparente à toutes les entrées members. Le déploiement européen renvoie actuellement un tableau vide ; le point de terminaison est livré pour que les clients puissent s'y appuyer dès qu'il faudra regrouper Mobilithek ou d'autres sources nationales.
[]
GET /api/v1/stats/summary
Statistiques agrégées détaillées — utilisées par les pages marketing pour des compteurs en direct. Requête plus lourde que /stats ; mise en cache 60 s.
{
"jurisdictions": { "total": 28, "active": 26, "by_country": { "DE": 1, "AT": 1, "FR": 1 } },
"events": {
"active": 3120,
"by_type": { "incident": 820, "construction": 1450, "closure": 280 },
"by_severity": { "minor": 1080, "moderate": 1450, "major": 510, "critical": 80 }
},
"features": { "total": 96450, "by_type": { "cameras": 8120, "ev_charging": 42100 } },
"last_sync": "2026-03-29T14:55:00Z",
"uptime": "72h15m"
}GET /api/v1/fuel-prices
Prix des carburants européens (lorsque des sources licenciées sont disponibles). Avec latest=true, uniquement la dernière observation par jurisdiction + fuel_type.
{
"data": [
{ "jurisdiction": "FR", "country": "FR", "fuel_type": "diesel",
"price": 1.74, "currency": "EUR", "unit": "litre",
"observed_at": "2026-05-05", "source": "DGEC" },
{ "jurisdiction": "DE", "country": "DE", "fuel_type": "diesel",
"price": 1.69, "currency": "EUR", "unit": "litre",
"observed_at": "2026-05-05", "source": "BAFA" }
],
"total": 128, "limit": 100, "offset": 0, "has_more": true
}GET /api/v1/status
Snapshot de statut public — adapté à une page de statut non authentifiée ou à un moniteur d'uptime externe. Compteurs agrégés et ventilation par juridiction avec disponibilité sur 7 jours. Mise en cache 60 s.
{
"overall": "operational",
"generated_at": "2026-05-08T14:00:00Z",
"worker": { "last_seen": "2026-05-08T13:59:50Z", "staleness_seconds": 10, "healthy": true },
"total_resources": 186, "healthy": 175, "warning": 8, "critical": 3,
"jurisdictions": [
{ "code": "FR", "name": "France", "country": "FR",
"status": "operational", "total_resources": 12, "open_circuits": 0,
"uptime_7day_percent": 99.8 }
]
}Webhooks Pro+
Abonnez-vous aux changements d'événements en temps réel. Lorsqu'un événement de circulation est créé, change de gravité ou de statut, ou est archivé, l'API envoie un POST signé HMAC-SHA256 à votre point de terminaison. Les clients Pro et Enterprise configurent leurs abonnements dans le portail — portal.napspan.com/webhooks. Le secret généré automatiquement est affiché une seule fois à la création ; copiez-le immédiatement.
Chaque webhook est filtré par juridiction (obligatoire, sélection multiple — choisissez-en une ou plusieurs), et facultativement par type d'événement, sous-type et gravité (également en sélection multiple). Les filtres sont combinés en ET entre les dimensions ; au sein d'une dimension, le webhook se déclenche dès que l'événement correspond à l'une des valeurs sélectionnées.
Le filtre facultatif sub_type affine au sein d'un type — par exemple, type: ["incident"] + sub_type: ["accident"] ne livre que les accidents, et non les pannes ou les débris. Il est additif : omettez-le (ou envoyez un tableau vide) et vous recevez tous les sous-types, exactement comme auparavant. sub_type est aussi un champ présent sur chaque événement et un paramètre de requête ?sub_type= sur /events. Consultez le vocabulaire complet dans la référence de l'API.
Types d'événements
| Événement | Déclenché lorsque |
|---|---|
event.created | Un nouvel événement de circulation arrive depuis une source nationale de données de mobilité. |
event.severity_change | La gravité d'un événement existant change (ex. moderate → major). |
event.status_change | Le statut bascule (ex. active → archived). |
event.archived | L'événement est terminé. |
test | Envoyé uniquement par le bouton « Tester » du portail — déclenché immédiatement, jamais enregistré dans l'historique des livraisons. |
Charge utile immédiate (par défaut)
Un POST par changement. À utiliser si vous avez besoin de la latence la plus faible possible et que votre récepteur supporte les pics.
{
"event": "event.severity_change",
"timestamp": "2026-03-29T14:35:00Z",
"data": {
"event_id": "fr-evt-a86-001",
"jurisdiction": "FR",
"type": "incident",
"sub_type": "accident",
"severity": "major",
"road_name": "A86",
"changes": {
"old_severity": "moderate",
"new_severity": "major"
}
}
}Charge utile groupée
Définissez batch_window_seconds (1–300) lors de la création du webhook pour collecter les changements pendant cette fenêtre et les livrer en un seul POST. Réduit le volume HTTP pour les destinataires bavards (Slack, Teams, Lambda) au prix d'une latence supplémentaire allant jusqu'à window_seconds.
{
"batch": {
"window_seconds": 60,
"started_at": "2026-03-29T14:30:00Z",
"ended_at": "2026-03-29T14:31:00Z",
"event_count": 2
},
"events": [
{
"event": "event.created",
"timestamp": "2026-03-29T14:30:15Z",
"data": { "event_id": "fr-evt-a4-002", "jurisdiction": "FR", "type": "construction", "sub_type": "roadwork", "severity": "minor", "road_name": "A4" }
},
{
"event": "event.severity_change",
"timestamp": "2026-03-29T14:30:42Z",
"data": { "event_id": "fr-evt-a86-001", "jurisdiction": "FR", "type": "incident", "sub_type": "accident", "severity": "major", "road_name": "A86", "changes": { "old_severity": "moderate", "new_severity": "major" } }
}
]
}Les récepteurs branchent selon la présence de la clé batch :
if ("batch" in body) { // groupé : itérer sur body.events for (const ev of body.events) handle(ev); } else { // immédiat : traiter le body lui-même handle(body); }
Un lot unique est plafonné à 100 événements ; les files d'attente plus longues sont réparties sur plusieurs POST dans la même fenêtre.
Vérification de la signature
Chaque livraison inclut un en-tête X-Webhook-Signature contenant le HMAC-SHA256 (hex, minuscule) du corps de la requête, signé avec votre secret webhook. Rejetez les requêtes dont la signature ne correspond pas.
import crypto from "crypto"; app.post("/napspan-webhook", (req, res) => { const sig = req.headers["x-webhook-signature"]; const expected = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET) .update(req.rawBody) // octets bruts, PAS JSON.stringify(req.body) .digest("hex"); if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) { return res.status(401).end(); } // traiter req.body… res.status(200).end(); });
Fiabilité
Les livraisons en échec sont relancées jusqu'à 3 fois avec un délai exponentiel (10s, 30s, 90s). Après 10 échecs consécutifs, l'abonnement est désactivé automatiquement — réactivez-le depuis le portail une fois votre point de terminaison rétabli. Utilisez le bouton Tester sur chaque ligne du portail pour déclencher immédiatement un événement test et confirmer l'accessibilité avant de dépendre d'un webhook en production.
Routage poids lourds Pro+
Un itinéraire adapté aux poids lourds, de l'origine à la destination — la géométrie de routage est fournie par notre moteur de routage (y compris les restrictions par catégorie de tunnel ADR pour les chargements dangereux), puis enrichie de chaque danger à proximité connu de notre base de données : incidents RTTI/SRTI en direct, travaux programmés, gabarits de ponts filtrés selon la hauteur du camion demandé, restrictions poids lourds et de poids, météo le long du corridor et alertes actives — selon ce que chaque NAP publie comme données sous-jacentes.
POST /api/v1/routing/route
| Champ | Type | Description |
|---|---|---|
| origin obligatoire | object | { "lat": ..., "lng": ... } |
| destination obligatoire | object | { "lat": ..., "lng": ... } |
| truck obligatoire | object | Bloc véhicule — profile (tractor, straight_truck, van), weight_t, height_m, optionnels length_m, width_m, axles, hazmat |
| alternatives | int | Nombre d'itinéraires alternatifs (0–3, par défaut 0) |
| avoid | array | Optionnel : tolls, ferries, tunnels |
| enrichment | object | Ajuste le filtrage des avertissements post-routage — ces champs ne sont PAS envoyés au moteur de routage. Sous-clés : exclude_types, min_severity (info/warning/critical), buffer_m (10–1000, défaut 100), clearance_pad_m (0–1, défaut 0.15), skip_temporal_filter, max_distance_m. |
| cargo | object | Métadonnées de cargaison stockées sur l'itinéraire sauvegardé. Sous-clé : hazmat_class (classe ADR, p. ex. "1.3D"). |
| truck.permit_number | string | Numéro de permis de transport exceptionnel. Renvoyé dans la réponse. |
| customer_route_id | string | Clé de corrélation libre (≤128 caractères). Renvoyée à l'identique. |
| tags | array | Étiquettes libres pour l'analyse flotte/ligne (max. 16 entrées, ≤64 caractères chacune). |
curl -X POST -H "X-API-Key: $KEY" \ -H "Content-Type: application/json" \ https://api.napspan.com/api/v1/routing/route \ -d '{ "origin": { "lat": 52.5200, "lng": 13.4050 }, "destination": { "lat": 48.1351, "lng": 11.5820 }, "truck": { "profile": "tractor", "weight_t": 20, "height_m": 4.0 } }'
{
"route_id": "rt_01HXYZ4K8Q2V7R9N3M5P6T8W0E",
"expires_at": "2026-05-18T14:32:00Z",
"routes": [
{
"summary": {
"distance_km": 585.4,
"duration_min": 352,
"toll_cost_eur": 0.00
},
"geometry": {
"type": "LineString",
"coordinates": [
[13.4050, 52.5200],
[12.3700, 51.3400],
[11.5820, 48.1351]
]
},
"warnings": [
{
"type": "bridge_clearance",
"severity": "critical",
"feature_id": "DE-bridge-A9-K1247",
"name": "Pont sur l'A9 près de Leipzig",
"latitude": 51.3400,
"longitude": 12.3700,
"clearance_m": 4.0,
"truck_height_m": 4.2,
"message": "Le gabarit vertical affiché est inférieur à la hauteur du camion demandée."
}
]
}
]
}Types d'alerte
Chaque entrée de warnings[] porte un type, une severity (info / warning / critical), la distance parcourue le long de l'itinéraire, une heure d'arrivée prévue et un bloc properties spécifique au type. Utilisez l'objet enrichment pour filtrer par gravité ou écarter les types qui ne vous intéressent pas. La disponibilité de chaque type dépend de ce que le NAP publie pour ce corridor.
| Type | Gravité typique | Se déclenche quand |
|---|---|---|
| bridge_clearance | critical | Gabarit affiché < hauteur du camion + clearance_pad_m, ou capacité de charge < poids du camion |
| truck_restriction | critical | Une limite de hauteur / largeur / longueur / poids affichée sur l'itinéraire est inférieure à la valeur du camion (signalée par dimension) |
| weight_restriction | critical | Une limite de poids affichée sur l'itinéraire est inférieure au poids du camion |
| traffic_event | critical–info | Un incident, une fermeture ou des travaux RTTI/SRTI en direct croisent le corridor pendant la fenêtre de trajet |
| future_construction | warning | Des travaux programmés chevauchent l'heure de trajet prévue à ce point de l'itinéraire |
| special_event | info | Une fermeture planifiée ou un événement spécial touche l'itinéraire |
| alert | info | Une alerte générale de juridiction s'applique le long du corridor |
| weather | info | Conditions / alertes météo signalées par des stations à proximité de l'itinéraire |
Les restrictions par catégorie de tunnel ADR sont appliquées par le routeur lui-même (via la catégorie de tunnel et la classe de marchandises dangereuses du camion), de sorte que les tunnels interdits sont évités dans la géométrie de l'itinéraire plutôt que signalés comme une alerte. Activez le canal distinct features[] avec enrichment.include_features pour obtenir également les commodités à proximité le long de l'itinéraire — truck_parking, truck_rest_areas, rest_areas adaptées aux poids lourds, service_plazas et ev_charging. Le nombre de types de commodités demandables à la fois dépend du forfait (Free : aucun ; le plafond augmente selon le palier ; Enterprise : illimité) — votre plafond est publié sous routing_max_feature_types via GET /api/v1/customer/plans. Le dépassement renvoie 403 feature_types_limit.
Planification des temps de conduite
Ajoutez un bloc truck.hos — le compteur de temps de conduite et de repos du conducteur au départ — et la réponse gagne un canal hos[] : les points où le conducteur doit faire une pause ou cesser de conduire selon le régime choisi, chacun accompagné des parkings poids lourds et aires de repos encore atteignables avant cette limite. Il couvre l'UE (règlement 561/2006), les États-Unis (FMCSA) et le Canada (au sud du 60e parallèle), et il est gratuit sur tous les forfaits. Omettez ruleset pour utiliser par défaut la région de votre déploiement (eu ici).
Chaque champ *_remaining_s correspond aux secondes restantes par rapport à cette limite — omettez-en un pour supposer un conducteur reposé. Un profil de camion réutilisable ne devrait stocker que ruleset ; envoyez les compteurs propres au trajet en ligne.
"truck": { "profile": "tractor", "weight_t": 20, "height_m": 4.0, "hos": { "ruleset": "eu", "drive_remaining_s": 32400, "since_break_s": 9000 } }
"hos": [ { "reason": "break_required", "distance_along_route_m": 112000, "projected_time": "2026-05-18T13:30:00Z", "legal_deadline": "2026-05-18T13:30:00Z", "feasible": true, "suggested_stops": [ { "type": "truck_parking", "name": "Autohof Montabaur A3", "distance_along_route_m": 106000 } ] } ]
reason vaut break_required, drive_limit, duty_window ou cycle_limit. Les suggested_stops sont triés de la plus proche de l'échéance en premier, afin que le conducteur exploite le plus de temps de conduite légal avant de s'arrêter ; feasible: false est un vrai signal indiquant qu'aucun stationnement légal n'est atteignable avant cette limite. Un ruleset inconnu renvoie 400 invalid_hos_ruleset.
Itinéraires enregistrés
Chaque appel est enregistré automatiquement pendant 30 minutes sous son route_id. Récupérez-le à tout moment dans cette fenêtre via GET /api/v1/routing/route/saved/{id} pour obtenir des avertissements réévalués sur l'état temps réel — ces relectures ne consomment pas de quota de routage. Pour conserver un itinéraire au-delà des 30 minutes, appelez POST /api/v1/routing/route/saved/{id}/persist ; les itinéraires persistés sont décomptés de la limite de slots de votre plan.
Évitement avancé & détail d'itinéraire
Au-delà d'avoid, la requête accepte des contrôles d'itinéraire plus fins : avoid_areas (bloquer des zones interdites en bbox / cercle / polygone), avoid_zones (environmental, congestion_pricing), avoid_truck_roads, avoid_countries et le plus strict exclude_countries (codes ISO-3), ainsi que details[] pour les attributs routiers par segment (limitations de vitesse, classe fonctionnelle, voies …). Chaque itinéraire porte aussi les notices[] du moteur de routage lui-même — des indicateurs tels que violatedVehicleRestriction lorsqu'aucun trajet entièrement conforme n'existe. Voir la référence pour la liste complète des champs.
Schéma complet de requête/réponse (tous les champs du profil camion, tous les types d'avertissement, enveloppes d'erreur) : voir la référence interactive sur /reference.html.
Groupes de fonctionnalités
57 types de fonctionnalités organisés en 16 groupes. Passez l'id comme paramètre group sur /features pour interroger tous les membres en une seule requête.
| ID du groupe | Nom | Membres |
|---|---|---|
| imagery | Imagerie | cameras |
| weather | Météo et environnement | weather_stations, regional_weather, weather_forecasts, weather_alerts, wind_warnings |
| road_conditions | État des routes | road_conditions, ice_roads, snow_plans |
| traffic_performance | Performance du trafic | traffic_segments, speed_data, travel_times, express_lanes, hov_lanes |
| planned_events | Événements et fermetures prévus | workzones, future_construction, future_roadwork, special_events, seasonal_loads |
| alerts_advisories | Alertes et avis | alerts, emergency_alerts, advisories, general_info |
| wildfires | Feux de forêt | wildfires, wildfire_incidents, wildfire_perimeters |
| trucking | Camionnage et véhicules commerciaux | truck_restrictions, weight_restrictions, bridge_clearances, truck_routes, freight_corridors, truck_parking, truck_rest_areas, weigh_stations, inspection_stations |
| traveler_services | Services aux voyageurs | rest_areas, service_centres, info_centres, visitor_locations, parks, communities, carpool_lots, airports |
| fuel_charging | Carburant et recharge | ev_charging, alt_fuel_stations |
| borders | Frontières et passages | border_crossings, ports_of_entry |
| ferries | Traversiers | ferries, coastal_ferries |
| transit | Transport en commun | transit_hubs, transit_stops |
| tolls | Péages | tolls |
| static_infrastructure | Infrastructure et application | bridges, signs, roundabouts, speed_cameras |
| operations | Opérations | service_vehicles |
Types de fonctionnalités
Passez l'une de ces valeurs comme paramètre type à /features.
Types d'événements
| Type | Plage de gravité | Description |
|---|---|---|
| incident | moderate–critical | Accidents, véhicules en panne, dangers |
| construction | minor–moderate | Travaux routiers, entretien, revêtement |
| closure | major–critical | Fermetures totales de route |
| weather | moderate–critical | Impacts routiers liés à la météo |
| special_event | minor–moderate | Événements planifiés (défilés, courses) |
| advisory | minor–moderate | Avis de voyage, avertissements |
| restriction | minor–moderate | Restrictions de poids, hauteur, vitesse |
Plans & limites
| Limite | Free (essai 14 jours) | Starter (29 €/mois) | Pro (99 €/mois) | Enterprise |
|---|---|---|---|---|
| RPM | 60 | 300 | 1 000 | 5 000 |
| Requêtes quotidiennes | 1 000 | 50 000 | 500 000 | Illimitées |
| Clés API | 1 | 3 | 10 | 50 |
| Juridictions/requête | 2 | 10 | Illimitées | Illimitées |
| Résultats/page | 100 | 500 | 1 000 | Illimités |
| Export GeoJSON | Non | Oui | Oui | Oui |
| Analytique | Non | Non | Oui | Oui |
| Corridor poids lourds | Non | Non | Oui | Oui |
| Délai des données | 15 min | Temps réel | Temps réel | Temps réel |