Pick a pattern
Effort vs. depth.
Most Geotab fleets start with Pattern 1 (one-shot roster sync) and graduate to Pattern 2 when they want maintenance triggers tied to real engine-hours. Pattern 3 is for shops already invested in the Geotab add-in ecosystem.
Pattern 1 — Vehicle roster sync via the MyGeotab SDK
Use Geotab's SDK to enumerate Devices on the database, then bulk-create matching DirtFleet assets. One-time or nightly cron. Same shape as the Samsara version.
When to pick this
First step in any Geotab → DirtFleet migration. Most fleets do this once at cutover and never re-run it.
Show the setup
# Python using the official mygeotab package # pip install mygeotab requests import mygeotab, requests, os api = mygeotab.API( username=os.environ["GEOTAB_USER"], password=os.environ["GEOTAB_PASSWORD"], database=os.environ["GEOTAB_DATABASE"], ) api.authenticate() devices = api.get("Device", resultsLimit=2000) DF_KEY = os.environ["DIRTFLEET_API_KEY"] batch = [] for d in devices: # Geotab's "name" is the friendly nickname; serialNumber + VIN # are surfaced as the device's identifying fields. batch.append({ "nickname": d.get("name") or d.get("serialNumber"), "assetClass": "on-road", "vin": d.get("vehicleIdentificationNumber") or None, "serial": d.get("serialNumber") or None, "customerAssetNumber": d.get("licensePlate") or None, }) for i in range(0, len(batch), 50): requests.post( "https://dirtfleet.app/api/v1/assets/batch", headers={"Authorization": f"Bearer {DF_KEY}"}, json=batch[i:i+50], timeout=30, ) print(f"Synced {len(batch)} Geotab devices into DirtFleet")Pattern 2 — Engine-hours capture (scheduled)
Geotab's StatusData feed surfaces engine-hours. Pull on a schedule (15 min or hourly), post deltas to /api/v1/hours/batch. PM tracking and AUTO_PM flagging start firing on whichever interval the asset has configured.
When to pick this
When drivers don't log manually and Geotab is the canonical hours source. Same scheduling pattern as Samsara Pattern 2.
Show the setup
# Pull diagnostic IDs first (Geotab's engine-hours diagnostic id is # stable across databases but documented as DiagnosticIdEngineHours). import mygeotab, requests, os, json, pathlib api = mygeotab.API(username=..., password=..., database=...) api.authenticate() # Last-seen reading per device — persist between runs SEEN_FILE = pathlib.Path("last-seen.json") seen = json.loads(SEEN_FILE.read_text()) if SEEN_FILE.exists() else {} VEHICLE_MAP = json.loads(os.environ["GEOTAB_TO_DF_ASSETS"]) # { device_id: df_asset_id } # Latest status reading per device status_data = api.get( "StatusData", search={ "diagnosticSearch": {"id": "DiagnosticEngineHoursAdjustmentId"}, "fromDate": "2026-05-13T00:00:00Z", }, ) new_logs = [] for s in status_data: device_id = s["device"]["id"] reading_hours = s["data"] / 3600.0 # Geotab returns seconds; convert if reading_hours <= seen.get(device_id, 0): continue df_asset = VEHICLE_MAP.get(device_id) if not df_asset: continue new_logs.append({ "assetId": df_asset, "hoursReading": reading_hours, "note": "auto-synced from Geotab", "clientMutationId": f"geotab-{device_id}-{int(reading_hours)}", }) seen[device_id] = reading_hours if new_logs: for i in range(0, len(new_logs), 100): requests.post( "https://dirtfleet.app/api/v1/hours/batch", headers={"Authorization": f"Bearer {os.environ['DIRTFLEET_API_KEY']}"}, json=new_logs[i:i+100], ) SEEN_FILE.write_text(json.dumps(seen))Pattern 3 — Geotab Marketplace add-in (long-term path)
For shops that want DirtFleet inside the Geotab fleet manager's UI: build a Geotab add-in that surfaces DirtFleet flags + WO status directly in MyGeotab. Standard add-in JS bundle hits the DirtFleet REST API.
When to pick this
For larger fleets that already invest in Geotab's add-in ecosystem. Optional; the JSON-pull patterns above cover 95% of the value.
Show the setup
# Status: Roadmap — not shipped today. # # The Geotab Marketplace add-in framework takes a static JS bundle # that runs inside the MyGeotab iframe. The DirtFleet side is just # the REST API at /api/v1/* — same auth, same bearer-token shape, # same OpenAPI spec at /openapi.yaml. The add-in is the missing # piece on the Geotab side; we're not blocking on this. # # If you want this prioritized, email hello@dirtfleet.app — first # named customer who commits to using it is the unlock path.
Companion
Pairs with /integrations/samsara
Mixed-fleet shops sometimes have Geotab on the trucks and Samsara on the off-road equipment (or vice versa). Both integrations use the same DirtFleet bulk endpoints — /api/v1/assets/batch and /api/v1/hours/batch — so you can run both syncs against the same DirtFleet org with different VEHICLE MAPs. See /integrations/samsara for the matching Node-flavored version.
Want Pattern 3 prioritized? Email hello@dirtfleet.app.