EU Directive 2010/40/EU mandates that every Member State operates a National Access Point (NAP) that publishes real-time traffic, safety, and multimodal travel data in DATEX II. On paper, that means one standard, 27 publishers, one integration.
In practice, every NAP serves a different DATEX II profile, over a different transport, behind a different authentication flow, on a different cadence. "DATEX II compliant" is a checkbox each Member State ticks differently.
This is what we ran into building NAPSPAN, a unified API across EU 27 + UK + EFTA. Here's the architecture that lets us treat 30 incompatible DATEX II feeds as if they were one.
The Spec, Then Reality
DATEX II is a mature European standard maintained by datex2.eu and coordinated by NAPCORE. Each delegated regulation references it as the mandatory exchange format:
- (EU) 2022/670 — RTTI: real-time events, speeds, road conditions
- (EU) No 886/2013 — SRTI: free, open access to safety alerts
- (EU) No 885/2013 — SSTP: safe and secure truck parking
- (EU) 2017/1926 — MMTIS: multimodal travel information
- AFIR Article 20 — live EV charging-station data
So the spec is clear. The implementation is anything but.
Where the NAPs Actually Diverge
"DATEX II compliant" hides at least five axes of divergence:
1. Profile
Each Member State publishes a national profile — a constrained subset of the DATEX II model. Germany's profile (German Traffic Data Profile) defines incidents, road works, and traffic flow on Autobahn + Bundesstraßen. France's transport.data.gouv.fr uses different sub-elements. The Netherlands' NDW publishes a tighter event taxonomy. The UK's NAP TDT wraps DATEX into TIH-specific containers. The DATEX II profiles directory lists them all — same root schema, every country a different shape.
2. Version
DATEX II v2.3 and v3.x are both in the wild. Some NAPs publish both. Some are mid-migration. The XML namespaces are different. Some elements were renamed between versions.
3. Transport
DATEX II is a payload format, not a transport. NAPs ship it over:
- HTTPS pull — you GET an XML document on a schedule
- HTTPS push (subscription) — you register an endpoint, the NAP POSTs to you
- SOAP-over-HTTPS — envelope-wrapped pull
- National telematics interfaces — country-specific exchange protocols layered on top of HTTP
- WFS / GeoJSON — some city-level supplements
4. Authentication
The matrix here is wide:
- None — SRTI safety feeds are mandated free and open
- Registered consumer (no key) — the NAP recognizes your registered access
- Bearer / API key — standard token in a header
- TLS client certificate — mutual-TLS handshakes
- OAuth2 — some operator portals
5. Cadence and freshness
Some publications update every 30 seconds (motorway flow). Others refresh nightly (planned road-work calendars). Some sit behind a CDN that lies about freshness via stale Last-Modified headers. Adaptive backoff matters.
The Format Zoo
The upshot of those five axes is that the same incident — an accident on a motorway, say — arrives looking completely different from each NAP. One ships it as XML, another as JSON-LD; one is on DATEX II v2.3, another on v3.x; each wraps the payload in its own envelope and reaches us over a different transport behind a different auth flow. Same regulation, same standard, no two feeds alike.
So the work isn't reading DATEX II once. It's reading 30 dialects of it and making them agree.
The Architecture
The solution is the same approach we use on the North American side: each NAP is handled by its own self-contained adapter, all of them producing the same normalized output. Whether a given NAP is pulled as XML, polled as JSON-LD, or unwrapped from a country-specific container, the adapter hides every one of those differences. It takes a source configuration (which feed, which credentials, which country, which profile version) and hands back normalized events and features.
The scheduler doesn't know or care which DATEX II profile, version, or transport each NAP uses. It asks an adapter to fetch and gets back normalized events and features — nothing else leaks through.
The Normalized Model
Everything lands in one consistent model regardless of where it came from:
Events — time-bounded incidents (accidents, road works, closures, weather) with severity, location, affected roads, planned-vs-actual timing, and lifecycle status. The source-specific fields each NAP carries are preserved alongside the normalized ones, so nothing is lost in translation.
Features — everything else: cameras, VMS signs, weather stations, parking sites, EV charging stations, truck-route segments, bridge clearances. They all share one generic shape with type-specific detail attached, so adding a new kind of point-of-interest is a configuration change, not a schema migration.
The Hard Parts
1. Profile Field Mapping
Each national profile keeps the same canonical information — the road name, the direction, the location — in a different place, often nested several levels deep and named differently. Every adapter knows where its profile keeps each field we care about. Where a profile simply doesn't carry something, the field is left empty — we don't synthesize data we don't have.
2. Lifecycle Tracking
The same incident is republished repeatedly as it evolves, and each NAP signals updates differently — some on every minor change, others only on substantive ones. We track how each record changes over time so that an update is recognized as the same event maturing, not a new one. That history is what powers cross-border analytics like clearance-time percentiles and corridor reliability.
3. Coordinate Reference Systems
Most NAPs publish WGS84 (EPSG:4326). Some German state feeds publish ETRS89 / UTM zones. A few city feeds use the local cadastral system. PostGIS handles transformation, but each adapter has to know what it's receiving.
4. Rate Limiting at Scale
30 NAPs, each with multiple resource types (events, VMS, parking, EV charging, weather), each polling on a 30 sec to 5 min cadence. The scheduler uses per-server semaphores with configurable concurrency limits and request gaps. Circuit breakers back off on repeated failures. Adaptive backoff increases poll intervals when a NAP is slow or returning errors — important when a single NAP can otherwise drag down a whole region's freshness.
5. One Model for Every Feature Type
The generic feature model pays off every time a new kind of data comes online. When AFIR EV charging data arrived on the EU side, it slotted into the same model as cameras and parking with no structural rework — just another feature type alongside the rest.
The API
After all that normalization work, the API is straightforward:
Get active incidents in Germany:
curl "https://api.napspan.com/api/v1/events?jurisdiction=DEU&type=incident&status=active" \
-H "X-API-Key: your_key"
{
"data": [
{
"id": "de_mobilithek_4711",
"jurisdiction": "DEU",
"type": "incident",
"severity": "major",
"title": "Verkehrsunfall A7 Richtung Hamburg",
"affected_roads": ["A7"],
"direction": "Hamburg",
"lanes_affected": "2 of 3 lanes closed",
"latitude": 53.5511,
"longitude": 9.9937,
"start_time": "2026-05-01T08:15:00Z",
"estimated_end_time": "2026-05-01T12:00:00Z"
}
],
"total": 142,
"limit": 100,
"offset": 0,
"has_more": true
}
Get traffic cameras in the Netherlands as GeoJSON:
curl "https://api.napspan.com/api/v1/features/geojson?type=cameras&jurisdiction=NLD" \
-H "X-API-Key: your_key"
Drop the response directly into Leaflet, Mapbox, or any GeoJSON-compatible tool.
Query a cross-border box (Berlin to Amsterdam):
curl "https://api.napspan.com/api/v1/events?status=active&\
bbox=4.90,52.37,13.40,52.52" \
-H "X-API-Key: your_key"
One call, two NAPs (Mobilithek + NDW), one consistent response shape — a bounding box spans the border with no per-country fan-out. (For events tied to an actual driven path rather than a box, POST /routing/route returns them as ranked warnings[].)
What's in the Data
Once normalized, the dataset includes:
- Real-time RTTI events — incidents, road works, traffic flow
- SRTI safety alerts (free, open under 886/2013)
- VMS dynamic message-sign content with current displayed text
- SSTP safe and secure truck parking with availability
- AFIR Article 20 EV charging-point status
- Traffic cameras with image URLs (where the NAP exposes them)
- RWIS-style weather-station readings
- TEN-T core network corridor metadata
- Bridge clearances and weight restrictions per Member State
Tech Stack
- Go — chi router, pgx v5, slog, encoding/xml + custom DATEX schema parsers
- PostgreSQL + PostGIS — spatial queries, GeoJSON generation, corridor intersection
- Redis — response caching, feature detail caching, rate-limit counters, webhook idempotency (mandatory — the API and worker refuse to start if Redis is unreachable)
- Vue 3 + Leaflet — live traffic map
Try It
The API is live with a free tier (no credit card):
- Live map — explore the data visually
- API docs — full endpoint reference
- Developer portal — sign up and get an API key
If you're building anything on European mobility data — logistics, fleet routing, navigation, urban-mobility dashboards — we'd love to hear which NAPs and which DATEX II profiles you most need normalized first. The hardest part isn't the parsing; it's knowing which Member State publishes which data on which transport behind which auth flow. After 30 NAPs, we've built a pretty good map of that.
Ready to try NAPSPAN?
Free 14-day trial. No credit card. EU 27 + UK + EFTA, normalized.
Get Free API Key Explore the Map