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
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://api.napspan.com/api/v1/events?jurisdiction=FR&limit=5"

3. Analyser la réponse

Réponse JSON
{
  "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.

En-tête (recommandé)
curl -H "X-API-Key: sk_live_abc123" https://api.napspan.com/api/v1/events
Paramètre de requête
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.

StatutSignification
200Succès
400Requête incorrecte (paramètres manquants/invalides)
401Clé API manquante ou invalide
403Limite du plan dépassée (juridiction, restriction de fonctionnalité, essai expiré)
429Limite de débit dépassée — réessayez après Retry-After secondes
500Erreur 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ètreTypeDescription
idsstringIdentifiants 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.
jurisdictionstringFiltrer 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.
typestringFiltrer par type d'événement : incident, construction, closure, weather, special_event, advisory, restriction
severitystringFiltrer : minor, moderate, major, critical
lat, lng, radius_kmnumberRecherche par rayon (ex. lat=48.86&lng=2.35&radius_km=50)
bboxstringBoîte englobante : minLng,minLat,maxLng,maxLat
limitintRésultats par page (par défaut 100, max selon le plan)
offsetintDécalage de pagination
Requête groupée — récupérer plusieurs ID d'événements en un seul appel
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.

Réponse
{
  "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 environ poll_interval × sweep_factor (facteur 15 par défaut) sans nouvelle observation. Traitez archived_at sur ces lignes comme un horodatage « présumé terminé avant », pas comme une fin précise. Un ratio stale_sweep durablement é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.

Réponse
{
  "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ètreTypeDescription
typestringType de fonctionnalité (voir Types de fonctionnalités). type ou group est obligatoire.
groupstringIdentifiant de groupe (voir Groupes de fonctionnalités). Expansion pratique vers tous les types du groupe.
jurisdictionstringFiltrer par code de juridiction. Interroger un code primaire étend automatiquement la requête à tous les membres de son groupe, le cas échéant.
activebooltrue pour ne retourner que les fonctionnalités actives
lat, lng, radius_kmnumberRecherche par rayon
bboxstringBoîte englobante : minLng,minLat,maxLng,maxLat
limit, offsetintPagination

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).

Exemple : caméras près de Paris
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"
Exemple : toutes les couches poids lourds en France (une requête)
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.

Réponse
[
  { "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.

Réponse (tronquée à 2 sur 16 groupes)
[
  {
    "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.

Réponse
{
  "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.

Requête
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"
Réponse (tableau parallèle, une entrée par ID demandé, dans le même ordre)
{
  "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ètreTypeDescription
from_lat obligatoirenumberLatitude d'origine
from_lng obligatoirenumberLongitude d'origine
to_lat obligatoirenumberLatitude de destination
to_lng obligatoirenumberLongitude de destination
buffer_kmnumberDistance de marge autour du corridor (par défaut 5, max 50 km)
heightnumberHauteur du véhicule en mètres — filtre les gabarits inférieurs
weightnumberPoids du véhicule en tonnes — filtre les limites de poids inférieures
jurisdictionstringRestreindre à une seule juridiction
limitintRésultats maximum (par défaut 500, max 2000)
Recherche dans : bridge_clearances, bridges, weight_restrictions, truck_restrictions, truck_routes, freight_corridors, truck_parking
Exemple : A8 Munich à Salzbourg
curl -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"
Réponse
{
  "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')})")
Réponse (FeatureCollection)
{
  "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.

Réponse
[
  { "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.

Réponse
{
  "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_timearchived_at.

Réponse
[
  { "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 ».

Réponse
[
  { "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.

Réponse
[
  { "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.

Réponse
[
  { "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.

Réponse
[
  { "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 terminaisonDescription
/analytics/feature-historyChangements 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-correlationAgré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.

Réponse
[
  { "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.

Réponse
[]

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.

Réponse
{
  "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.

Réponse
{
  "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.

Réponse (tronquée)
{
  "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énementDéclenché lorsque
event.createdUn nouvel événement de circulation arrive depuis une source nationale de données de mobilité.
event.severity_changeLa gravité d'un événement existant change (ex. moderatemajor).
event.status_changeLe statut bascule (ex. activearchived).
event.archivedL'événement est terminé.
testEnvoyé 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.

Corps du POST webhook
{
  "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.

Corps du POST groupé (batch_window_seconds=60)
{
  "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 :

Pseudo-code récepteur
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.

Récepteur Node.js
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

ChampTypeDescription
origin obligatoireobject{ "lat": ..., "lng": ... }
destination obligatoireobject{ "lat": ..., "lng": ... }
truck obligatoireobjectBloc véhicule — profile (tractor, straight_truck, van), weight_t, height_m, optionnels length_m, width_m, axles, hazmat
alternativesintNombre d'itinéraires alternatifs (0–3, par défaut 0)
avoidarrayOptionnel : tolls, ferries, tunnels
enrichmentobjectAjuste 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.
cargoobjectMétadonnées de cargaison stockées sur l'itinéraire sauvegardé. Sous-clé : hazmat_class (classe ADR, p. ex. "1.3D").
truck.permit_numberstringNuméro de permis de transport exceptionnel. Renvoyé dans la réponse.
customer_route_idstringClé de corrélation libre (≤128 caractères). Renvoyée à l'identique.
tagsarrayÉtiquettes libres pour l'analyse flotte/ligne (max. 16 entrées, ≤64 caractères chacune).
Chaque appel réussi est décompté de votre quota de routage mensuel — le solde de l'abonnement est consommé en priorité, puis les éventuels packs de recharge dans l'ordre FIFO. Les nouveaux appels sur un itinéraire enregistré (voir ci-dessous) ne consomment pas de quota.
Exemple : Berlin → Munich, tracteur 20 t, 4,0 m de haut
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
    }
  }'
Réponse
{
  "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.

TypeGravité typiqueSe déclenche quand
bridge_clearancecriticalGabarit affiché < hauteur du camion + clearance_pad_m, ou capacité de charge < poids du camion
truck_restrictioncriticalUne limite de hauteur / largeur / longueur / poids affichée sur l'itinéraire est inférieure à la valeur du camion (signalée par dimension)
weight_restrictioncriticalUne limite de poids affichée sur l'itinéraire est inférieure au poids du camion
traffic_eventcritical–infoUn incident, une fermeture ou des travaux RTTI/SRTI en direct croisent le corridor pendant la fenêtre de trajet
future_constructionwarningDes travaux programmés chevauchent l'heure de trajet prévue à ce point de l'itinéraire
special_eventinfoUne fermeture planifiée ou un événement spécial touche l'itinéraire
alertinfoUne alerte générale de juridiction s'applique le long du corridor
weatherinfoConditions / 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.

Requête : ajouter le compteur HOS du conducteur au bloc truck
    "truck": {
      "profile":  "tractor",
      "weight_t": 20,
      "height_m": 4.0,
      "hos": {
        "ruleset":           "eu",
        "drive_remaining_s": 32400,
        "since_break_s":     9000
      }
    }
Réponse : le canal hos[] (aux côtés de warnings[] / features[])
      "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 groupeNomMembres
imageryImageriecameras
weatherMétéo et environnementweather_stations, regional_weather, weather_forecasts, weather_alerts, wind_warnings
road_conditionsÉtat des routesroad_conditions, ice_roads, snow_plans
traffic_performancePerformance du trafictraffic_segments, speed_data, travel_times, express_lanes, hov_lanes
planned_eventsÉvénements et fermetures prévusworkzones, future_construction, future_roadwork, special_events, seasonal_loads
alerts_advisoriesAlertes et avisalerts, emergency_alerts, advisories, general_info
wildfiresFeux de forêtwildfires, wildfire_incidents, wildfire_perimeters
truckingCamionnage et véhicules commerciauxtruck_restrictions, weight_restrictions, bridge_clearances, truck_routes, freight_corridors, truck_parking, truck_rest_areas, weigh_stations, inspection_stations
traveler_servicesServices aux voyageursrest_areas, service_centres, info_centres, visitor_locations, parks, communities, carpool_lots, airports
fuel_chargingCarburant et rechargeev_charging, alt_fuel_stations
bordersFrontières et passagesborder_crossings, ports_of_entry
ferriesTraversiersferries, coastal_ferries
transitTransport en communtransit_hubs, transit_stops
tollsPéagestolls
static_infrastructureInfrastructure et applicationbridges, signs, roundabouts, speed_cameras
operationsOpérationsservice_vehicles

Types de fonctionnalités

Passez l'une de ces valeurs comme paramètre type à /features.

camerasCaptures de caméras de circulation avec URL d'images
weather_stationsCapteurs RWIS : température, vent, humidité, état de la chaussée
rest_areasAires de repos avec équipements (toilettes, wifi, vidange camping-car)
signsPanneaux à messages variables avec texte actuel
road_conditionsÉtat de la surface routière par segment
ev_chargingStations de recharge EV (registres nationaux de points de recharge)
bridge_clearancesGabarit vertical de pont, capacité de charge, statut d'affichage
truck_restrictionsLimites de hauteur/poids, zones matières dangereuses, permis
weight_restrictionsLimites de poids saisonnières/permanentes (polylignes)
truck_routesItinéraires camions désignés (polylignes)
freight_corridorsCorridors du réseau de fret, dont RTE-T (polylignes)
truck_parkingParkings poids lourds avec capacité et équipements
weigh_stationsEmplacements et statut des stations de pesage
traffic_segmentsDonnées des capteurs de flux de circulation (vitesse/volume)
service_vehiclesChasse-neige, véhicules de service DOT (saisonniers)
ferriesTerminaux de ferry et statut des lignes

Types d'événements

TypePlage de gravitéDescription
incidentmoderate–criticalAccidents, véhicules en panne, dangers
constructionminor–moderateTravaux routiers, entretien, revêtement
closuremajor–criticalFermetures totales de route
weathermoderate–criticalImpacts routiers liés à la météo
special_eventminor–moderateÉvénements planifiés (défilés, courses)
advisoryminor–moderateAvis de voyage, avertissements
restrictionminor–moderateRestrictions de poids, hauteur, vitesse

Plans & limites

LimiteFree (essai 14 jours)Starter (29 €/mois)Pro (99 €/mois)Enterprise
RPM603001 0005 000
Requêtes quotidiennes1 00050 000500 000Illimitées
Clés API131050
Juridictions/requête210IllimitéesIllimitées
Résultats/page1005001 000Illimités
Export GeoJSONNonOuiOuiOui
AnalytiqueNonNonOuiOui
Corridor poids lourdsNonNonOuiOui
Délai des données15 minTemps réelTemps réelTemps réel

Obtenir une clé API gratuite