S3 Data Export
Purpose
The S3 Data Export service automatically delivers junction traffic statistics directly to your AWS S3 bucket. Instead of polling the API for data, this service pushes processed data files to your storage as soon as they become available.
The data flow is one-directional: TomTom exports data to your S3 bucket. No data is read from your bucket.
Data pipelines
The S3 Data Export service supports two distinct data pipelines. Depending on your use case, you can subscribe to one or both.
| Pipeline | Description |
|---|---|
| Live | Delivers near real-time junction traffic statistics. Data is exported every 15 minutes, with each file delivered approximately 30 minutes after the corresponding 15-minute interval ends. Use this pipeline when you need up-to-date traffic data with low delay. |
| Historical (Archive) | Delivers consolidated, post-processed junction traffic statistics. Data is exported periodically after the consolidation of each 6-hour time window is complete. Use this pipeline when you need verified, post-processed historical data. |
Both pipelines deliver data in the same file format and directory structure. The key difference lies in when and how the data is produced:
- Live pipeline: data is collected in 15-minute intervals and exported to S3 approximately 30 minutes after each interval ends.
- Historical pipeline: data is collected and processed in 6-hour cycles, and the export to S3 is triggered once processing for each cycle is complete.
Delivery schedule comparison
| Aspect | Live pipeline | Historical (Archive) pipeline |
|---|---|---|
| Trigger | Scheduled, every 15 minutes | Event-driven, after data consolidation |
| Latency | ~30 minutes after each 15-minute interval ends | Periodic (every ~6 hours) |
| Processing cycles | Every 15 minutes (one 15-minute bucket per cycle) | Four times per day (~02:00, 08:00, 14:00, 20:00 UTC) |
Prerequisites
To enable the S3 Data Export integration, you must provide the following information to your TomTom account representative.
| Required information | Description |
|---|---|
| The name of your AWS S3 bucket where data will be delivered. |
| The AWS region where the bucket is hosted (e.g. |
| AWS IAM Access Key ID with write permissions to the bucket. |
| The corresponding AWS IAM Secret Access Key. |
| The data pipeline(s) you wish to subscribe to: Live, Historical (Archive), or both. |
| Optional information | Description |
|---|---|
| An optional directory prefix for organizing files within the bucket (e.g. |
| Whether TomTom should write a completion marker into each day folder of the Historical (Archive) pipeline once the whole day has been delivered. Off by default. Enabling it also switches the export to the dated folder layout ( |
Required IAM permissions
The provided AWS credentials must have at minimum the s3:PutObject permission on the target bucket. We recommend creating a dedicated IAM user specifically for this integration.
Example minimal IAM policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::your-bucket-name/*" } ]}Security recommendations
- Create a dedicated IAM user specifically for this integration.
- Apply the principle of least privilege - grant only
s3:PutObjectpermission. - Enable S3 bucket versioning if you need to retain previously overwritten data.
Delivered data
File format
Both pipelines deliver data in the same format: gzip-compressed Apache Avro Object Container files containing junction traffic statistics aggregated in 15-minute intervals. The Avro schema is embedded within each delivered file, allowing standard Avro tools and libraries to read the data without additional configuration.
| Property | Value |
|---|---|
| File name | data.bin |
| Format | Apache Avro Object Container |
| Content-Type | application/octet-stream |
| Content-Encoding | gzip |
| Integrity check | Checksum verified on each upload |
Data schema
Each Avro file contains records with the following structure:
BatchJunctionTraceStats (root record)
| Field | Type | Description |
|---|---|---|
junctionId | string | Unique identifier of the junction. |
externalId | string (nullable) | External identifier of the junction, as set in the junction definition (see External identifiers). null for junctions defined without external identifiers (for example, automatically created ones) and for records produced before external identifiers were introduced. |
approaches | array | List of approach trace data for this junction. |
BatchApproachTraces (element of approaches)
| Field | Type | Description |
|---|---|---|
approachId | string | Unique identifier of the approach. |
traces | array | List of individual trace statistics for this approach. |
externalId | string (nullable) | External identifier of the approach, as set in the junction definition. null for junctions defined without external identifiers (for example, automatically created ones) and for records produced before external identifiers were introduced. |
BatchApproachTraceStats (element of traces)
| Field | Type | Description |
|---|---|---|
entryTime | long | Timestamp (epoch milliseconds) when the trace entered the approach. |
arrivals | array | List of arrival point observations along the approach. |
hasStops | boolean (nullable) | Whether the trace had any stops along the approach. |
numberOfStops | int (nullable) | Number of stops detected along the approach. |
exitId | string (nullable) | Identifier of the exit used by the trace, if applicable. |
exitExternalId | string (nullable) | External identifier of the exit used by the trace, as set in the junction definition. null when none was set or no exit was used. |
averageSpeed | int (nullable) | Average speed of the trace through the approach. |
travelTime | int (nullable) | Total travel time through the approach. |
firstStopDistance | int (nullable) | Distance to the first stop from the approach entry point. |
controlStats | object (nullable) | Additional control-related statistics, if available. |
junctionPassId | string (nullable) | Stable, opaque identifier of the vehicle pass through the junction, intended for delta detection across deliveries. The same value can appear on records under different approaches of the junction: they describe one physical pass, not duplicates. null means no delta information is available for the record. See Junction pass identifier. |
taskId | string (nullable) | Identifier of the archive generation task (the id returned by POST .../archive/data/tasks/generate) whose run added this record or changed its content. null on every record produced by the regular daily processing and on records no task has touched. The same value names the run’s _COMPLETE_<taskId> marker, see Completion markers. For the full semantics, see the Enhanced Junction Batch page. |
BatchArrival (element of arrivals)
| Field | Type | Description |
|---|---|---|
name | string (nullable) | Name of the arrival point. |
point | object | Geographic coordinates (lat: double, lon: double). |
time | long | Timestamp (epoch milliseconds) when the trace passed the arrival point. |
speed | int (nullable) | Speed of the trace at the arrival point. |
externalId | string (nullable) | External identifier of the arrival point, as set in the junction definition. null for junctions defined without external identifiers (for example, automatically created ones) and for records produced before external identifiers were introduced. |
BatchControlStats (value of controlStats)
| Field | Type | Description |
|---|---|---|
controlDelay | int (nullable) | Delay caused by traffic control. |
approachDelay | int (nullable) | Total delay on the approach. |
decelerationPointSpeed | int (nullable) | Speed at the deceleration point. |
accelerationEndpointSpeed | int (nullable) | Speed at the acceleration endpoint. |
entryDistance | int (nullable) | Distance from the approach entry. |
exitDistance | int (nullable) | Distance to the approach exit. |
coveredLength | int (nullable) | Total length covered by the trace through the control area. |
numberOfStops | int (nullable) | Number of stops within the control area. |
firstStopDistance | int (nullable) | Distance to the first stop within the control area. |
Directory structure
Files are organized in your S3 bucket by date and time in 15-minute intervals. The structure is identical for both pipelines:
<prefix>/<day>/<hour>/<quarter>/data.binWhere:
<prefix>- your configured prefix path (if provided).<day>- date identifier.<hour>- hour of the day (0-23).<quarter>- quarter-hour interval (0-3), corresponding to minutes 0-14, 15-29, 30-44, and 45-59 respectively.
Example file paths:
live/2026-04-30/14/2/data.binarchive/2026-04-30/14/2/data.binThe examples above show data for April 30, 2026, between 14:30 and 14:44 UTC, using live/ and archive/ prefixes respectively. If you subscribe to both pipelines, using separate prefixes like this is recommended to keep the data clearly separated.
The layout can differ per export. Your export can instead be configured with a dated layout, in which the day is split into year, month and day folders and the hour is zero-padded:
<prefix>/<yyyy>/<MM>/<dd>/<HH>/<quarter>/data.binFor example archive/2026/04/30/14/2/data.bin. Exports with completion markers always use the dated layout. Your TomTom account representative confirms which layout your export uses.
Completion markers
On request (see completionMarkers under Prerequisites), the Historical (Archive) pipeline also writes an empty marker object into each day folder once the whole day has been delivered, so your downstream job knows when a day’s set of files is complete. A quarter-hour without traffic produces no data.bin, so a day does not always contain 96 files: the marker, together with a LIST of the day folder, gives you the exact set.
Markers are written only for exports with the dated layout described under Directory structure. Enabling markers on an export that uses the default layout switches it to the dated layout. Days delivered before the switch stay in the default layout; a re-delivery or reprocessing after the switch writes to the dated layout. The markers sit at the root of the day folder, next to the hour folders:
<prefix>/<yyyy>/<MM>/<dd>/_COMPLETE<prefix>/<yyyy>/<MM>/<dd>/_COMPLETE_<taskId>For example archive/2026/04/30/_COMPLETE for the day whose data files are under archive/2026/04/30/.
| What you see | What it means |
|---|---|
_COMPLETE appears, or its LastModified changes | The regular daily processing delivered or re-delivered this day. All |
A new _COMPLETE_<taskId> appears | An archive generation task you submitted with |
Properties of the markers:
- A marker is written only after all files of the day have been built and uploaded. The marker is an empty file, gzip-encoded like the data files (
Content-Encoding: gzip), so its stored size is 20 bytes, not 0. Only its presence and itsLastModifiedcarry information. Its content type istext/plain. - A day on which your junctions produced no data files gets no marker, neither
_COMPLETEnor_COMPLETE_<taskId>. Do not wait for a marker on such a day. - A reprocessing run writes only its own
_COMPLETE_<taskId>; the plain_COMPLETEis not touched. A day that has only been reprocessed, and not yet delivered by the daily processing, therefore holds data files and run markers but no_COMPLETE. Do not treat a missing_COMPLETEas “not ready” for such a day. - Run markers accumulate and are never removed: a day reprocessed several times carries several of them. Discover them by listing the day folder, which you already do to get the set of files.
- A
_COMPLETE_<taskId>means the run finished for that day, not that it changed anything. A run that found nothing to change still leaves its marker, and then no record carries itsid. - When a day is re-delivered or reprocessed, its
data.binfiles are overwritten. Reprocess the whole day and replace your local state for it rather than ingesting only the records stamped with the task id: a reprocessing run does not delete records it no longer produces, and a changed junction geometry can move a pass to the neighbouring quarter-hour while the previous copy remains where it was.
Write semantics
- Files are written using the S3
PutObjectoperation (full replace, not append). - If the same time interval is reprocessed, the corresponding file will be overwritten with updated data.
- Each upload is idempotent - uploading the same data twice produces the same result.
- If you need to preserve previous versions of files, enable S3 bucket versioning on your bucket.
- Completion markers, if enabled, are written only after all files of the day have been uploaded. The daily
_COMPLETEmarker is overwritten on re-delivery; a reprocessing run adds its own_COMPLETE_<taskId>marker instead.
Onboarding checklist
Follow these steps to set up the S3 Data Export integration:
- Decide which pipeline(s) you need: Live, Historical (Archive), or both.
- Create an S3 bucket or designate an existing one for receiving data.
- If subscribing to both pipelines, decide on separate prefix paths to keep data organized (e.g.
live/andarchive/). - Create a dedicated IAM user with
s3:PutObjectpermission on the bucket. - Generate an Access Key ID and Secret Access Key for the IAM user.
- Send the required information to your TomTom account representative:
- Bucket name
- AWS region
- Access Key ID
- Secret Access Key
- Selected pipeline(s)
- Prefix path(s) (if desired)
- Whether you want completion markers for the Historical pipeline (if desired)
- TomTom configures the export and confirms activation.
- Verify that data is arriving in your bucket:
- Live pipeline: data should appear approximately 30 minutes after each 15-minute interval ends.
- Historical pipeline: data should appear after the next processing cycle (~6 hours).
Troubleshooting
| Issue | Possible cause |
|---|---|
| No files appearing in the bucket | The provided credentials may be invalid or the IAM user may lack |
| Files missing for a specific time window | For the Live pipeline, this may indicate a temporary data collection gap. For the Historical pipeline, a processing delay may have occurred and files will arrive after the next processing cycle. If the issue persists, contact your TomTom account representative. |
| Unexpected file overwrites | This is normal behavior when time intervals are reprocessed. Enable S3 bucket versioning to retain a history of all file versions. |
No _COMPLETE marker for a day | Completion markers are written only when enabled for your export and only by the Historical pipeline, at the root of the day folder in the dated layout ( |
| Data differs between Live and Historical pipelines | This is expected. The Live pipeline delivers data as it arrives, while the Historical pipeline delivers post-processed, consolidated data. The Historical pipeline may contain corrections or additional data that was not available at the time of live delivery. |