---
title: "Ship tracking"
description: "Find any vessel and follow its position, voyage and route, and register your own shipments by tracking, B/L or container number so their vessel is followed for you. Scope: ships, granted per contract — not on a new key by default. Shipments are scoped to the key that registered them."
canonical: "https://thecarapi.com/docs/ships"
contract_version: "2026-10-05"
api_base: "https://api.thecarapi.com"
source: "https://thecarapi.com/docs/ships.md"
---

# Ship tracking

Find any vessel and follow its position, voyage and route, and register your own shipments by tracking, B/L or container number so their vessel is followed for you. Scope: ships, granted per contract — not on a new key by default. Shipments are scoped to the key that registered them.

## Following a shipment

1. Register it with POST /api/shipments, giving the vessel as mmsi, imo or vessel_name plus whichever of your own numbers you want to search by later. A name is resolved to an MMSI; vessel_resolved says whether that worked.
2. Read GET /api/shipments/{shipment_id} for the shipment, its vessel exactly as /api/vessels/{mmsi} returns it, and the route since you registered it.
3. Mark it arrived with PATCH (status: arrived) or stop following it with DELETE. Either way it stays readable.

## GET /api/vessels/search

Find vessels by name, MMSI, IMO or call sign.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `q` | Query string | string | yes | `MSC OSCAR` | What your user typed. | Exact MMSI (9 digits), IMO (7 digits, IMO prefix optional), call sign, or any part of the name. At least 3 characters. |
| `limit` | Query string | integer | no | `20` | How many results you show. | Default 20, maximum 100. |
| `online` | Query string | boolean | no | `false` | Your latency choice. | Default true: when nothing is known and q is a name, the exact name is looked up on a public vessel directory and remembered (source: vesselfinder). false answers from what we already know. |

### Example requests

```http
GET https://api.thecarapi.com/api/vessels/search?q=MSC%20OSCAR
```

```http
GET https://api.thecarapi.com/api/vessels/search?q=9703291
```

### Example response

```json
{
  "success": true,
  "source": "known",
  "vessels": [
    {
      "mmsi": "355906000",
      "imo": "9703291",
      "name": "MSC OSCAR",
      "call_sign": "3FOB4",
      "ship_type": "cargo",
      "length_m": 395,
      "width_m": 59,
      "destination": "ROTTERDAM",
      "last_position": {
        "latitude": 51.95,
        "longitude": 4.05,
        "speed_knots": 0.1,
        "time": "2026-10-18T09:12:00Z"
      }
    }
  ],
  "contract_version": "2026-10-05"
}
```

### Response fields

| Field | Type | Meaning |
| --- | --- | --- |
| `source` | enum \| null | known (from our AIS feed and earlier look-ups) or vesselfinder (found by the online look-up just now). null when nothing matched. |
| `vessels[]` | object[] | mmsi, imo, name, call_sign, ship_type, length_m, width_m, destination and last_position { latitude, longitude, speed_knots, time } or null. |

## GET /api/vessels/{mmsi}

Everything known about one vessel: identity, declared voyage, latest position and how much track we hold.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `mmsi` | URL path | string | yes | `355906000` | /api/vessels/search -> vessels[].mmsi | The vessel’s 9-digit MMSI. |
| `refresh` | Query string | boolean | no | `false` | Your latency choice. | Default true: when our latest fix is older than 30 minutes it is refreshed from a public AIS source during the request. false answers from what is stored. |

### Example requests

```http
GET https://api.thecarapi.com/api/vessels/355906000
```

```http
GET https://api.thecarapi.com/api/vessels/355906000?refresh=false
```

### Example response

```json
{
  "success": true,
  "vessel": {
    "mmsi": "355906000",
    "imo": "9703291",
    "name": "MSC OSCAR",
    "call_sign": "3FOB4",
    "ship_type": "cargo",
    "length_m": 395,
    "width_m": 59,
    "max_draught_m": 16,
    "declared_destination": "ROTTERDAM",
    "declared_eta": "2026-10-19T06:00:00Z",
    "position": {
      "latitude": 51.95,
      "longitude": 4.05,
      "speed_knots": 0.1,
      "course": 182,
      "heading": 180,
      "nav_status": "moored",
      "area": "North Sea",
      "time": "2026-10-18T09:12:00Z",
      "source": "AISStream"
    },
    "voyage": {
      "departure_port": "Algeciras",
      "departed_at": "2026-10-14T21:30:00Z",
      "progress_pct": 96
    },
    "track": {
      "points": 1840,
      "first": "2026-08-01T00:04:00Z",
      "last": "2026-10-18T09:12:00Z"
    },
    "tracked_live": true
  },
  "contract_version": "2026-10-05"
}
```

### Response fields

| Field | Type | Meaning |
| --- | --- | --- |
| `position` | object \| null | Latest fix: latitude, longitude, speed_knots, course, heading, nav_status, area, time and source. |
| `voyage` | object | departure_port, departed_at and progress_pct where known. |
| `track` | object | How many track points we hold and the first and last of them. Read them with /api/vessels/{mmsi}/track. |
| `tracked_live` | boolean | Whether the vessel is in the live AIS subscription right now. |

**Notes.** A request can reach upstream when the stored fix is stale, so it can take a few seconds; pass refresh=false where freshness does not matter. 404 when no vessel with that MMSI is known or could be found.

## GET /api/vessels/{mmsi}/track

The vessel’s recorded route, oldest first.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `mmsi` | URL path | string | yes | `355906000` | /api/vessels/search -> vessels[].mmsi | The vessel’s 9-digit MMSI. |
| `since` | Query string | string | no | `2026-10-01` | The start of the window you draw. | ISO 8601 date or datetime. |
| `limit` | Query string | integer | no | `500` | How many points your map can draw. | The track is thinned evenly to at most this many points. Default 500, maximum 5000. |

### Example requests

```http
GET https://api.thecarapi.com/api/vessels/355906000/track?since=2026-10-01&limit=500
```

### Example response

```json
{
  "success": true,
  "mmsi": "355906000",
  "track": [
    {
      "latitude": 36.1,
      "longitude": -5.4,
      "speed_knots": 14.2,
      "course": 12,
      "time": "2026-10-14T22:00:00Z"
    }
  ],
  "contract_version": "2026-10-05"
}
```

## GET /api/shipments

The calling key’s shipments, newest first.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `q` | Query string | string | no | `MSCU1234567` | Any number you registered. | Matches the reference, tracking / B/L / container / booking number, vessel name, MMSI or IMO, ignoring case and punctuation. |
| `status` | Query string | enum | no | `active` | active, arrived or cancelled. | Only shipments in this state. |
| `limit / offset` | Query string | integer | no | `50 / 0` | Pagination state in your application. | Page size and row offset. |

### Example requests

```http
GET https://api.thecarapi.com/api/shipments?status=active
```

```http
GET https://api.thecarapi.com/api/shipments?q=MSCU1234567
```

### Example response

```json
{
  "success": true,
  "shipments": [
    {
      "id": 42,
      "reference": "PO-1189",
      "container_number": "MSCU1234567",
      "vessel_name": "MSC OSCAR",
      "mmsi": "355906000",
      "imo": "9703291",
      "destination_port": "Varna",
      "eta": "2026-11-02",
      "status": "active",
      "vessel_resolved": true,
      "created_at": "2026-10-12T08:00:00Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0,
  "contract_version": "2026-10-05"
}
```

**Notes.** Shipments are scoped to the key that created them: another key, even on the same account, does not see them.

## POST /api/shipments

Register a shipment so its vessel is followed.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `mmsi / imo / vessel_name` | JSON body | string | no | `355906000` | /api/vessels/search, or your booking paperwork. | The vessel, given any one way. A name or IMO is resolved to an MMSI; vessel_resolved in the response says whether that worked. |
| `reference` | JSON body | string | no | `PO-1189` | Your own reference. | Free text to search by later. |
| `tracking_number / bl_number / container_number / booking_number` | JSON body | string | no | `MSCU1234567` | Your shipping documents. | Numbers to search by later. Container numbers are upper-cased and stripped of punctuation. |
| `carrier` | JSON body | string | no | `MSC` | Your shipping documents. | The shipping line. |
| `origin_port / destination_port` | JSON body | string | no | `Antwerp / Varna` | Your shipping documents. | Free-text ports. |
| `eta` | JSON body | string | no | `2026-11-02` | Your shipping documents. | Expected arrival, ISO date. The vessel is followed until 14 days after it (90 days after creation without one). |
| `notes` | JSON body | string | no | `2 cars, BMW 320d and Audi A4` | Anything you want to keep with it. | Free text. |

### Example requests

```http
POST https://api.thecarapi.com/api/shipments
{"reference": "PO-1189", "container_number": "MSCU1234567", "vessel_name": "MSC OSCAR", "destination_port": "Varna", "eta": "2026-11-02"}
```

### Example response

```json
{
  "success": true,
  "shipment": {
    "id": 42,
    "reference": "PO-1189",
    "container_number": "MSCU1234567",
    "vessel_name": "MSC OSCAR",
    "mmsi": "355906000",
    "status": "active",
    "vessel_resolved": true
  },
  "contract_version": "2026-10-05"
}
```

**Notes.** Answers 201. Active shipments are followed by the live AIS feed within a system-wide 50-vessel subscription that our own shipments fill first. At most 25 active shipments per key — a 26th is a 409.

## GET /api/shipments/{shipment_id}

One shipment with its vessel and its route since registration.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `shipment_id` | URL path | integer | yes | `42` | /api/shipments -> shipments[].id | The shipment id. |
| `track_limit` | Query string | integer | no | `500` | How many points your map can draw. | Track points, thinned evenly. Default 500, maximum 5000. |

### Example requests

```http
GET https://api.thecarapi.com/api/shipments/42
```

### Example response

```json
{
  "success": true,
  "shipment": {
    "id": 42,
    "reference": "PO-1189",
    "status": "active",
    "mmsi": "355906000",
    "vessel": {
      "mmsi": "355906000",
      "name": "MSC OSCAR",
      "position": {
        "latitude": 51.95,
        "longitude": 4.05
      }
    },
    "track": []
  },
  "contract_version": "2026-10-05"
}
```

**Notes.** 404 when the shipment does not exist or belongs to another key.

## PATCH /api/shipments/{shipment_id}

Change a shipment.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `shipment_id` | URL path | integer | yes | `42` | /api/shipments -> shipments[].id | The shipment id. |
| `status` | JSON body | enum | no | `arrived` | active, arrived or cancelled. | The new state. |
| `any create field` | JSON body | string | no | `eta` | POST /api/shipments body fields. | Any field from the create body. A new vessel name or IMO re-resolves the MMSI. |

### Example requests

```http
PATCH https://api.thecarapi.com/api/shipments/42
{"status": "arrived"}
```

### Example response

```json
{
  "success": true,
  "shipment": {
    "id": 42,
    "status": "arrived"
  },
  "contract_version": "2026-10-05"
}
```

## DELETE /api/shipments/{shipment_id}

Stop following a shipment.

**Scope:** `ships`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `shipment_id` | URL path | integer | yes | `42` | /api/shipments -> shipments[].id | The shipment id. |

### Example requests

```http
DELETE https://api.thecarapi.com/api/shipments/42
```

### Example response

```json
{
  "success": true,
  "shipment": {
    "id": 42,
    "status": "cancelled"
  },
  "contract_version": "2026-10-05"
}
```

**Notes.** Marks it cancelled; it stays readable.
