GEO TELEMATIC GEO TELEMATIC / API
Public API · v1

Live fleet data, straight from GEO TELEMATIC

Positions, mileage, driving time, tracks, stops and alarms of your vehicles, in your own systems. One key, simple REST endpoints, JSON responses.

Request an API key on WhatsApp →

Overview

The GEO TELEMATIC Public API gives you programmatic, read-only access to the vehicles registered on your GEO TELEMATIC account: their live state, and their history (mileage, driving time, tracks, stops, alarms). It is designed for integrations such as dispatch systems, ERP/logistics software, dashboards, and third-party fleet platforms.

Base URL
https://api.geotelematic.ge/v1

Format — all requests and responses are JSON over HTTPS. All timestamps are Unix epoch seconds (UTC). Coordinates are WGS84.

Each API key is bound to a specific scope: by default it returns only the vehicles of your own account. A device outside your scope returns 404, as if it did not exist. Partner-level keys with access to a wider set of vehicles are issued case by case.

Getting an API key

API keys are issued manually by the GEO TELEMATIC team. To get one, contact us on WhatsApp and tell us:

  • your company / account name on GEO TELEMATIC,
  • what you are integrating (briefly),
  • the expected polling frequency.
Keep your key secret. The key is shown to you once and is stored on our side only as a hash — we cannot recover it. If it is lost or compromised, contact us and we will revoke it and issue a new one immediately. Never put the key in a URL, in client-side code, or in a public repository.

Authentication

Every request must carry your API key in the Authorization header using the Bearer scheme:

Authorization: Bearer gtk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys start with the prefix gtk_. A missing, malformed, or revoked key returns 401 Unauthorized. There is no token exchange or request signing — the header is all you need.

Conventions

TopicRule
Time parametersstartTime / endTime are Unix timestamps in seconds (milliseconds are also accepted). endTime is optional and defaults to now.
Maximum periodThe span between startTime and endTime cannot exceed 31 days.
UnitsDistance in kilometres, speed in km/h, durations in seconds.
DaysDaily mileage is aggregated by local calendar day (Asia/Tbilisi, UTC+4).
SuccessHistory endpoints return "code": 0 on success. Errors use HTTP status codes (see Errors).

GET/public/positions

Returns the latest known position and status of every vehicle your key has access to. Only vehicles that have reported at least one position are included, so lat/lng are never null.

Query parameters

ParameterTypeDefaultDescription
pageinteger1Page number, starting at 1. See Pagination.
per_pageinteger500Vehicles per page, 1–1000.
imeistring—Only these devices, comma-separated: imei=861449050856266,865190074229124.
fieldsstringallReturn only these fields, comma-separated: fields=imei,lat,lng,speed. Smaller responses for frequent polling.
active_minutesinteger—Only vehicles that reported in the last N minutes.
moving_minutesintegerper keySame filter with minute precision: only vehicles that moved (speed > 3 km/h) or had the ignition on in the last N minutes (1–720), e.g. moving_minutes=5. 0 returns all vehicles. Takes priority over moving_hours; the value applied is echoed in the response.
moving_hoursintegerper keyOnly vehicles that moved (speed > 3 km/h) or had the ignition on in the last N hours (1–12). 0 returns all vehicles. Some keys have a default set on our side; the value applied is echoed in the response as moving_hours.

Request

GET https://api.geotelematic.ge/v1/public/positions?page=1
Authorization: Bearer gtk_xxxxxxxx…

Response 200 OK

{
  "pagination": {
    "total": 21,
    "per_page": 500,
    "current_page": 1,
    "total_pages": 1,
    "has_next": false,
    "has_prev": false
  },
  "generated_at": "2026-09-29T13:40:46.048Z",
  "moving_hours": null,
  "devices": [
    {
      "imei":            "000009171863346",
      "deviceName":      "UV-032-VV",
      "licenseNumber":   "UV-032-VV",
      "accountId":       "3ee3871f-5faa-47d3-a14c-2160065f66d9",
      "parentAccount":   null,
      "lat":             42.267771,
      "lng":             42.706086,
      "speed":           0,
      "heading":         215,
      "accStatus":       1,
      "status":          "online",
      "locationType":    "GPS",
      "gpsTime":         1789911611,
      "statusTime":      1789911611,
      "offlineDuration": 35,
      "address":         null
    }
  ]
}

Field descriptions: see Position fields.

GET/public/devices

The registry of devices your key can see — use it to detect devices that were added to or removed from your fleet. Same scope as /public/positions.

Query parameters

ParameterTypeDefaultDescription
pageinteger1Page number.
per_pageinteger1000Devices per page, 1–1000.

Response 200 OK

{
  "pagination": { "total": 58, "per_page": 1000, "current_page": 1, "total_pages": 1 },
  "generated_at": "2026-09-29T13:40:46.048Z",
  "devices": [
    { "imei": "865190074229124", "deviceName": "AA-123-BB", "licenseNumber": "AA-123-BB",
      "accountId": "…", "accountName": "…", "parentAccount": null,
      "addedAt": "2026-05-02T08:11:00Z", "updatedAt": "…", "lastSeenAt": "…" }
  ]
}

GET/public/device/info NEW

Full details and current state of a single device, including its total mileage.

Query parameters

ParameterRequiredDescription
imeiyesDevice IMEI.

Response 200 OK

{ "code": 0, "data": {
  "imei": "865190074229124", "deviceName": "AA-123-BB", "licenseNumber": "AA-123-BB",
  "vin": null, "model": "H19P", "vehicleType": "car", "simNumber": "5XXXXXXXX",
  "accountId": "…", "accountName": "…", "installedAt": "2026-05-02", "activatedAt": 1777700000,
  "status": "online", "accStatus": 1, "speed": 42, "lat": 41.7151, "lng": 44.8271,
  "gpsTime": 1790000000, "totalMiles": 1999.0, "overspeedLimit": 120
} }

totalMiles — total distance in km recorded by GEO TELEMATIC for this device.

GET/public/device/status/statistic NEW

How many of your vehicles are moving, standing, offline, or have never reported. No parameters.

{ "code": 0, "moveNum": 12, "staticNum": 40, "offlineNum": 5, "unusedNum": 1, "total": 58 }

Moving = online and speed above 3 km/h; standing = online and not moving; offline = not connected now; unused = never reported.

GET/public/device/miles NEW

Distance driven and driving time of a vehicle over any period — a day, a week, or any custom range up to 31 days.

Query parameters

ParameterRequiredDescription
imeiyesDevice IMEI.
startTimeyesPeriod start, Unix seconds.
endTimenoPeriod end, Unix seconds. Defaults to now.

Request

GET https://api.geotelematic.ge/v1/public/device/miles?imei=865190074229124&startTime=1759089600&endTime=1759176000

Response 200 OK

{ "code": 0, "imei": "865190074229124", "startTime": 1759089600, "endTime": 1759176000,
  "miles": 83.68, "runTime": 17762 }
FieldDescription
milesDistance driven in the period, kilometres. GPS jumps and long signal gaps are filtered out. This is the same figure the GEO TELEMATIC app and reports show.
runTimeTime the vehicle was moving with the ignition on, seconds.

GET/public/device/track/history NEW

All recorded points of a vehicle for the period, oldest first.

Query parameters

ParameterRequiredDescription
imeiyesDevice IMEI.
startTimeyesPeriod start, Unix seconds.
endTimenoPeriod end. Defaults to now.
onlyGpsno1 = return only satellite (GPS) fixes.

Response 200 OK

{ "code": 0, "imei": "865190074229124", "count": 3751, "truncated": false,
  "data": [
    { "gpsTime": 1759090000, "lat": 41.7151, "lng": 44.8271, "speed": 38, "course": 120,
      "satellites": 11, "acc": 1, "positionType": "GPS" }
  ] }

At most 100,000 points per request. If truncated is true, split the period into shorter requests.

GET/public/vehicle/stay NEW

Stops (parking) of a vehicle during the period.

Query parameters

ParameterRequiredDescription
imeiyesDevice IMEI.
startTimeyesPeriod start, Unix seconds.
endTimenoPeriod end. Defaults to now.
minMinutesnoMinimum stop length in minutes, default 2.

Response 200 OK

{ "code": 0, "imei": "865190074229124", "count": 16,
  "data": [ { "startTime": 1759090000, "endTime": 1759093600, "duration": 3600, "lat": 41.7151, "lng": 44.8271 } ] }

A stop is a period with speed at or below 2 km/h lasting at least minMinutes — the same rule as the reports on the platform.

GET/public/device/alarm NEW

Alarms and events of a vehicle during the period: ignition on/off, overspeed, power cut, low voltage, geofence entry/exit, harsh driving and more. Up to 5,000 per request.

Query parameters

ParameterRequiredDescription
imeiyesDevice IMEI.
startTimeyesPeriod start, Unix seconds.
endTimenoPeriod end. Defaults to now.

Response 200 OK

{ "code": 0, "imei": "865190074229124", "count": 26,
  "details": [ { "alarmType": "overspeed", "severity": "warning", "alarmName": "…", "message": "…",
                  "lat": 41.7151, "lng": 44.8271, "alarmTime": 1759090000 } ] }

Common alarmType values: ignition_on, ignition_off, overspeed, power_cut, power_restored, low_voltage, offline, geofence_enter, geofence_exit, harsh_accel, harsh_brake, harsh_corner, sos.

GET/public/vehicle/acc NEW

How many times the ignition was switched on, per vehicle, during the period.

Query parameters

ParameterRequiredDescription
startTimeyesPeriod start, Unix seconds.
endTimenoPeriod end. Defaults to now.
imeinoOnly these devices, comma-separated. Without it, the whole fleet is counted and the period is limited to 7 days.

Response 200 OK

{ "code": 0, "startTime": 1759089600, "endTime": 1759176000,
  "data": [ { "imei": "865190074229124", "accCount": 14 } ] }

Vehicles with no ignition-on in the period are not listed.

GET/public/fence/query NEW

Geofences of the account the vehicle belongs to (read only).

Query parameters

ParameterRequiredDescription
imeiyesDevice IMEI.

Response 200 OK

{ "code": 0, "imei": "865190074229124",
  "fenceBeanList": [ { "fenceId": "…", "fenceName": "Warehouse", "radius": 300,
      "alarmOnEnter": true, "alarmOnExit": true, "lat": 41.7151, "lng": 44.8271,
      "geometry": { "type": "Polygon", "coordinates": [ … ] } } ] }

geometry is GeoJSON (for polygon fences); circular fences have lat/lng and radius in metres.

GET/public/account/tree NEW

Accounts available to your key. No parameters.

{ "code": 0, "data": [ { "accountId": "…", "userName": "My Company LLC", "parentAccountId": null } ] }

Position fields

Fields returned by /public/positions.

FieldTypeDescription
imeistringDevice IMEI. Stable, unique identifier of the tracker — use this as your primary key.
deviceNamestringVehicle name as shown on the GEO TELEMATIC platform (usually the plate number).
licenseNumberstring · nullLicense plate, if set separately from the name.
accountIdstring (UUID)The account the vehicle belongs to.
parentAccountstring · nullParent account UUID for sub-accounts, otherwise null.
lat / lngnumberLast known position, WGS84 decimal degrees. Never null.
speedintegerSpeed at the last position, km/h.
headinginteger · nullDirection of travel, degrees (0 = north, clockwise).
accStatus0 · 1Ignition: 1 = on, 0 = off.
status"online" · "offline"Whether the tracker is currently connected to the platform.
locationTypestringPosition source. Currently always "GPS".
gpsTimeintegerUnix timestamp (seconds, UTC) of the last position.
statusTimeintegerUnix timestamp (seconds, UTC) of the last contact. Currently equal to gpsTime.
offlineDurationintegerSeconds elapsed since the last contact.
addressnullReserved. Reverse-geocoded address is not included in this version; always null.

Pagination

/public/positions and /public/devices are paginated. Results come in pages of 500 (positions) or 1000 (devices) by default; change it with per_page (up to 1000). The pagination object tells you how many pages exist. Most accounts fit on a single page.

# fetch every page
page = 1
while True:
    r = get(f"/public/positions?page={page}")
    process(r["devices"])
    if not r["pagination"]["has_next"]: break
    page += 1

Requesting a page beyond total_pages returns an empty devices array.

Rate limits

Each key is limited to 120 requests per minute by default. A full refresh of a typical fleet is one request, so polling once every 30–60 seconds is well within the limit. If your integration needs a higher limit, tell us when requesting the key.

Positions on the platform update as trackers report in (typically every few seconds while a vehicle is moving), so polling more often than every ~10–15 seconds brings no benefit. History endpoints (mileage, tracks, stops) do not need polling — call them when you need a report.

Errors

Errors are returned as JSON with an HTTP status code:

{ "statusCode": 400, "error": "Bad Request", "message": "the span cannot exceed 31 days" }
StatusMeaningWhat to do
401Missing, malformed, revoked, or unknown API key.Check the Authorization header. Contact us if the key was revoked.
400Invalid parameter: missing imei / startTime, endTime before startTime, or a period longer than allowed.Fix the request; the message says what is wrong.
404The device does not exist or is not available to your key.Check the IMEI.
429Rate limit exceeded.Slow down; wait the number of seconds in the Retry-After header.
5xxTemporary server-side problem.Retry with exponential back-off (start at 5 s).

Code examples

Fetch the whole fleet's live positions, and the mileage of one vehicle for yesterday.

cURL
Python
JavaScript (Node)
PHP
# live positions
curl -s \
  -H "Authorization: Bearer $GEO_API_KEY" \
  "https://api.geotelematic.ge/v1/public/positions?page=1"

# mileage and driving time for a period
curl -s \
  -H "Authorization: Bearer $GEO_API_KEY" \
  "https://api.geotelematic.ge/v1/public/device/miles?imei=865190074229124&startTime=1759089600&endTime=1759176000"
import os, time, requests

BASE = "https://api.geotelematic.ge/v1"
H = {"Authorization": "Bearer " + os.environ["GEO_API_KEY"]}

def all_positions():
    page = 1
    while True:
        r = requests.get(f"{BASE}/public/positions", headers=H, params={"page": page}, timeout=15)
        r.raise_for_status()
        data = r.json()
        yield from data["devices"]
        if not data["pagination"]["has_next"]: break
        page += 1

for v in all_positions():
    print(v["deviceName"], v["lat"], v["lng"], v["speed"], "ON" if v["accStatus"] else "OFF")

# mileage for the last 24 hours
now = int(time.time())
m = requests.get(f"{BASE}/public/device/miles", headers=H,
                 params={"imei": "865190074229124", "startTime": now - 86400, "endTime": now}, timeout=30).json()
print(m["miles"], "km,", round(m["runTime"] / 3600, 1), "h driving")
const BASE = "https://api.geotelematic.ge/v1";
const H = { Authorization: `Bearer ${process.env.GEO_API_KEY}` };

async function allPositions() {
  const out = [];
  for (let page = 1; ; page++) {
    const r = await fetch(`${BASE}/public/positions?page=${page}`, { headers: H });
    if (!r.ok) throw new Error(`HTTP ${r.status}`);
    const data = await r.json();
    out.push(...data.devices);
    if (!data.pagination.has_next) break;
  }
  return out;
}

async function miles(imei, startTime, endTime) {
  const q = new URLSearchParams({ imei, startTime, endTime });
  const r = await fetch(`${BASE}/public/device/miles?${q}`, { headers: H });
  return r.json(); // { miles, runTime }
}

allPositions().then(v => console.log(v.length, "vehicles"));
<?php
$base = 'https://api.geotelematic.ge/v1';
$key  = getenv('GEO_API_KEY');

function geo_get($url, $key) {
  $ch = curl_init($url);
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $key"],
    CURLOPT_TIMEOUT => 30,
  ]);
  $data = json_decode(curl_exec($ch), true); curl_close($ch);
  return $data;
}

$page = 1; $all = [];
do {
  $data = geo_get("$base/public/positions?page=$page", $key);
  $all = array_merge($all, $data['devices'] ?? []);
  $page++;
} while (!empty($data['pagination']['has_next']));
echo count($all), " vehicles\n";

$now = time();
$m = geo_get("$base/public/device/miles?imei=865190074229124&startTime=" . ($now - 86400) . "&endTime=$now", $key);
echo $m['miles'], " km\n";

Migrating from iopgps

If your integration used the iopgps API, the equivalent GEO TELEMATIC endpoints use the same field names wherever possible (miles, runTime, accStatus, gpsTime, fenceBeanList…). Authentication is simpler: no token exchange and no signature, just the Authorization: Bearer header.

iopgps endpointGEO TELEMATIC endpoint
/api/device/miles/public/device/miles
/api/device/track/history/public/device/track/history
/api/vehicle/stay/v2/public/vehicle/stay
/api/device/alarm/public/device/alarm
/api/vehicle/acc/public/vehicle/acc
/api/device/status/statistic/public/device/status/statistic
/api/device/info, /api/device/detail/public/device/info
/api/fence/query/public/fence/query
/api/account/tree/public/account/tree
/api/device/location, /api/device/status, /api/vehicle/location/v2/public/positions (with ?imei=)
/api/device, /api/device/detail/page/public/devices
/api/authNot needed — use the Authorization: Bearer header.

Coordinates are always WGS84; the Chinese map systems (bd09ll, gcj02) are not used.

Best practices

  • Key by IMEI. Names and plates can be edited on the platform; the IMEI is stable.
  • Poll, don't hammer. Every 30–60 s is plenty for live positions. Use fields and active_minutes to keep frequent polls small, and gpsTime to detect whether a vehicle has actually moved since your last poll.
  • Ask for history when you need it. Mileage, tracks and stops are reports — request them per day or per week instead of polling.
  • Handle offline gracefully. An offline vehicle still returns its last known position — check status and offlineDuration before treating the position as current.
  • Store the key in an environment variable or secret manager — never in source code.
  • Retry on 5xx / network errors with exponential back-off; do not retry on 400, 401 or 404.

Changelog

DateChange
2026-09-29New history and report endpoints: /device/miles (mileage and driving time for any period), /device/track/history, /vehicle/stay, /device/alarm, /vehicle/acc, /device/status/statistic, /device/info, /fence/query, /account/tree. New moving_hours filter on /positions. Migration guide from iopgps.
2026-09-21/public/devices device registry. /positions: per_page, fields, active_minutes, imei parameters and the heading field.
2026-09-20Public API v1 released: GET /public/positions with Bearer-key authentication, pagination and per-account scoping.