Mileage and distance
How to calculate mileage and distance traveled.
When working with Samsara’s platform to track vehicle mileage, it’s important to understand the nuances of how mileage is measured and reported. You can obtain three key mileage-related data points from the Vehicle Stats History API:
obdOdometerMeters
– The odometer reading reported by the vehicle’s onboard diagnostics (ECU).gpsDistanceMeters
– The distance traveled (in meters) since the Samsara Vehicle Gateway was installed, based on GPS calculations.gpsOdometerMeters
– The GPS-based “odometer” reading, which can be manually edited by the customer in Samsara to correct odometer readings for vehicle maintenance and other use cases.
Recommendation: use obdOdometerMeters
obdOdometerMeters
We recommend using obdOdometerMeters
as your primary mileage data source. Fleets commonly rely on the ECU odometer read to track usage and service intervals. However, there are scenarios where the vehicle's ECU does not provide diagnostic coverage for odometer readings. In these cases:
- Fallback to
gpsDistanceMeters
: If noobdOdometerMeters
value is available, usegpsDistanceMeters
to calculate distance traveled for the time period. - Avoid
gpsOdometerMeters
for general mileage: This value is only used if (a) the ECU does not report odometer data and (b) a customer has manually edited in a "starting odometer" in Samsara. While useful for fleet maintenance intervals within Samsara, this value may not reflect the true mileage unless your use case specifically requires the manually edited reading.
How to retrieve distance data
The example below shows how to pull odometer and distance information for vehicles over a specified time period.
Example request
curl --request GET \
--url "https://api.samsara.com/fleet/vehicles/stats/history?vehicleIds=123&types=obdOdometerMeters,gpsOdometerMeters,gpsDistanceMeters&endTime=2025-03-19T11:27:31Z&startTime=2025-03-19T11:25:31Z" \
--header "Accept: application/json"
--header "Authorization: Bearer <YOUR_API_TOKEN>"
Example response
{
"data": [
{
"id": "123",
"name": "Truck",
"externalIds": {
"samsara.serial": "G122SBGJT9",
"samsara.vin": "111"
},
"obdOdometerMeters": {
"time": "2025-03-20T12:13:38Z",
"value": 188982000
},
"gpsOdometerMeters": {
"time": "2025-03-20T16:14:20Z",
"value": 190111382
},
"gpsDistanceMeters": {
"time": "2025-03-20T16:14:20Z",
"value": 1810088.2739995187
}
}
],
"pagination": {
"endCursor": "",
"hasNextPage": false
}
}
obdOdometerMeters.value
: Odometer reading in meters from the onboard diagnostics system.gpsDistanceMeters.value
: The distance in meters traveled based on GPS since the Vehicle Gateway was installed.gpsOdometerMeters.value
: A GPS-based odometer that may have been manually overridden in the Samsara UI.
How to calculate distance for a time range
- Check
obdOdometerMeters
:
If available, calculate the difference between the odometer readings at the start and end of the period. - Use
gpsDistanceMeters
ifobdOdometerMeters
is missing:- If no ECU reading is present, you can use
gpsDistanceMeters
to see how much the vehicle traveled during the same period. - Keep in mind,
gpsDistanceMeters
accumulates from the time the gateway is installed, so typically you would calculate distance by comparing values from two timestamps.
- If no ECU reading is present, you can use
- Do not rely on
gpsOdometerMeters
for general usage:- This is primarily for manual maintenance interval tracking. Unless your specific workflow requires the manual data entered by the fleet manager, avoid using
gpsOdometerMeters
as a general source of truth for mileage.
- This is primarily for manual maintenance interval tracking. Unless your specific workflow requires the manual data entered by the fleet manager, avoid using
Handling potential data gaps
- Missing odometer readings:
If the ECU read is missing or invalid (e.g., certain vehicles do not report an odometer), always fall back ongpsDistanceMeters
. - Jumps in odometer values:
If you see a large jump inobdOdometerMeters
, verify that the Vehicle Gateway was properly associated and configured for this vehicle, and that a different vehicle wasn’t accidentally assigned the same gateway.
Putting It All Together
Below is an example that might be used to calculate daily distance traveled:
import requests
import json
class MileageService:
API_BASE = "https://api.samsara.com"
def __init__(self, api_token):
self.api_token = api_token
def fetch_distance_for_vehicle(self, vehicle_id, start_time, end_time):
url = f"{self.API_BASE}/fleet/vehicles/stats/history"
params = {
"vehicleIds": vehicle_id,
"types": "obdOdometerMeters,gpsOdometerMeters,gpsDistanceMeters",
"startTime": start_time,
"endTime": end_time
}
headers = {
"Accept": "application/json",
"Authorization": f"Bearer {self.api_token}"
}
response = requests.get(url, params=params, headers=headers)
response.raise_for_status()
data = response.json().get("data")
if not data:
return 0
vehicle_data = data[0]
obd_value = vehicle_data.get("obdOdometerMeters", {}).get("value")
gps_distance_value = vehicle_data.get("gpsDistanceMeters", {}).get("value")
# Fallback to GPS distance if obd_value is missing or None
if obd_value is not None:
# In a real-world implementation, you’d compare the current OBD reading
# to a previously recorded OBD reading to compute the difference over time.
distance = "Use the difference between current and previous OBD reads"
elif gps_distance_value is not None:
distance = gps_distance_value
else:
distance = 0
return distance
# app/services/mileage_service.rb
require 'net/http'
require 'uri'
require 'json'
class MileageService
API_BASE = "https://api.samsara.com"
def initialize(api_token)
@api_token = api_token
end
def fetch_distance_for_vehicle(vehicle_id, start_time, end_time)
uri = URI("#{API_BASE}/fleet/vehicles/stats/history")
uri.query = URI.encode_www_form({
vehicleIds: vehicle_id,
types: "obdOdometerMeters,gpsOdometerMeters,gpsDistanceMeters",
startTime: start_time,
endTime: end_time
})
request = Net::HTTP::Get.new(uri)
request["Accept"] = "application/json"
request["Authorization"] = "Bearer #{@api_token}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
data = JSON.parse(response.body)["data"]&.first
return 0 unless data
obd_value = data.dig("obdOdometerMeters", "value")
gps_distance_value = data.dig("gpsDistanceMeters", "value")
# Fallback to GPS distance if obd_value is missing or nil
distance = if obd_value
# Your logic for converting OBD odometer values into a period-specific distance
# Example: Subtract the start odometer from the end odometer to get the distance
# In a real implementation, you might store historical odometer readings
# and compute the delta between them for the time range.
"Use the difference between current and previous OBD reads"
elsif gps_distance_value
gps_distance_value
else
0
end
distance
end
end
By combining these data fields, you can build robust mileage-tracking features in your integration:
- Prefer ECU odometer (
obdOdometerMeters.value
) where available. - Fallback to GPS distance (
gpsDistanceMeters.value
) in the absence of ECU coverage. - Avoid manual “GPS odometer” (
gpsOdometerMeters.value
) unless your specific workflow requires it for maintenance logs.
This approach ensures that you produce accurate, consistent mileage calculations in most fleet management scenarios.
Updated 5 days ago