This is the documentation of version 0.60.0. Go to the latest version, 1.0.0-rc.5.

Traffic area analytics

Request Access

Important note Traffic Area Analytics is unavailable on a Freemium or Pay As You Grow (PAYG) basis. Click the Request Access button above to contact our Sales team.

Traffic Area Analytics runs historical traffic reports for a polygon or multi-polygon region. Provide a GeoJSON Polygon (or MultiPolygon), a date range or specific dates, and the metrics you care about — the trafficAreaAnalytics method synchronously returns aggregated statistics and time-series breakdowns without any polling.

Introduction

Traffic Area Analytics is TomTom's custom traffic analytics tool for analyzing congestion patterns and mobility insights across any geographic area and timeframe. Define custom regions at any scale — from a single neighborhood to an entire country — and retrieve detailed historical traffic metrics: congestion levels, speeds, travel times, and road network coverage.

Who is it for?

Traffic Area Analytics is built for professionals who need reliable, historical traffic data to make decisions:

Key advantages

Core concepts

Each analysis report is described by:

Basic usage

Traffic Area Analytics is a TomTom Move Portal product, so it needs a Move Portal API key, not the Maps API key the rest of the SDK uses. Keep your Maps key in TomTomConfig for the map and other services, and pass the Move Portal key per call:

import { TomTomConfig } from '@tomtom-org/maps-sdk/core';
import { trafficAreaAnalytics } from '@tomtom-org/maps-sdk/services';

TomTomConfig.instance.put({ apiKey: 'YOUR_API_KEY' });  // Maps API key, for everything else

const result = await trafficAreaAnalytics({
    apiKey: 'YOUR_MOVE_PORTAL_KEY',  // this service only
    startDate: '2024-08-06',   // YYYY-MM-DD string or Date object
    endDate: '2024-08-12',     // optional: defaults to three days ago when omitted
    metrics: ['speed', 'congestionLevel'],  // or 'all' to include every metric
    functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],
    hours: [5, 6, 7, 15, 16],  // UTC: Amsterdam's morning and evening rush hours in summer time (UTC+2)
    geometry: {
        type: 'Polygon',
        coordinates: [
            [[4.896128, 52.382402], [4.875701, 52.368459], [4.923611, 52.36341], [4.896128, 52.382402]]
        ]
    }
});

const region = result.features[0];
console.log(region.properties.baseData.speed);          // avg speed (km/h)
console.log(region.properties.baseData.congestionLevel); // congestion %

Available metrics

metrics value Description Unit
'speed' Average speed on the road network km/h
'freeFlowSpeed' Average speed under uncongested conditions km/h
'congestionLevel' Percentage increase in travel time above free-flow %
'travelTime' Average travel time per 10 km minutes
'networkLength' Total length of road segments with data meters

Parameters

Date selection

Supply either startDate + endDate or days — these two options are mutually exclusive.

Continuous range (startDate / endDate)

Both fields accept a JavaScript Date object or an ISO 'YYYY-MM-DD' string, and span at least one and at most 31 days, counted in UTC calendar days. The latest endDate the API accepts is two days ago, but it publishes a day 24 to 48 hours after it ends, so an omitted endDate defaults to three days ago, the latest date that always has data.

// startDate only: endDate defaults to three days ago, so this covers the last week with data
const startDate = new Date();
startDate.setUTCDate(startDate.getUTCDate() - 9);
const lastWeek = await trafficAreaAnalytics({ startDate, /* ... */ });

// Explicit range using YYYY-MM-DD strings
const firstWeekOfAugust = await trafficAreaAnalytics({ startDate: '2024-08-01', endDate: '2024-08-07', /* ... */ });

Specific dates (days)

Use days to analyse a list of individual (possibly non-consecutive) dates. Accepts Date objects or 'YYYY-MM-DD' strings:

// All Mondays in August 2024
const result = await trafficAreaAnalytics({
    days: ['2024-08-05', '2024-08-12', '2024-08-19', '2024-08-26'],
    metrics: ['speed', 'congestionLevel'],
    functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],
    hours: [7, 8, 9, 17, 18],
    geometry: { /* ... */ }
});

Functional road classes

functionalRoadClasses selects which road categories are included in the analysis. Pass 'all' to include every class:

Value Description
'MOTORWAY' Motorways, freeways, and major roads
'MAJOR_ROAD' Major roads, less important than motorways
'OTHER_MAJOR_ROAD' Other major roads connecting neighbouring regions
'SECONDARY_ROAD' Secondary roads linking parts of the same region
'LOCAL_CONNECTING_ROAD' Local roads providing settlement access
'LOCAL_ROAD_HIGH_IMPORTANCE' Local roads of high importance within a settlement
'LOCAL_ROAD' Local roads within settlement sections
'LOCAL_ROAD_MINOR_IMPORTANCE' Local roads of minor importance (dead-ends, alleys)
'OTHER_ROAD' Other roads (paths, cycle routes, pedestrian infrastructure)

Region geometry

The geometry property accepts a GeoJSON Polygon or MultiPolygon. Coordinates must be in [longitude, latitude] order and rings must be closed (first and last coordinate identical):

// Polygon
const geometry = {
    type: 'Polygon',
    coordinates: [
        // Coordinates in [longitude, latitude] order — ring must be closed
        [[4.89, 52.37], [4.91, 52.37], [4.91, 52.39], [4.89, 52.39], [4.89, 52.37]]
    ]
};

// MultiPolygon (e.g. a city boundary with enclaves)
const geometry = {
    type: 'MultiPolygon',
    coordinates: [
        [[[4.89, 52.37], [4.91, 52.37], [4.91, 52.39], [4.89, 52.39], [4.89, 52.37]]],
        [[[4.95, 52.40], [4.97, 52.40], [4.97, 52.42], [4.95, 52.42], [4.95, 52.40]]]
    ]
};

You can obtain a city boundary polygon directly from the SDK using geocodeOne and geometryData:

import { geocodeOne, geometryData, trafficAreaAnalytics } from '@tomtom-org/maps-sdk/services';

const geocodeResult = await geocodeOne('Amsterdam, Netherlands');
const boundaries = await geometryData({ geometries: [geocodeResult] });
const boundary = boundaries.features[0];

// Use the boundary geometry directly as the analysis region
const result = await trafficAreaAnalytics({
    startDate: '2024-08-06',
    endDate: '2024-08-12',
    metrics: ['speed', 'congestionLevel'],
    functionalRoadClasses: ['MOTORWAY', 'MAJOR_ROAD', 'OTHER_MAJOR_ROAD', 'SECONDARY_ROAD', 'LOCAL_CONNECTING_ROAD', 'LOCAL_ROAD_HIGH_IMPORTANCE'],
    hours: [7, 8, 9, 17, 18],
    geometry: boundary.geometry,
});

Response structure

The result is a TrafficAreaAnalytics FeatureCollection. Each feature corresponds to the submitted region.

Top-level properties

const { startDate, endDate, metrics, frcs } = result.properties; // frcs contains the numeric indices sent to the API

Feature properties

const region = result.features[0];
const { baseData, timedData, tiledData, anomalies } = region.properties;

Base data

baseData contains the overall aggregated metrics for the full analysis period:

const {
    speed,           // average speed (km/h)
    freeFlowSpeed,   // free-flow speed (km/h)
    congestionLevel, // congestion % above free-flow
    travelTime,      // avg travel time per 10 km (minutes)
    networkLength,   // total road segment length with data (meters)
} = region.properties.baseData;

Only metrics included in metrics will be present.

Timed data

timedData contains time-series breakdowns at multiple granularities. Each granularity has its own entry type, with the same metric fields as baseData and its time identifiers always set:

Granularity Entry type Time identifiers
yearly AreaAnalyticsYearlyEntry year
monthly AreaAnalyticsMonthlyEntry year, month (1–12)
weekly AreaAnalyticsWeeklyEntry year, week (ISO 8601, from Monday)
daily AreaAnalyticsDailyEntry date (midnight UTC)
hourly AreaAnalyticsHourlyEntry date, hour (0–23)
average AreaAnalyticsAverageEntry day (1 = Monday to 7 = Sunday), hour (0–23)
const { daily, hourly, average } = region.properties.timedData;

// Daily breakdown
daily?.forEach(entry => {
    console.log(entry.date.toISOString().slice(0, 10), entry.speed, entry.congestionLevel);
});

// Hourly breakdown: it spans every day of the range, so show the day with the hour
hourly?.forEach(entry => {
    console.log(`${entry.date.toISOString().slice(0, 10)} ${entry.hour}:00: speed=${entry.speed} km/h`);
});

// Average week: day 1 (Monday) to 7 (Sunday), then hour
average?.forEach(entry => {
    console.log(`Day ${entry.day}, ${entry.hour}:00: congestion=${entry.congestionLevel}%`);
});

Available granularities depend on the date range and requested hours. Every time identifier is in UTC, like hours. average holds one entry per day of the week and hour with data, Monday first, so a range shorter than a week or an hours filter yields fewer than 168 entries: read day and hour rather than the array index.

A weekly entry's year is the ISO 8601 year its week counts in, not always the calendar year: the week from Monday 30 December 2024 is week 1 of 2025.

Tiled data

tiledData contains per-tile heatmap metrics when available. Each tile entry includes a tileCentre coordinate ([longitude, latitude]) and the requested metrics:

region.properties.tiledData?.tiles.forEach(tile => {
    const [lon, lat] = tile.tileCentre;
    console.log(`Tile at [${lon}, ${lat}]: congestion=${tile.congestionLevel}%`);
});

Anomalies

anomalies is a map from metric key to detected traffic anomalies for that metric: runs of UTC hours, each labelled with the names of the public holidays it overlaps. It is currently always empty, so the snippet below logs nothing: the service detects anomalies only over 35 days or more, beyond a request's 31-day limit.

const speedAnomalies = region.properties.anomalies?.speed ?? [];
speedAnomalies.forEach(anomaly => {
    console.log(anomaly.startDate, anomaly.endDate, anomaly.labels);
});

Multi-day analysis

// Continuous 7-day range using string dates
const result = await trafficAreaAnalytics({
    startDate: '2024-08-01',
    endDate: '2024-08-07',
    metrics: ['speed', 'freeFlowSpeed', 'congestionLevel', 'travelTime', 'networkLength'],
    functionalRoadClasses: 'all',
    hours: 'all',
    geometry: {
        type: 'Polygon',
        coordinates: [[[4.89, 52.37], [4.91, 52.37], [4.91, 52.39], [4.89, 52.39], [4.89, 52.37]]]
    }
});

result.features[0].properties.timedData.daily?.forEach(day => {
    console.log(day.date.toISOString().slice(0, 10), day.congestionLevel);
});

Error handling

try {
    const result = await trafficAreaAnalytics({ /* ... */ });
} catch (error) {
    // SDKServiceError contains .status (HTTP code) and .service
    console.error('Traffic Area Analytics request failed:', error.message);
}

A 401 or 403 on this service usually means the call went out with your Maps API key. Pass a Move Portal key in apiKey on the call, as shown in Basic usage.

API reference

Related guides and examples

Related examples

Related services