Skip to main content

RouteReporter API

Contents

RouteReporter is a web-based route compliance tool that provides visibility of your fleet’s planned and post-trip routing activity while using CoPilot navigation. With RouteReporter, you can easily view and analyze data from vehicles in the field over a specified time, helping to ensure true route compliance and measure driver performance. The RouteReporter API is a RESTful API provides endpoints to retrieve trip information.

(Please note: The RouteReporter API is under public preview. This allows you to access and test new API features before they are officially released. This stage is designed for gathering feedback and making improvements based on your input. While the features are fully functional, breaking changes may occur without prior communication before the final release.)

What can you do with the RouteReporter API?

With this API, you can:

  • Retrieve information for a specific trip including the origin and destination, vehicle information, the number of safety events, as well as statistics including planned and actual distance, toll costs, and time of travel.
  • Retrieve information for all trips within a specified timeframe. The list of trips can be filtered based on: vehicle ID, vehicle group, driver ID, or driver group.
  • Retrieve a detailed list of stops for the parent trip and all its modified child trips.

Getting started

To use the API, you must be licensed for RouteReporter and request access by contacting your Trimble Maps sales representative or our support team. You will be provided with an API key.

Authentication

Use the API key provided by Trimble to make a POST request for authentication to the server. If the authentication is successful, the server creates and sends a JSON Web Token (JWT) in the response.

To access the API resources, all subsequent requests must pass the JWT in the request header using the format: Authorization: Bearer {token}.

Authentication URLs

  • AWS: https://api.trimblemaps.com/identity/v2/token
  • Azure: https://api.az.trimblemaps.com/identity/v2/token

Sample authentication request

{
  "apiKey": "string"
}

Sample authentication response

{
  "access_token": "dfjsdjfksdjkjfjklfjfkldfjdjkljdfklgjgjkgjkbljfkjlkfgjlkfjgfjkjhjjkhljklhjhjkhljhjhljhkjhklhgjhkjhgkljjhkjhkjhgkljhkjhgklhgjhgjhlkjgjhklgjkljgklhjkljfklhgjkhjklhjhkgljhkjjkhgjhkljhkgljhkhlgjhkjgjhklgjhklgjkhjgkljhklghfj",
  "token_type": "Bearer",
  "expires_in": 21600, //Time in seconds token will expire
  "refresh_token": "D9Hpn9283drz4X3MyxEVu4BWjKB4G0sI", //A token you can use to get a new access_token, after the initial token expires, without having to send the credentials again.
  "scope": "offline_access"
}

Terminology

Trip information is retrieved using the following IDs:

  • copilotTripId - The unique Trip ID that RouteReporter assigns to a CoPilot trip. In requests it’s referred to as copilotTripId, and it maps to the tripID field in responses. In the RouteReporter web application, it is displayed as TripId or ParentTripId.
  • externalTripId - Your own trip/shipment identifier from your TMS or dispatch system that RouteReporter stores alongside each CoPilot trip. The same ID may map to many RouteReporter trips.

Endpoints

The service’s endpoints are briefly described below. Use the left menu or scroll down this page for sample requests and parameter descriptions for each endpoint.

GET /trip

Retrieve information for a single trip via its copilotTripId. (Limit of 5 requests per second.)

Resource URLs

  • AWS: https://routereporterservice.trimblemaps.com/api/v1/trip?copilotTripId={id}
  • Azure: https://routereporterservice.az.trimblemaps.com/api/v1/trip?copilotTripId={id}

API flow

To retrieve trip information, you will need the TripId or ParentTripId as shown in RouteReporter.

  1. Authenticate your account and obtain an access_token. Pass this token as the Bearer token in the Authorization header for all subsequent requests.

  2. Make a GET request to the /trip endpoint, including the copilotTripId parameter. This value should be the TripId or ParentTripId from the RouteReporter web application.

Trip ID Example

GET /trips

Retrieve an array of all CoPilot trips associated with a given externalTripId. (Limit of 100 requests per minute.) This endpoint allows you to compare data from the same trip—identified by its externalTripId—across many different drivers or vehicles over time.

Resource URLs

  • AWS: https://routereporterservice.trimblemaps.com/api/v1/trips?externalTripId={id}&pageNumber={pageNumber}
  • Azure: https://routereporterservice.az.trimblemaps.com/api/v1/trips?externalTripId={id}&pageNumber={pageNumber}

API flow

To retrieve trip information, you need the External TripId displayed in RouteReporter.

  1. Authenticate your account and get an access_token, which should be passed as the Bearer token in each subsequent request.

  2. Make a GET request to the /trips endpoint passing the externalTripId displayed in RouteReporter. Up to 100 trips are returned per call. Use the pageNumber parameter to retrieve multiple pages if an externalTripId has more than 100 trips. The first page is 1. (e.g. send pageNumber=2 to retrieve trips 101-200.)

CoPilot ID

POST /search

Retrieve all CoPilot trips based on the search criteria provided in the request body. (Limit of 100 requests per minute.)

Resource URLs

  • AWS: https://routereporterservice.trimblemaps.com/api/v2/search
  • Azure: https://routereporterservice.az.trimblemaps.com/api/v2/search

API flow

  1. Authenticate your account and get an access_token, which should be passed as the Bearer token in each subsequent request.

  2. You may also pass a single optional filter in the request body. Only one of the following filters is supported per API call: an array of driverIds, a driverGroupName, an array of vehicleIds, or a vehicleGroupName. (Note: vehicleGroupName is a future enhancement and is not currently available). Providing more than one filter in the request body will result in an error.

  3. The endpoint returns up to 100 trips per page. You must include the pageNumber parameter in your request to retrieve each page. The first page is 0 (for trips 1–100); for example, send pageNumber=1 to retrieve trips 101–200. You can make up to 100 requests per minute.

Notes:

  • Only one of these filters—driverIds, driverGroupName, vehicleIds, or vehicleGroupName—is supported in a single API call. Providing more than one filter in the request body causes an error.
  • All filters are optional. pageNumber is the only mandatory parameter in the request body.
  • If the startDate and endDate are not provided in the request body, the API defaults to the last 7 days.
  • If both startDate and endDate are provided, then trips that started within that timeframe are returned in the response. (The trip doesn’t need to be completed by the endDate. All the trips that have departed within the date range are returned.)
  • The maximum range you can query is any 30-day period during the past year.

POST /stops

Retrieve a comprehensive list of all stops associated with a trip, including origin, destination, and intermediate stops.

Resource URLs

  • AWS: https://routereporterservice.trimblemaps.com/api/v2/stops
  • Azure: https://routereporterservice.az.trimblemaps.com/api/v2/stops

API flow

  1. Authenticate your account and get an access_token, which should be passed as the Bearer token in each subsequent request.

  2. Search for stops by passing either a copilotTripId or externalTripId in the body of your POST request. copilotTripId is the TripId or ParentTripId displayed in RouteReporter, and externalTripId is displayed under the same name in RouteReporter. These values can also be retrieved by searching for trips using the /search endpoint.

  3. A pageNumber is also required in the request body. The first page is 0 (for trips 1–100); for example, send pageNumber=1 to retrieve trips 101–200. You can make up to 100 requests per minute.

  4. The endpoint returns a detailed list of stops for the parent trip and all its modified child trips. Each stop includes address details, coordinates, stop type, site name, and customPlaceId (where applicable).

POST /gpspings

Retrieve all GPS pings associated with a specific trip. Limit of 1 request per second.

Resource URLs

  • AWS: https://routereporterservice.trimblemaps.com/api/v2/gpspings
  • Azure: https://routereporterservice.az.trimblemaps.com/api/v2/gpspings

API flow

  1. Authenticate your account and get an access_token, which should be passed as the Bearer token in each subsequent request.

  2. Search for GPS pings by passing either a copilotTripId or externalTripId in the body of your POST request. copilotTripId is the TripId or ParentTripId displayed in RouteReporter, and externalTripId is displayed under the same name in RouteReporter. These values can also be retrieved by searching for trips using the /search endpoint.

  3. A pageNumber is also required in the request body. The first page is 0 (for trips 1–100); for example, send pageNumber=1 to retrieve trips 101–200. You can make 1 request per second.

  4. The endpoint returns detailed information for each ping in the trip, including latitude, longitude, timestamp, speed, heading, and altitude.

POST /road_events

Retrieve all road events that occurred during a trip. Limit of 10 requests per second.

Resource URLs

  • AWS: https://routereporterservice.trimblemaps.com/api/v2/road_events
  • Azure: https://routereporterservice.az.trimblemaps.com/api/v2/road_events

API flow

  1. Authenticate your account and get an access_token, which should be passed as the Bearer token in each subsequent request.

  2. Search for road events by passing either a copilotTripId or externalTripId in the body of your POST request. copilotTripId is the TripId or ParentTripId displayed in RouteReporter, and externalTripId is displayed under the same name in RouteReporter. These values can also be retrieved by searching for trips using the /search endpoint.

  3. A pageNumber is also required in the request body. The first page is 0 (for trips 1–100); for example, send pageNumber=1 to retrieve trips 101–200. You can make up to 100 requests per minute.

  4. The endpoint returns trip details for events like “Left Route” and “Rejoined Route,” with data points for location, timestamp, and out-of-route mileage.

Events

Event Types:

  • LeftRoute: The driver diverged from the planned route.
  • RejoinedRoute: The driver returned to the planned route.

Sample response

{
  "pageNumber": 0,
  "pageSize": 100,
  "totalPages": 1,
  "totalEvents": 2,
  "events": [
    {
      "eventId": "string",         // unique identifier
      "eventType": "LeftRoute",    // enum: LeftRoute, RejoinedRoute
      "timestamp": "2025-12-18T18:12:37Z",
      "location": {
        "lat": 40.123456,
        "lon": -74.123456
      },
      "oorDistance": 2.34,         // out‑of‑route distance in trip units
      "oorDuration": 300,          // seconds out of route (if available)
      "sequence": 1,               // order within trip
      "copilotTripId": "1731355367928",
      "parentTripId": "1731355367928",
      "externalTripId": "411706",
      "utcOffset": "-0500",        // if exposed
      "customPlaceId": "string or null" // if tied to a custom place
    }
  ]
}

POST /safety_events

Retrieve all safety-related events for a trip. Limit of 10 requests per second.

Resource URLs

  • AWS: https://routereporterservice.trimblemaps.com/api/v2/safety_events
  • Azure: https://routereporterservice.az.trimblemaps.com/api/v2/safety_events

API flow

  1. Authenticate your account and get an access_token, which should be passed as the Bearer token in each subsequent request.

  2. Search for safety events by passing either a copilotTripId or externalTripId in the body of your POST request. copilotTripId is the TripId or ParentTripId displayed in RouteReporter, and externalTripId is displayed under the same name in RouteReporter. These values can also be retrieved by searching for trips using the /search endpoint.

  3. A pageNumber is also required in the request body. The first page is 0 (for trips 1–100); for example, send pageNumber=1 to retrieve trips 101–200. You can make up to 100 requests per minute.

  4. The endpoint returns all safety events for the trip including event type, timestamp, location, and any other relevant information.

Events

Event Types:

  • Speed: Driver exceeded the speed alert threshold.
  • RolloverRisk: Driver traveled above the safe speed for a high-rollover turn.
  • RestrictedRoad: Driver entered a road restricted for the vehicle type.
  • Idle: Driver idled for more than 5 minutes at a location that is not a designated stop.
  • LowBridge: CoPilot displayed a low bridge warning.
  • OutOfRoute: Driver left or rejoined the planned route.
  • Weather: CoPilot displayed a weather notification during the trip.
  • SuddenSlowdown: Driver received a warning about a traffic slowdown.
  • TrafficDetour: Driver was prompted with and responded to a traffic detour.
  • HosEvent: CoPilot issued a hours-of-service time-remaining warning.
  • SuddenElevation: CoPilot issued a notification for a steep uphill or downhill grade.
  • TrafficDetour (Closure): Driver was prompted with a traffic detour due to closure.
{
  "pageNumber": 0,
  "pageSize": 100,
  "totalPages": 1,
  "totalEvents": 12,
  "events": [
    {
      "eventId": "string",
      "eventType": "Speed",        // See enum list above
      "timestamp": "2025-12-18T18:12:37Z",
      "location": {
        "lat": 39.104528,
        "lon": -88.546931
      },
      "heading": 268,
      "copilotTripId": "080625115811899_3",
      "parentTripId": "10313829",
      "externalTripId": "500473679",
      "driverId": "546",
      "vehicleId": "string or null",
      "utcOffset": "-0600",
      "mapVersion": "GRD_ALK.NA.2025.02.21.7.1.1",
      "appVersion": "10.28.2.351",

      // One of the following typed payloads will be populated,
      // others present as null (per “all relevant attributes even if null”).[^]
      "speedEvent": {
        "currentSpeed": 67,
        "postedSpeed": 55
      },
      "rolloverEvent": {
        "currentSpeed": 42,
        "rolloverPostedSpeed": 27,
        "rolloverPostedLat": 35.285943,
        "rolloverPostedLon": -81.187014
      },
      "restrictedRoadEvent": {
        "roadRestrictionType": "general"
      },
      "idleEvent": {
        "idleDuration": 600    // seconds
      },
      "lowBridgeEvent": {
        "bridgeHeight": null   // if returned, otherwise null
      },
      "outOfRouteEvent": {
        "eventSubtype": "LeftRoute", // or RejoinedRoute
        "oorDistance": 0.5,
        "oorDuration": 120
      },
      "weatherEvent": {
        "weatherNotificationId": "396e266d-f9c8-4852-8121-9772c1fe3ed3",
        "roadRisk": "moderate",
        "impactSummary": "visibility",
        "impactsStartPoint": "-85236207,31174661",
        "impactsEndPoint": "-85109274,31133676"
      },
      "slowdownEvent": {
        "currentSpeed": 65,
        "beforeSlowdownSpeed": 62,
        "slowdownDropSpeed": 27,
        "slowdownGridLinkId": "45671140|19",
        "slowdownLat": 32.957554,
        "slowdownLon": -96.351695
      },
      "trafficDetourEvent": {
        "initialLat": 36.060017,
        "initialLon": -79.398190,
        "minutesSaved": 34,
        "newDistanceMeters": 124506,
        "newEta": "2025-08-04T12:41:05Z",
        "response": "Accepted",
        "trafficClosureInfo": "1100452,65;-94117631,44144487|"
      },
      "hosEvent": {
        "hosCategory": 0,      // 0 Break, 1 Driving, 2 OnDuty, 3 Cycle
        "timeRemainingMinutes": 15
      },
      "suddenElevationEvent": {
        "startLat": 38.567498,
        "startLon": -79.090300,
        "gridId": 1763596,
        "linkId": 153,
        "startSpeedKph": 72,
        "elevationType": "Uphill",
        "gradePercent": 20
      }
    }
  ]
}

cURL Guide

How to authenticate and search for trips with cURL commands.

Prerequisites

  • cURL installed on your system
  • A valid RouteReporter API key

Step 1: Authentication

Get a bearer token by sending your API key to the authentication endpoint:

curl -X POST "https://api.trimblemaps.com/identity/v2/token" \
  -H "Content-Type: application/json" \
  -d '{"apiKey": "YOUR_API_KEY_HERE"}'

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
  "token_type": "Bearer",
  "expires_in": 21600,
  "refresh_token": "D9Hpn9283drz4X3MyxEVu4BWjKB4G0sI",
  "scope": "offline_access"
}

Save the access_token value for use in subsequent requests.

Step 2: Search Trips

Use the bearer token to search for trips within a date range:

curl -X POST "https://routereporterservice.trimblemaps.com/api/v2/search" \
  -H "Content-Type: application/json" \
  -H "Authorization: bearer YOUR_ACCESS_TOKEN_HERE" \
  -d '{
    "startDate": "2025-12-17T00:00:00",
    "endDate": "2025-12-18T00:00:00",
    "pageNumber": 0
  }'

Response:

{
  "responseMessage": "Success",
  "results": [
    {
      "tripID": "1731355367928",
      "externalTripID": "411706",
      "vehicleID": "39462118",
      "vehicleName": "TestVehicle",
      "status": "Completed",
      "plannedDistance": 6.57,
      "actualDistance": 6.34,
      "startTime": "2024-11-11T20:02:47",
      "endTime": "2024-11-12T02:15:47"
    }
  ],
  "pageNumber": 0,
  "totalPages": 5,
  "totalTrips": 47
}

Pagination

If totalPages is greater than 1, fetch additional pages by incrementing pageNumber:

curl -X POST "https://routereporterservice.trimblemaps.com/api/v2/search" \
  -H "Content-Type: application/json" \
  -H "Authorization: bearer YOUR_ACCESS_TOKEN_HERE" \
  -d '{
    "startDate": "2025-12-17T00:00:00",
    "endDate": "2025-12-18T00:00:00",
    "pageNumber": 1
  }'

Filtering Results

You can optionally filter trips by adding parameters to the request body:

curl -X POST "https://routereporterservice.trimblemaps.com/api/v2/search" \
  -H "Content-Type: application/json" \
  -H "Authorization: bearer YOUR_ACCESS_TOKEN_HERE" \
  -d '{
    "startDate": "2025-12-17T00:00:00",
    "endDate": "2025-12-18T00:00:00",
    "pageNumber": 0,
    "vehicleIds": ["vehicle1", "vehicle2"],
    "driverIds": ["driver1", "driver2"],
    "vehicleGroupName": "MyVehicleGroup",
    "driverGroupName": "MyDriverGroup",
    "hasEvents": true
  }'

Rate Limiting

  • The search endpoint is limited to 100 requests per minute
  • If you receive a 429 Too Many Requests response, wait for the time specified in the retry-after header (typically 60 seconds) before retrying

JavaScript Guide

How to export a day’s worth of trip data from the RouteReporter API using JavaScript with the Fetch API (browser-compatible).

Prerequisites

  • Modern web browser with Fetch API support
  • Or Node.js 18+ (which includes native fetch)

Code Sample

// =============================================================================
// CONFIGURATION
// =============================================================================

// Replace with your actual API key
const API_KEY = "YOUR_API_KEY_HERE";

// API Endpoints
const AUTH_URL = "https://api.trimblemaps.com/identity/v2/token";
const SEARCH_URL = "https://routereporterservice.trimblemaps.com/api/v2/search";

// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================

/**
 * Sleep for a specified number of milliseconds
 * @param {number} ms - Milliseconds to sleep
 * @returns {Promise} Promise that resolves after the delay
 */
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * Format a date as ISO string without timezone (YYYY-MM-DDTHH:mm:ss)
 * @param {Date} date - Date to format
 * @returns {string} Formatted date string
 */
function formatDate(date) {
    return date.toISOString().slice(0, 19);
}

// =============================================================================
// AUTHENTICATION
// =============================================================================

/**
 * Authenticate with the Trimble Maps API and retrieve an access token.
 * @param {string} apiKey - Your RouteReporter API key
 * @returns {Promise<string>} The access token
 * @throws {Error} If authentication fails
 */
async function getAccessToken(apiKey) {
    const response = await fetch(AUTH_URL, {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            apiKey: apiKey
        })
    });

    if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`Authentication failed: ${response.status} - ${errorText}`);
    }

    const tokenData = await response.json();
    return tokenData.access_token;
}

// =============================================================================
// TRIP SEARCH WITH PAGINATION
// =============================================================================

/**
 * Search for all trips within the specified date range.
 * Handles pagination automatically and respects rate limits.
 * @param {string} accessToken - Bearer token from authentication
 * @param {Date} startDate - Start of date range
 * @param {Date} endDate - End of date range
 * @returns {Promise<Array>} List of all trip objects
 */
async function searchTrips(accessToken, startDate, endDate) {
    const headers = {
        "Content-Type": "application/json",
        "Authorization": `bearer ${accessToken}`
    };

    // Variable to store all trips from all pages
    let allTrips = [];

    let pageNumber = 0;
    let totalPages = null;

    while (true) {
        const payload = {
            startDate: formatDate(startDate),
            endDate: formatDate(endDate),
            pageNumber: pageNumber
        };

        console.log(`Fetching page ${pageNumber}${totalPages ? '/' + totalPages : ''}...`);

        const response = await fetch(SEARCH_URL, {
            method: "POST",
            headers: headers,
            body: JSON.stringify(payload)
        });

        // Handle rate limiting (429 Too Many Requests)
        if (response.status === 429) {
            // Get retry-after header, default to 60 seconds
            const retryAfter = parseInt(response.headers.get("retry-after") || "60", 10);
            console.log(`Rate limited. Waiting ${retryAfter} seconds before retrying...`);
            await sleep(retryAfter * 1000);
            continue; // Retry the same page
        }

        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Search failed: ${response.status} - ${errorText}`);
        }

        const data = await response.json();

        // Extract results and add to our collection
        const results = data.results || [];
        allTrips = allTrips.concat(results);

        // Get pagination info from first response
        if (totalPages === null) {
            totalPages = data.totalPages || 1;
            const totalTrips = data.totalTrips || 0;
            console.log(`Total trips to fetch: ${totalTrips} across ${totalPages} page(s)`);
        }

        console.log(`  Retrieved ${results.length} trips from page ${pageNumber}`);

        // Check if we've fetched all pages (pages are zero-based: 0 .. totalPages - 1)
        if (pageNumber >= totalPages - 1) {
            break;
        }

        // Move to next page
        pageNumber++;

        // Rate limiting: pause 1 second between requests
        await sleep(1000);
    }

    return allTrips;
}

// =============================================================================
// MAIN EXECUTION
// =============================================================================

/**
 * Main function to export a day's worth of trips from RouteReporter.
 * @returns {Promise<Array>} All trips from the last 24 hours
 */
async function exportTrips() {
    console.log("=".repeat(60));
    console.log("RouteReporter Trip Export");
    console.log("=".repeat(60));

    // Step 1: Authenticate
    console.log("\n[1/3] Authenticating...");
    let accessToken;
    try {
        accessToken = await getAccessToken(API_KEY);
        console.log("  Authentication successful!");
    } catch (error) {
        console.error(`  ERROR: ${error.message}`);
        return [];
    }

    // Step 2: Calculate date range (last 24 hours)
    console.log("\n[2/3] Setting up date range...");
    const endDate = new Date();
    const startDate = new Date(endDate.getTime() - 24 * 60 * 60 * 1000); // 24 hours ago
    console.log(`  From: ${startDate.toLocaleString()}`);
    console.log(`  To:   ${endDate.toLocaleString()}`);

    // Step 3: Fetch all trips
    console.log("\n[3/3] Fetching trips...");
    let allTrips;
    try {
        allTrips = await searchTrips(accessToken, startDate, endDate);
    } catch (error) {
        console.error(`  ERROR: ${error.message}`);
        return [];
    }

    // ==========================================================================
    // ALL COMPLETE!
    // The 'allTrips' variable now contains all trip data from the last 24 hours.
    // Each trip is an object with fields like:
    //   - tripID
    //   - externalTripID
    //   - vehicleID, vehicleName
    //   - driverID, driverName
    //   - status (InProgress, Completed, Canceled, Modified, Planned)
    //   - plannedDistance, actualDistance
    //   - plannedDuration, actualDuration
    //   - origin, destination
    //   - startTime, endTime
    //   - and more...
    // ==========================================================================

    console.log("\n" + "=".repeat(60));
    console.log("EXPORT COMPLETE!");
    console.log("=".repeat(60));
    console.log(`Total trips retrieved: ${allTrips.length}`);
    console.log("\nThe 'allTrips' variable contains all trip data.");
    console.log("You can now process, save, or analyze this data as needed.");

    // Example: Print first trip summary (if any trips exist)
    if (allTrips.length > 0) {
        const firstTrip = allTrips[0];
        console.log("\nSample trip data (first trip):");
        console.log(`  Trip ID: ${firstTrip.tripID}`);
        console.log(`  Vehicle: ${firstTrip.vehicleName} (${firstTrip.vehicleID})`);
        console.log(`  Status: ${firstTrip.status}`);
        console.log(`  Distance: ${firstTrip.actualDistance} miles`);
    }

    return allTrips;
}

// =============================================================================
// RUN THE EXPORT
// =============================================================================

// Execute the export and store results
let allTrips;
exportTrips().then(trips => {
    allTrips = trips;
    // You can now work with allTrips here
    // Example: console.log(JSON.stringify(allTrips, null, 2));
});

How It Works

1. Authentication

The script first authenticates with the Trimble Maps identity service using your API key. This returns a bearer token that’s used for all subsequent API calls.

2. Date Range Calculation

The script automatically calculates the date range from the current time to 24 hours prior.

The /api/v2/search endpoint returns paginated results. The script:

  • Fetches the first page to determine total pages
  • Loops through all pages, collecting results
  • Waits 1 second between requests to respect rate limits
4. Rate Limit Handling

If the API returns a 429 Too Many Requests error, the script:

  • Reads the retry-after header (defaults to 60 seconds)
  • Waits the specified time
  • Retries the same request

Filtering Results

You can optionally filter trips by adding parameters to the search request:

const payload = {
    startDate: formatDate(startDate),
    endDate: formatDate(endDate),
    pageNumber: pageNumber,
    // Optional filters:
    vehicleIds: ["vehicle1", "vehicle2"],      // Filter by specific vehicles
    driverIds: ["driver1", "driver2"],         // Filter by specific drivers
    vehicleGroupName: "MyVehicleGroup",        // Filter by vehicle group
    driverGroupName: "MyDriverGroup",          // Filter by driver group
    hasEvents: true                            // Only trips with events
};

Python Guide

How to export a day’s worth of trip data from the RouteReporter API using Python.

Prerequisites

  • Python 3.7+
  • requests library (pip install requests)

Code Sample

import requests
import time
from datetime import datetime, timedelta

# =============================================================================
# CONFIGURATION
# =============================================================================

# Replace with your actual API key
API_KEY = "YOUR_API_KEY_HERE"

# API Endpoints
AUTH_URL = "https://api.trimblemaps.com/identity/v2/token"
SEARCH_URL = "https://routereporterservice.trimblemaps.com/api/v2/search"

# =============================================================================
# AUTHENTICATION
# =============================================================================

def get_access_token(api_key):
    """
    Authenticate with the Trimble Maps API and retrieve an access token.

    Args:
        api_key: Your RouteReporter API key

    Returns:
        The access token string

    Raises:
        Exception: If authentication fails
    """
    headers = {
        "Content-Type": "application/json"
    }

    payload = {
        "apiKey": api_key
    }

    response = requests.post(AUTH_URL, json=payload, headers=headers)

    if response.status_code != 200:
        raise Exception(f"Authentication failed: {response.status_code} - {response.text}")

    token_data = response.json()
    return token_data["access_token"]

# =============================================================================
# TRIP SEARCH WITH PAGINATION
# =============================================================================

def search_trips(access_token, start_date, end_date):
    """
    Search for all trips within the specified date range.
    Handles pagination automatically and respects rate limits.

    Args:
        access_token: Bearer token from authentication
        start_date: Start of date range (datetime object)
        end_date: End of date range (datetime object)

    Returns:
        List of all trip objects
    """
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"bearer {access_token}"
    }

    # Variable to store all trips from all pages
    all_trips = []

    page_number = 0
    total_pages = None

    while True:
        payload = {
            "startDate": start_date.strftime("%Y-%m-%dT%H:%M:%S"),
            "endDate": end_date.strftime("%Y-%m-%dT%H:%M:%S"),
            "pageNumber": page_number
        }

        print(f"Fetching page {page_number}{'/' + str(total_pages) if total_pages else ''}...")

        response = requests.post(SEARCH_URL, json=payload, headers=headers)

        # Handle rate limiting (429 Too Many Requests)
        if response.status_code == 429:
            # Get retry-after header, default to 60 seconds
            retry_after = int(response.headers.get("retry-after", 60))
            print(f"Rate limited. Waiting {retry_after} seconds before retrying...")
            time.sleep(retry_after)
            continue  # Retry the same page

        if response.status_code != 200:
            raise Exception(f"Search failed: {response.status_code} - {response.text}")

        data = response.json()

        # Extract results and add to our collection
        results = data.get("results", [])
        all_trips.extend(results)

        # Get pagination info from first response
        if total_pages is None:
            total_pages = data.get("totalPages", 1)
            total_trips = data.get("totalTrips", 0)
            print(f"Total trips to fetch: {total_trips} across {total_pages} page(s)")

        print(f"  Retrieved {len(results)} trips from page {page_number}")

        # Check if we've fetched all pages
        if page_number >= total_pages:
            break

        # Move to next page
        page_number += 1

        # Rate limiting: pause 1 second between requests
        time.sleep(1)

    return all_trips

# =============================================================================
# MAIN EXECUTION
# =============================================================================

def main():
    """
    Main function to export a day's worth of trips from RouteReporter.
    """
    print("=" * 60)
    print("RouteReporter Trip Export")
    print("=" * 60)

    # Step 1: Authenticate
    print("\n[1/3] Authenticating...")
    try:
        access_token = get_access_token(API_KEY)
        print("  Authentication successful!")
    except Exception as e:
        print(f"  ERROR: {e}")
        return

    # Step 2: Calculate date range (last 24 hours)
    print("\n[2/3] Setting up date range...")
    end_date = datetime.now()
    start_date = end_date - timedelta(days=1)
    print(f"  From: {start_date.strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  To:   {end_date.strftime('%Y-%m-%d %H:%M:%S')}")

    # Step 3: Fetch all trips
    print("\n[3/3] Fetching trips...")
    try:
        all_trips = search_trips(access_token, start_date, end_date)
    except Exception as e:
        print(f"  ERROR: {e}")
        return

    # ==========================================================================
    # ALL COMPLETE!
    # The 'all_trips' variable now contains all trip data from the last 24 hours.
    # Each trip is a dictionary with fields like:
    #   - tripID
    #   - externalTripID
    #   - vehicleID, vehicleName
    #   - driverID, driverName
    #   - status (InProgress, Completed, Canceled, Modified, Planned)
    #   - plannedDistance, actualDistance
    #   - plannedDuration, actualDuration
    #   - origin, destination
    #   - startTime, endTime
    #   - and more...
    # ==========================================================================

    print("\n" + "=" * 60)
    print("EXPORT COMPLETE!")
    print("=" * 60)
    print(f"Total trips retrieved: {len(all_trips)}")
    print("\nThe 'all_trips' variable contains all trip data.")
    print("You can now process, save, or analyze this data as needed.")

    # Example: Print first trip summary (if any trips exist)
    if all_trips:
        first_trip = all_trips[0]
        print("\nSample trip data (first trip):")
        print(f"  Trip ID: {first_trip.get('tripID')}")
        print(f"  Vehicle: {first_trip.get('vehicleName')} ({first_trip.get('vehicleID')})")
        print(f"  Status: {first_trip.get('status')}")
        print(f"  Distance: {first_trip.get('actualDistance')} miles")

    return all_trips


if __name__ == "__main__":
    # Run the export and store results
    all_trips = main()

How It Works

1. Authentication

The script first authenticates with the Trimble Maps identity service using your API key. This returns a bearer token that’s used for all subsequent API calls.

2. Date Range Calculation

The script automatically calculates the date range from the current time to 24 hours prior.

3. Paginated Search

The /api/v2/search endpoint returns paginated results. The script:

  • Fetches the first page to determine total pages
  • Loops through all pages, collecting results
  • Waits 1 second between requests to respect rate limits
4. Rate Limit Handling

If the API returns a 429 Too Many Requests error, the script:

  • Reads the retry-after header (defaults to 60 seconds)
  • Waits the specified time
  • Retries the same request

Filtering Results

You can optionally filter trips by adding parameters to the search request:

payload = {
    "startDate": start_date.strftime("%Y-%m-%dT%H:%M:%S"),
    "endDate": end_date.strftime("%Y-%m-%dT%H:%M:%S"),
    "pageNumber": page_number,
    # Optional filters:
    "vehicleIds": ["vehicle1", "vehicle2"],      # Filter by specific vehicles
    "driverIds": ["driver1", "driver2"],         # Filter by specific drivers
    "vehicleGroupName": "MyVehicleGroup",        # Filter by vehicle group
    "driverGroupName": "MyDriverGroup",          # Filter by driver group
    "hasEvents": True                            # Only trips with events
}
Last updated June 10, 2026.
API Endpoint:

Contents