Developers
API Reference
Track ocean and air shipments programmatically — start trackings, follow live milestones, and read full container timelines over a simple REST API.
https://api.navlo.ioAPI v1Format JSON01 · 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
- 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.
- 2Start tracking.
POST /v1/shipmentswith the identifier. Navlo detects the carrier and begins pulling data. - 3Read the shipment. Poll
GET /v1/shipments/{id}to follow progress — status moves fromdiscoveringtoactiveas 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
/v1/shipmentsStart tracking an identifierBody 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.
/v1/shipments/batchStart tracking up to 50 identifiersBody 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
/v1/shipmentsPaginated shipment listQuery 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
/v1/shipments/{id}Full shipment detailPath 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.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