API Reference

TrukTek Developer Docs

Integrate TrukTek tools directly into your dispatch software, in-cab devices, or internal workflows. All endpoints use standard HTTP — no SDK required.

Base URL: https://www.truktek.com

Overview

TrukTek exposes four REST endpoints for external use. Three require a valid Bearer token (obtained from a TrukTek account passphrase). The Bandit endpoint also requires an active Carrier Pro subscription or trial. All endpoints accept browser session cookies too, so logged-in users on your site can call them directly.

EndpointAuthBillingDescription
POST/api/auth/device
PassphraseAny accountExchange passphrase for Bearer token
GET/api/loads
NonePublicPublic load board search
POST/api/loads/search
Bearer / cookieAny verified accountAuthenticated load search with partner matching
GET/api/loads/rate-check
Bearer / cookieAny verified accountMarket rate summary for a lane (avg/min/max RPM)
POST/api/loads/chain
Bearer / cookiePro / trialHOS-compliant multi-stop load chain planner
GET/api/mileage
Bearer / cookiePro / trialZip-to-zip driving mileage
POST/api/bandit
Bearer / cookiePro / trialBandit AI freight assistant (SSE)
POST/api/tools/bol
Bearer / cookiePro / trial (10 free anonymous uses)BOL document scanner
POST/api/tools/invoice
Bearer / cookiePro / trial (10 free anonymous uses)Invoice document processor

Authentication

API access uses a device passphrase to obtain a long-lived Bearer token. The passphrase is a four-word phrase generated on your TrukTek Profile page. Exchange it once — the token lasts 365 days.

Step 1 — Get a token

POST https://www.truktek.com/api/auth/device
Content-Type: application/json

{
  "passphrase": "amber-cargo-freight-river"
}

Response 200 OK

{
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "expiresAt": "2027-07-09T00:00:00.000Z",
  "user": {
    "id": "abc123",
    "email": "dispatch@yourcompany.com",
    "companyId": "1234567",
    "dotnumber": "1234567"
  }
}

Store the token securely. You can regenerate your passphrase from Profile at any time — doing so immediately invalidates any tokens issued from the previous one.

Step 2 — Use the token

Include the token in every request as a Bearer token in the Authorization header.

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
StatusMeaning
200Token issued — use the token field in subsequent requests
400Missing or malformed passphrase
401Passphrase not found, or account not verified
429Too many attempts — 10 per minute per IP

GET /api/loads

Public load board search. No authentication required. Returns loads with calculated metrics including gross RPM, deadhead distance, and route coordinates.

Rate limit: 30 requests per minute per IP.

Query parameters

ParamRequiredDescription
octyNoOrigin city name
ostNoOrigin state (2-letter)
dctyNoDestination city name
dstNoDestination state (2-letter)
freightNoEquipment type: Van, Reefer, Flatbed, Tanker, Intermodal, Auto
pickupDateNoPickup date (YYYY-MM-DD)
deliveryDateNoDelivery date (YYYY-MM-DD)
hoursSliderNoMax extra drive hours vs direct route (default 999 = no limit)
milesSliderNoMax deadhead miles (default 9999 = no limit)
gross_rpmNoMinimum gross rate per mile (default 0 = no minimum)

Example request

GET https://www.truktek.com/api/loads?octy=Atlanta&ost=GA&dcty=Dallas&dst=TX&freight=Reefer

Response 200 OK

{
  "loads": [
    {
      "loadId": "abc123",
      "octy": "Atlanta",    "ost": "GA",
      "dcty": "Dallas",     "dst": "TX",
      "equip": "Reefer",
      "ratePay": 2400,
      "pickupDate": "2026-07-10T00:00:00.000Z",
      "deliveryDate": "2026-07-12T00:00:00.000Z",
      "shipperNm": "Broker Name",
      "weight": 44000,
      "length": 53,
      "loadDist": 783.4,
      "grossRpm": 2.31,
      "o2oDist": 12.1,
      "coordinates": [[lon, lat], ...]
    },
    ...
  ],
  "total": 847,
  "geo": { "olat": 33.748, "olon": -84.387, "dlat": 32.779, "dlon": -96.808 }
}
StatusMeaning
200Loads returned (may be empty array if no results)
429Rate limit exceeded — 30 requests/minute
500Search failed

Authenticated load search with partner carrier matching. Returns the same load shape as the public endpoint, but excludes loads already reserved by your company and applies partner carrier visibility rules.

Auth: Bearer token or session cookie required.

Request body (JSON)

FieldRequiredDescription
octyNoOrigin city name
ostNoOrigin state (2-letter)
dctyNoDestination city name
dstNoDestination state (2-letter)
freightNoEquipment type
pickupDateNoPickup date (YYYY-MM-DD)
deliveryDateNoDelivery date (YYYY-MM-DD)
hoursSliderNoMax extra drive hours (default 10)
milesSliderNoMax deadhead miles (default 750)
gross_rpmNoMinimum gross RPM (default 0)

Example request

POST https://www.truktek.com/api/loads/search
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Content-Type: application/json

{
  "octy": "Atlanta",
  "ost": "GA",
  "dcty": "Dallas",
  "dst": "TX",
  "freight": "Reefer",
  "milesSlider": 300,
  "gross_rpm": 2.0
}

Response shape is identical to GET /api/loads.

StatusMeaning
200Loads returned
401Not authenticated
500Search failed

GET /api/loads/rate-check

Market rate summary for a lane. Samples up to 50 available loads and returns average, min, max, and median rate per mile (RPM) along with average gross pay.

Auth: Bearer token or session cookie required.

Query parameters

ParamRequiredDescription
octyYesOrigin city name
ostYesOrigin state (2-letter)
dctyYesDestination city name
dstYesDestination state (2-letter)
freightNoEquipment type filter (omit for all equipment)
pickupDateNoPickup date (YYYY-MM-DD)

Example request

GET https://www.truktek.com/api/loads/rate-check?octy=Atlanta&ost=GA&dcty=Dallas&dst=TX&freight=Reefer
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Response 200 OK

{
  "lane": "Atlanta, GA → Dallas, TX",
  "equipment": "Reefer",
  "sample_count": 34,
  "avg_rpm": 2.31,
  "min_rpm": 1.87,
  "max_rpm": 3.12,
  "median_rpm": 2.24,
  "avg_pay": 1810
}

If no loads are found, sample_count is 0 and a message field explains why rather than returning rate fields.

StatusMeaning
200Rate data returned (check sample_count — may be 0)
400Missing required params or city geocoding failed
401Not authenticated
404Origin or destination city not found

POST /api/loads/chain

HOS-compliant multi-stop load chain planner. Given an origin, destination, and date window, finds the best sequences of back-to-back loads that minimize deadhead and maximize gross RPM across the full trip. Returns the top chains sorted by overall RPM.

Auth: Bearer token or session cookie required. Requires Pro subscription or trial.

Rate limit: 10 requests per minute per IP. Requests can take 5–30 seconds depending on corridor size — set your timeout accordingly.

Request body (JSON)

FieldRequiredDescription
octyYesOrigin city name
ostYesOrigin state (2-letter)
dctyYesDestination city name
dstYesDestination state (2-letter)
startDateYesEarliest pickup date (YYYY-MM-DD)
endDateYesLatest delivery date (YYYY-MM-DD)
freightNoEquipment type filter (omit for all)
maxDeadheadNoMax empty miles between legs (default 200)

Example request

POST https://www.truktek.com/api/loads/chain
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Content-Type: application/json

{
  "octy": "Seattle",
  "ost": "WA",
  "dcty": "Boston",
  "dst": "MA",
  "startDate": "2026-07-14",
  "endDate": "2026-07-21",
  "freight": "Van",
  "maxDeadhead": 150
}

Response 200 OK

{
  "chains": [
    {
      "loads": [
        {
          "id": "abc123",
          "octy": "Seattle",   "ost": "WA",
          "dcty": "Chicago",   "dst": "IL",
          "origLat": 47.606,   "origLon": -122.332,
          "destLat": 41.878,   "destLon": -87.629,
          "rateAmount": 3200,
          "totalMiles": 2064,
          "pickupDateMs": 1752451200000,
          "deliveryDateMs": 1752624000000,
          "equip": "Van",
          "routeCoords": [[lon, lat], ...]
        },
        {
          "id": "def456",
          "octy": "Chicago",   "ost": "IL",
          "dcty": "Boston",    "dst": "MA",
          ...
        }
      ],
      "totalMiles": 4198,
      "totalGross": 5900,
      "overallRpm": 1.41
    },
    ...
  ],
  "candidateCount": 8423,
  "searchTimeMs": 4821,
  "originLat": 47.606,
  "originLon": -122.332
}

Up to 5 chains are returned, sorted by overallRpm descending. Each chain's loads array is ordered sequentially — leg 1 → leg 2 → … → final leg.candidateCount shows how many loads were evaluated in the search.

StatusMeaning
200Chains returned (chains array may be empty if no valid sequence found)
400Missing required fields or city not found
401Not authenticated
403Trial expired or subscription inactive
429Rate limit exceeded — 10 requests/minute
500Search failed

GET /api/mileage

Returns the driving mileage between two US zip codes using TomTom lane reference data. Requires an active Pro subscription or trial.

Auth: Bearer token or session cookie required.

Query parameters

ParamRequiredDescription
fromYesOrigin zip code (5 digits)
toYesDestination zip code (5 digits)

Example request

GET https://www.truktek.com/api/mileage?from=30301&to=90210
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

curl

curl "https://www.truktek.com/api/mileage?from=30301&to=90210" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."

Response 200 OK

{
  "from": { "zip": "30301", "city": "Atlanta", "state": "GA" },
  "to":   { "zip": "90210", "city": "Beverly Hills", "state": "CA" },
  "miles": 2183,
  "matchedLane": {
    "from": "Atlanta, GA",
    "to": "Los Angeles, CA"
  }
}

matchedLane shows the TomTom reference lane that was used to produce the mileage figure. It may differ slightly from the exact zip pair when no lane originates at the precise zip location.

StatusMeaning
200Miles returned
400Missing or invalid zip code format
401Not authenticated
403Trial expired or subscription inactive
404Zip code not found, or no route available for this zip pair

POST /api/bandit

Streams responses from the Bandit AI freight assistant. Bandit can search loads, check market rates, plan load chains, look up mileage, answer trucking questions, and more. Responses are delivered as Server-Sent Events (SSE).

Auth: Bearer token or session cookie required. Requires Pro subscription or trial.

Request body

FieldRequiredDescription
messageYesThe user's plain-text message
historyNoPrevious conversation turns (OpenAI message array). Pass back whatever was returned in the done event to maintain context across turns.

Example request

POST https://www.truktek.com/api/bandit
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Content-Type: application/json

{
  "message": "Find reefer loads from Atlanta GA to Dallas TX pickup tomorrow",
  "history": []
}

Response — SSE stream

The response is Content-Type: text/event-stream. Each event is a line beginning with data: followed by a JSON object, terminated by .

data: {"type":"tool_start","tool":"search_loads","label":"Searching Atlanta GA → Dallas TX · Reefer…"}

data: {"type":"tool_result","tool":"search_loads","label":"Found 8 loads (8 total)","ok":true}

data: {"type":"text","content":"Found 8 reefer loads from Atlanta to Dallas picking up tomorrow."}

data: {"type":"text","content":" Best rate is $2.31/mi ($1,940 total). Want me to shortlist the top 3?"}

data: {"type":"done","history":[...]}

SSE event types

typeFieldsDescription
textcontentStreamed text token from the model
tool_starttool, labelA tool call is beginning
tool_resulttool, label, okTool call completed (ok=false means an error)
donehistoryTurn complete. Pass history back on the next request to maintain context.
errormessageUnrecoverable error in the agent loop

Reading the stream — JavaScript example

const res = await fetch("https://www.truktek.com/api/bandit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9...",
  },
  body: JSON.stringify({ message: "Find reefer loads Atlanta to Dallas", history: [] }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let history = [];

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  const parts = buffer.split("\n\n");
  buffer = parts.pop() ?? "";

  for (const part of parts) {
    if (!part.startsWith("data: ")) continue;
    const event = JSON.parse(part.slice(6));

    if (event.type === "text") process.stdout.write(event.content);
    if (event.type === "done") history = event.history; // save for next turn
  }
}
StatusMeaning
200Stream opened — SSE events follow
401Not authenticated
403Trial expired or subscription inactive

POST /api/tools/bol

Extracts structured data from a Bill of Lading image or PDF using Gemini Vision. Returns shipper, consignee, addresses, commodity, weight, reference numbers, and special handling instructions as formatted HTML.

Auth: Bearer token or session cookie. Anonymous users get 10 free lifetime uses — authenticated Pro/trial users get unlimited.

Request

Send the file as multipart/form-data with field name file. Supported formats: JPG, PNG, PDF. Max size: 4 MB (compress images before sending if needed).

POST https://www.truktek.com/api/tools/bol
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Content-Type: multipart/form-data

file: <BOL image or PDF>

curl

curl -X POST "https://www.truktek.com/api/tools/bol" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
  -F "file=@/path/to/bol.jpg"

Response 200 OK

{
  "result": "<h3>Shipper</h3><p><strong>Name:</strong> ABC Freight Co</p>...",
  "remaining": null
}

remaining is null for authenticated users (no cap). For anonymous users it shows how many free scans remain. Processing typically takes 10–20 seconds — set your timeout to at least 60 seconds.

StatusMeaning
200Result field contains extracted HTML
400No file provided
401Not authenticated (Bearer token only — anonymous OK for free tier)
402Subscription required or anonymous quota exhausted
429Rate limit exceeded
502Processing service temporarily unavailable — retry

POST /api/tools/invoice

Extracts structured data from a carrier invoice or freight bill. Returns shipper, receiver, load number, invoice/due dates, line item charges, fuel surcharge, accessorials, and total amount due as formatted HTML.

Auth: Same as BOL Scanner — Bearer token or session cookie. 10 free anonymous uses.

Request

POST https://www.truktek.com/api/tools/invoice
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Content-Type: multipart/form-data

file: <invoice image or PDF>

curl

curl -X POST "https://www.truktek.com/api/tools/invoice" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
  -F "file=@/path/to/invoice.pdf"

Response 200 OK

{
  "result": "<h3>Invoice</h3><p><strong>Invoice #:</strong> INV-2024-1089</p>...",
  "remaining": null
}
StatusMeaning
200Result field contains extracted HTML
400No file provided
402Subscription required or anonymous quota exhausted
429Rate limit exceeded
502Processing service temporarily unavailable — retry

Error codes

All error responses return JSON with an error field describing the issue.

// Example error response
{
  "error": "trial_expired"
}
StatusMeaning
400Bad request — missing required fields or invalid format
401Unauthenticated — missing or invalid token
402Payment required — trial expired, subscription inactive, or quota exhausted
403Forbidden — authenticated but not permitted (e.g. not admin)
404Resource not found (e.g. zip code not in database)
429Too many requests — slow down
500Internal server error
502Upstream processing service unavailable — retry after a few seconds

Rate limits

Limits are applied per IP address. Authenticated users share the same limits.

EndpointLimit
POST /api/auth/device10 requests / minute
GET /api/loads30 requests / minute per IP
POST /api/loads/searchNo hard limit — fair use expected
GET /api/loads/rate-checkNo hard limit — fair use expected
POST /api/loads/chain10 requests / minute per IP — can take 5–30s, set timeout ≥ 60s
GET /api/mileageNo hard limit — fair use expected
POST /api/banditNo hard limit — each request can take up to 60 seconds
POST /api/tools/bolNo hard limit — processing takes 10–20s, set timeout ≥ 60s
POST /api/tools/invoiceNo hard limit — processing takes 10–20s, set timeout ≥ 60s

Ready to integrate?

Create a TrukTek account, get approved, and generate your device passphrase from your Profile page. Need a custom integration, higher limits, or Enterprise API access? Talk to us.