Developers

API Reference

Track ocean and air shipments programmatically — start trackings, follow live milestones, and read full container timelines over a simple REST API.

Base URL  https://api.navlo.ioAPI  v1Format  JSON

01 · Overview

The shipment tracking API

Navlo exposes the same tracking engine that powers the app through a public REST API: start tracking a container, B/L, booking or AWB number, list your shipments, and read full detail — containers, ports, dates and the merged event timeline. Requests authenticate with organization-level keys created in Settings → API by an organization admin.

Quick start

  1. 1Create a key. In the app, open Settings → API and create a key with the REST API scope. The full key is shown once — store it in your secret manager.
  2. 2Start tracking. POST /v1/shipments with the identifier. Navlo detects the carrier and begins pulling data.
  3. 3Read the shipment. Poll GET /v1/shipments/{id} to follow progress — status moves from discovering to active as carrier data arrives.

02 · Authentication

Authentication

Every request carries your key in the X-API-Key header.

  • Base URL: https://api.navlo.io — all paths on this page are relative to it, and all request and response bodies are JSON.
  • REST keys start with nvl_ and are created in the API keys section of the app. The full key is shown once, at creation.
  • Requests act as the key's creator: shipments started through the API appear in the app under their name and draw from the organization's credit pool.
  • Revoking a key blocks it immediately.

Example

curl https://api.navlo.io/v1/shipments \
  -H "X-API-Key: nvl_your_key_here"

03 · Rate limit & errors

Rate limit & errors

60 requests per minute per key. Errors are JSON with a single error field.

401Missing, invalid or revoked key — {"error": "Invalid or revoked API key"}
404Shipment id doesn't exist or belongs to another organization (empty body).
429More than 60 requests in the current minute — {"error": "Rate limit exceeded (60 requests/minute)"}. Back off and retry the next minute.

04 · REST endpoints

Start tracking

POST/v1/shipmentsStart tracking an identifier

Body fields

identifierrequiredContainer, B/L, booking or AWB number.
modeoptional"OCEAN" (default) or "AIR".
carrieroptionalCarrier code (e.g. MAERSK, MSC) or the carrier in your own wording, like "Hapag Lloyd" or "MAERSK LINE A/S" — known names resolve instantly, Navlo AI maps new wordings, and anything unresolvable is still accepted and auto-detected from the identifier (never rejected). For AIR shipments the field is informational only — the carrier is detected from the AWB prefix.
  • Idempotent: re-posting an identifier you already track returns your existing shipment — no duplicate, no extra charge.
  • A new identifier uses one credit from the organization pool (free when a teammate already tracks the same identifier).

Request

curl -X POST https://api.navlo.io/v1/shipments \
  -H "X-API-Key: nvl_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"identifier": "DEMO1234567", "mode": "OCEAN"}'

Response — 201 Created

{
  "id": "6b1f0c9a-4d2e-4f7b-9a3c-8e5d21f0a4b7",
  "identifier": "DEMO1234567",
  "mode": "OCEAN",
  "status": "discovering",
  "carrier": null,
  "createdAt": "2026-08-01T09:12:44Z"
}

carrier fills in once carrier data arrives — poll the detail endpoint to follow progress.

04 · REST endpoints

Batch start tracking

Bulk-push up to 50 shipments in one call — built for integration platforms. The whole batch counts as a single request against the rate limit.

POST/v1/shipments/batchStart tracking up to 50 identifiers

Body fields

shipmentsrequiredArray of shipments to start tracking — 1 to 50 items. An empty array or more than 50 items returns 400.
shipments[].identifierrequiredContainer, B/L, booking or AWB number.
shipments[].modeoptional"OCEAN" (default) or "AIR".
shipments[].carrieroptionalCarrier code or the carrier in your own wording — resolved exactly like the single-shipment endpoint; unresolvable wordings are still accepted and the carrier is auto-detected from the identifier.

Request

curl -X POST https://api.navlo.io/v1/shipments/batch \
  -H "X-API-Key: nvl_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"shipments": [
    {"identifier": "MEDU1234567", "carrier": "MSC"},
    {"identifier": "BKNG9876543", "carrier": "Hapag Lloyd"},
    {"identifier": "176-12345678", "mode": "AIR"}
  ]}'

Response — 200 OK

{
  "submitted": 3, "created": 2, "existing": 1, "errors": 0,
  "results": [
    { "identifier": "MEDU1234567", "status": "created", "id": "9f4b1c2e-demo-uuid" },
    { "identifier": "BKNG9876543", "status": "existing", "id": "5a3d8e0f-demo-uuid" },
    { "identifier": "176-12345678", "status": "created", "id": "c7e2a941-demo-uuid" }
  ]
}

Per-item status

createdNew shipment started — uses one credit from the organization pool.
existingYou already track this identifier — no charge; the id of your existing shipment is returned.
errorThe item failed — error says why (e.g. "Insufficient credits"). Items fail individually; the rest of the batch is unaffected.

04 · REST endpoints

List shipments

GET/v1/shipmentsPaginated shipment list

Query parameters

pageoptionalZero-based page index. Default 0.
sizeoptionalPage size. Default 20, maximum 100.

Request

curl "https://api.navlo.io/v1/shipments?page=0&size=20" \
  -H "X-API-Key: nvl_your_key_here"

Response — 200 OK

{
  "page": 0,
  "size": 20,
  "totalElements": 143,
  "totalPages": 8,
  "shipments": [
    {
      "id": "8f41c2e5-5b0a-4a4e-9d3b-2f6a8e9c1d07",
      "identifier": "DEMO7654321",
      "mode": "OCEAN",
      "status": "active",
      "carrier": "MSC",
      "createdAt": "2026-07-18T14:03:11Z"
    }
  ]
}

04 · REST endpoints

Shipment detail

GET/v1/shipments/{id}Full shipment detail

Path parameters

idrequiredThe shipment UUID from the create or list response.

Returns the shipment's containers with their dates and ports, plus the merged event timeline across all containers — oldest first, up to 200 events per container.

Request

curl https://api.navlo.io/v1/shipments/8f41c2e5-5b0a-4a4e-9d3b-2f6a8e9c1d07 \
  -H "X-API-Key: nvl_your_key_here"

Response — 200 OK

{
  "id": "8f41c2e5-5b0a-4a4e-9d3b-2f6a8e9c1d07",
  "identifier": "DEMO7654321",
  "mode": "OCEAN",
  "status": "active",
  "carrier": "MSC",
  "containers": [
    {
      "containerNumber": "DEMO7654321",
      "containerType": "40HC",
      "status": "DISCHARGED",
      "etd": "2026-07-02T18:00:00Z",
      "atd": "2026-07-02T21:47:00Z",
      "eta": "2026-07-28T06:00:00Z",
      "ata": "2026-07-27T22:15:00Z",
      "origin": { "code": "CNSHA", "name": "Shanghai" },
      "destination": { "code": "NLRTM", "name": "Rotterdam" },
      "currentVessel": "MSC MARTINA"
    }
  ],
  "events": [
    {
      "timestamp": "2026-07-02T21:47:00Z",
      "type": "ACTUAL",
      "status": "DEPARTED",
      "location": "Shanghai",
      "locationCode": "CNSHA",
      "vessel": "MSC MARTINA",
      "voyage": "428W"
    },
    {
      "timestamp": "2026-07-27T22:15:00Z",
      "type": "ACTUAL",
      "status": "ARRIVED",
      "location": "Rotterdam",
      "locationCode": "NLRTM",
      "vessel": "MSC MARTINA",
      "voyage": "428W"
    },
    {
      "timestamp": "2026-07-28T08:31:00Z",
      "type": "ACTUAL",
      "status": "DISCHARGED",
      "location": "Rotterdam",
      "locationCode": "NLRTM",
      "vessel": null,
      "voyage": null
    },
    {
      "timestamp": "2026-07-30T10:00:00Z",
      "type": "EXPECTED",
      "status": "GATE_OUT",
      "location": "Rotterdam",
      "locationCode": "NLRTM",
      "vessel": null,
      "voyage": null
    }
  ]
}

Field notes

containers[].statusCurrent milestone code for the container (e.g. GATE_OUT, DISCHARGED).
events[].statusNavlo's normalized status when the carrier event is mapped; otherwise the carrier's own status text.
events[].typeACTUAL — the event has happened. EXPECTED — a carrier projection that may still change. Null when the carrier doesn't say.
origin / destinationPort code (UN/LOCODE for ocean, IATA for air) and port name. Fields are null until the carrier reports them.

05 · Statuses & timestamps

Statuses & timestamps

What the status values mean, and how to read date-times.

Shipment status

discoveringNavlo is identifying the carrier and pulling the first data. Shipments stay here while no carrier data has been found yet.
resolvingCarrier found — the first full dataset is being resolved.
activeTracked and refreshed on Navlo's polling schedule.
completedThe shipment reached its final milestone; polling has ended.
stoppedTracking was stopped — by a user, or by Navlo support for identifiers that turn out to be untrackable.
Reading timestamps. All timestamps are ISO-8601. createdAt is UTC. Carrier-sourced times — container ETD/ATD/ETA/ATA and event timestamps — carry the carrier's reported local port time with a Z suffix: display them verbatim and don't shift them into the viewer's timezone.

Questions or missing capability? contact@navlo.io