Developer access

Fleet API keys

Create read-only credentials, connect fleet integrations, and explore the API documentation.

Create an API key
Give it a name that identifies the system using it. You can have up to 10 active keys.
Active keys
Revoking a key stops it immediately.
API documentation
Read-only JSON API · Version 1

Authentication

Send the API key with every request using X-API-Key. You may also use Authorization: Bearer YOUR_API_KEY. All results are limited to vehicles accessible by the key’s account.

Quick start

            
GET/api/v1/vehicles

List every vehicle available to the account.

Response example
{
  "count": 1,
  "data": [{
    "imei": "123456789012345",
    "name": "Truck 12",
    "plate_number": "70-1234",
    "vehicle_type": "truck",
    "status": "online",
    "last_update": "2026-08-22T04:31:00.000Z"
  }]
}
GET/api/v1/locations

Return the latest known position and status for all vehicles, including tier-aware low-power state and variable input channels.

Response example
{
  "count": 1,
  "generated_at": "2026-08-22T04:32:00.000Z",
  "data": [{
    "imei": "123456789012345",
    "plate_number": "70-1234",
    "latitude": 13.7563,
    "longitude": 100.5018,
    "speed_kmh": 0,
    "mode": "low_power",
    "powerTier": "critical",
    "batteryVoltage": 3.05,
    "vin24": 0,
    "externalPowerPresent": false,
    "inputs": [12.1, 0, 5.25],
    "expectedReportIntervalSeconds": 7200,
    "expectedNextReportAt": "2026-08-22T06:31:58.000Z",
    "gsmExpectedOff": true,
    "stale": false,
    "gps_time": "2026-08-22T04:31:58.000Z"
  }]
}
GET/api/v1/travel-distance

Calculate travelled distance from stored GPS history. Omit both vehicle selectors to calculate every vehicle.

ParameterRequiredDescription
fromYesISO-8601 start date/time, including timezone.
toYesISO-8601 end date/time. Must be after from; maximum range is 366 days.
imeiNoCalculate one device by IMEI.
plate_numberNoCalculate one device by plate number, case-insensitive. Do not combine with imei.
All vehicles/api/v1/travel-distance?from=2026-08-01T00:00:00%2B07:00&to=2026-08-02T00:00:00%2B07:00
By IMEI/api/v1/travel-distance?from=2026-08-01T00:00:00%2B07:00&to=2026-08-02T00:00:00%2B07:00&imei=123456789012345
By plate/api/v1/travel-distance?from=2026-08-01T00:00:00%2B07:00&to=2026-08-02T00:00:00%2B07:00&plate_number=70-1234
Response example
{
  "from": "2026-07-31T17:00:00.000Z",
  "to": "2026-08-01T17:00:00.000Z",
  "count": 1,
  "data": [{
    "imei": "123456789012345",
    "name": "Truck 12",
    "plate_number": "70-1234",
    "distance_km": 184.72,
    "sample_count": 412,
    "accepted_segments": 411,
    "rejected_segments": 0
  }]
}
GET/api/v1/idle-time

Calculate when one vehicle was truly idling: speed from 0 through 1 km/h while its engine/ignition was confirmed ON. Engine-off parking and unknown engine state are excluded.

Request: authenticated GET with query parameters. Send no request body. Success returns 200 application/json.

HeaderRequiredValue
X-API-KeyYesYour Fleet-Eye API key. Alternatively send Authorization: Bearer YOUR_API_KEY.
AcceptNoapplication/json
Query parameter / typeRequiredRules
imei · stringYesThe device IMEI. The API key must have access to this vehicle.
from · ISO-8601 stringYesBeginning of the range, including timezone. Encode a positive-offset + as %2B.
to · ISO-8601 stringYesEnd of the range. Must be after from; maximum range is 366 days.
max_gap_s · numberNoPositive number of seconds, default 3600. Longer telemetry gaps end a period at its last idle sample.
Request URL/api/v1/idle-time?imei=123456789012345&from=2026-08-01T00:00:00%2B07:00&to=2026-08-02T00:00:00%2B07:00
Complete cURL request
curl --get 'https://YOUR_SERVER/api/v1/idle-time' \
  --header 'X-API-Key: YOUR_API_KEY' \
  --header 'Accept: application/json' \
  --data-urlencode 'imei=123456789012345' \
  --data-urlencode 'from=2026-08-01T00:00:00+07:00' \
  --data-urlencode 'to=2026-08-02T00:00:00+07:00'
JavaScript request
const query = new URLSearchParams({
  imei: '123456789012345',
  from: '2026-08-01T00:00:00+07:00',
  to: '2026-08-02T00:00:00+07:00'
});

const response = await fetch(`/api/v1/idle-time?${query}`, {
  headers: { 'X-API-Key': 'YOUR_API_KEY' }
});
const result = await response.json();
if (!response.ok) throw new Error(result.error);
console.log(result.total_idle_minutes, result.periods);
Response example
{
  "from": "2026-07-31T17:00:00.000Z",
  "to": "2026-08-01T17:00:00.000Z",
  "imei": "123456789012345",
  "name": "Truck 12",
  "plate_number": "70-1234",
  "idle_speed_range_kmh": { "min": 0, "max": 1 },
  "max_gap_s": 3600,
  "engine_detection": {
    "source": "engine_status_or_voltage_input",
    "voltage_input": "input1",
    "engine_on_threshold_v": 8
  },
  "total_idle_seconds": 5400,
  "total_idle_minutes": 90,
  "idle_period_count": 1,
  "longest_idle_seconds": 5400,
  "sample_count": 48,
  "idle_sample_count": 4,
  "stationary_sample_count": 12,
  "engine_off_stationary_sample_count": 8,
  "unknown_engine_stationary_sample_count": 0,
  "skipped_sample_count": 0,
  "periods": [{
    "started_at": "2026-08-01T02:00:00.000Z",
    "ended_at": "2026-08-01T03:30:00.000Z",
    "last_idle_at": "2026-08-01T03:00:00.000Z",
    "duration_s": 5400,
    "open_at_range_end": false,
    "ended_reason": "movement",
    "sample_count": 4
  }]
}
Response field reference
Field / typeScopeMeaning
from, to · stringResponseAccepted range normalized to UTC.
imei, name, plate_numberResponseVehicle identity. Plate number can be null.
idle_speed_range_kmh · objectResponseInclusive minimum and maximum speed used.
max_gap_s · numberResponseTelemetry-gap rule used by the calculation.
engine_detection · objectResponseEngine-state source and, when applicable, the configured voltage input and ON threshold.
total_idle_seconds · integerResponseSum of every idle-period duration.
total_idle_minutes · numberResponseSame total in minutes, rounded to one decimal place.
idle_period_count · integerResponseNumber of periods found.
longest_idle_seconds · integerResponseLongest observed period.
sample_count · integerResponseValid speed samples considered.
idle_sample_count · integerResponseSamples at 0–1 km/h with engine confirmed ON.
stationary_sample_count · integerResponseAll samples at 0–1 km/h, regardless of engine state.
engine_off_stationary_sample_count · integerResponseStationary samples excluded because the engine was OFF.
unknown_engine_stationary_sample_count · integerResponseStationary samples excluded because engine state was unavailable.
skipped_sample_count · integerResponseInvalid or out-of-order samples ignored.
periods · arrayResponseIdle periods in chronological order; empty when none are found.
started_at, ended_atPeriodFirst idle sample and observed end. ended_at can be null.
last_idle_at · stringPeriodLast sample still at 0–1 km/h.
duration_s · integerPeriodObserved duration in seconds.
open_at_range_end · booleanPeriodTrue when no later moving sample exists within the range.
ended_reason · stringPeriodmovement, engine_off, engine_unknown, telemetry_gap, or range_end.
sample_count · integerPeriodNumber of idle samples in this period.
Endpoint errors
400  { "error": "imei is required" }
400  { "error": "from and to must be ISO-8601 date/time values" }
400  { "error": "to must be after from" }
400  { "error": "Date range cannot exceed 366 days" }
400  { "error": "max_gap_s must be a positive number of seconds" }
401  { "error": "Invalid or missing API key" }
404  { "error": "Vehicle not found" }
500  { "error": "Failed to calculate idle time" }

How time is counted. A period starts only when speed is 0–1 km/h and the engine is confirmed ON. REST devices use engine_status; V5/V6 devices use the configured engine-input voltage and threshold. Movement or engine OFF closes the period. Unknown engine state is excluded. A silence longer than max_gap_s closes it at the last idle sample, and the API never guesses beyond stored telemetry. With no confirmed idle history, the response is still 200 with zero totals and an empty periods array.

GET/api/v1/geofence-history

Historic geofencing. Give a place — centre and radius — and a time window, and get back every time the vehicle was there: when it arrived, when it left, how long it stayed, and how many visits in total. The circle is not saved anywhere: stored GPS history is replayed against it, so you can ask about a place after the fact. Omit both vehicle selectors to ask about every vehicle.

ParameterRequiredDescription
fromYesISO-8601 start date/time, including timezone.
toYesISO-8601 end date/time. Must be after from; maximum range is 366 days.
latitudeYesCentre of the place. Alias: lat.
longitudeYesCentre of the place. Alias: lng.
radius_mYesRadius in metres, 50–50000. Alias: radius.
imeiNoOne device by IMEI.
plate_numberNoOne device by plate number, case-insensitive. Do not combine with imei.
max_gap_sNoEnd a visit when the device goes silent for longer than this instead of counting the whole outage as time spent there.
limitNoVisits listed per vehicle, 1–5000 (default 500). visit_count and the totals always cover every visit.
One car at a depot/api/v1/geofence-history?from=2026-08-01T00:00:00%2B07:00&to=2026-08-08T00:00:00%2B07:00&latitude=13.7563&longitude=100.5018&radius_m=200&imei=123456789012345
Whole fleet/api/v1/geofence-history?from=2026-08-01T00:00:00%2B07:00&to=2026-08-08T00:00:00%2B07:00&lat=13.7563&lng=100.5018&radius=500
Response example
{
  "from": "2026-07-31T17:00:00.000Z",
  "to": "2026-08-07T17:00:00.000Z",
  "place": {
    "latitude": 13.7563,
    "longitude": 100.5018,
    "radius_m": 200,
    "exit_radius_m": 240,
    "max_gap_s": null
  },
  "count": 1,
  "total_visit_count": 2,
  "total_seconds": 9000,
  "data": [{
    "imei": "123456789012345",
    "name": "Truck 12",
    "plate_number": "70-1234",
    "visit_count": 2,
    "total_seconds": 9000,
    "longest_seconds": 5400,
    "average_seconds": 4500,
    "first_entered_at": "2026-08-01T02:00:00.000Z",
    "last_entered_at": "2026-08-03T05:00:00.000Z",
    "still_inside": false,
    "sample_count": 1840,
    "inside_sample_count": 96,
    "visits": [{
      "entered_at": "2026-08-01T02:00:00.000Z",
      "exited_at": "2026-08-01T03:30:00.000Z",
      "duration_s": 5400,
      "still_inside": false,
      "sample_count": 54,
      "closest_distance_m": 18
    }],
    "visits_truncated": false
  }]
}

How a visit is counted. A car has to cross radius_m to arrive but the wider exit_radius_m to leave, so a parked car drifting on GPS noise stays one visit instead of hundreds. A visit still open when the window ends has exited_at: null, still_inside: true, and a duration counted only up to the last position actually recorded.

Errors

Errors use JSON in the form { "error": "Description" }.

400 Invalid parameters or date range401 Missing, invalid, or revoked API key404 Vehicle not found409 Plate number matches multiple vehicles500 Server could not complete the request