# Ocean Container Tracker - MSC, ONE, COSCO & Yang Ming (`neverempty/ocean-container-tracker`) Actor

Track containers, bills of lading and booking numbers from MSC, ONE, COSCO and Yang Ming in one run: current status and location, full event history, ETA, vessel and voyage. The carrier is found from the number. Watch mode returns only changes. Unofficial.

- **URL**: https://apify.com/neverempty/ocean-container-tracker.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.50 / 1,000 tracking result returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Ocean Container Tracker - MSC, ONE, COSCO & Yang Ming

Track ocean containers, bills of lading and booking numbers from **MSC**, **ONE (Ocean Network Express)**, **COSCO Shipping** and **Yang Ming** in one run. Paste your numbers, mixed from any of the four carriers, and get one row per number with the current status and location, the last event time, the ETA, the vessel and voyage, the ports of loading and discharge, the transshipment ports and the full event history. The carrier is found from the number, so you do not have to say who it belongs to.

Turn on **watch mode** and schedule the Actor: each run returns the full row only for numbers that have a new event or a changed ETA since they were last returned, with the new events and the ETA before and after listed. Every other number is a short check row at a fraction of the price, and you are never charged twice for the same update.

### What you can do with it

- **Track every open shipment in one place**, whatever carrier it is on, and send the rows to a sheet, a TMS, Slack or an alert.
- **Get told when a shipment moves**: discharged, transshipped, gated out, empty returned, or the ETA slipped by days.
- **Check a bill of lading or booking at once**: every container on it comes back with its own type and event history.
- **Keep customers informed** with the vessel, voyage and ETA from the carrier instead of copying them by hand from four websites.

### What you get for each number

| Field | Example |
|---|---|
| `trackingNumber` / `numberType` | `MSMU8698933` / `container` (also `bill-of-lading`, `booking`) |
| `carrier` / `carrierCode` | `MSC` / `msc` |
| `currentStatus` / `currentLocation` / `currentFacility` | `Full Transshipment Loaded` / `COEGA, ZA` / the terminal or yard |
| `lastEventAt` | `2026-10-17` (local time at that port) |
| `eta` / `delivered` | `2026-11-10` / `false` |
| `vesselName` / `voyage` | `MSC NURIA` / `UF642R` |
| `portOfLoading` / `portOfDischarge` | `ENNORE, IN` / `NEW YORK, US` |
| `origin` / `destination` | where the shipment starts and ends, when the carrier shows them |
| `transshipments` | `["COLOMBO, LK", "COEGA, ZA"]` |
| `containerCount` / `containers` | each container with its number, type (`40' HIGH CUBE`, `20' DRY VAN`), ETA and events |
| `eventCount` / `events` | every event with `at`, `description`, `location`, `locationCode`, `facility`, `vessel`, `voyage`, `isActual` (planned events are `false`) |
| `billOfLadingNumbers` / `bookingNumbers` | the related numbers the carrier shows |
| `changes` | watch mode: `newEvents`, `newEventCount`, `etaChanged`, `etaBefore`, `etaNow`, `statusBefore`, `statusNow` |
| `checkDigitValid` | whether the container number's check digit is right |
| `trackingUrl`, `scrapedAt`, `note` | the carrier's tracking page for that number, when it was checked, and a note when there is something to say |

Values the carrier does not show are `null`, never guessed.

### How to use it

**Paste your numbers.** Put container numbers (`MSCU0203658`), bills of lading (`MEDUEZ472303`) or booking numbers into **Tracking numbers**, one per line. Spaces and dashes are ignored. Container numbers are sent to the carrier that owns the prefix (`MSCU`, `MEDU`, `ONEU`, `CSNU`, `CBHU`, `YMLU`, ...). Leased containers (`TGHU`, `TCLU`, ...) and other numbers are asked of each carrier in turn, and the carrier that knows the number answers.

**Say the carrier when you know it.** Write it in front of a number (`one:NB6BS4513400`, `cosco:6438012340`) or set **Carrier** for the whole run. This saves a few lookups for numbers without a prefix.

**Watch mode.** Switch on **Watch mode** and add a schedule (hourly or daily). Run 1 remembers each number's events and ETA and returns one short `watch-started` row per number. From run 2 on, a number with an event that was not there before or a new ETA is returned with its full history as a tracking result. A number with no change returns a short `no-new-since-last-run` row with its status and ETA. Numbers that did not fit under your spending limit are not remembered, so they come back on the next run. **Reset watch memory** starts over for the numbers in that run.

### Input

| Field | What it does |
|---|---|
| `trackingNumbers` | Container, bill of lading or booking numbers, one per line. Up to 500 per run. |
| `carrier` | `auto` (default), `msc`, `one`, `cosco` or `yangming`. |
| `monitoringMode` | Watch mode: return only numbers with a new event or a changed ETA. |
| `resetMonitoringState` | Forget what was remembered for the numbers in this run. |

Example input:

```json
{
  "trackingNumbers": ["MSCU0203658", "ONEU0024740", "CSNU1036292", "YMLU3560471"]
}
```

### Pricing

Pay per result, with two events:

- **Tracking result** (`tracking-returned`): one per number returned with its data, with the whole event history and every container on it included. In watch mode, this is a number with a new event or a changed ETA.
- **Tracking check** (`tracking-checked`, watch mode only): one per watched number that was found but had nothing new, including the first watch run. It costs a small part of a tracking result. A number is never charged both events in the same run.

Rows that explain why there is no result (a number no carrier knows, a number that could not be read, an invalid number) are free and say so in their `note` field.

### FAQ

**How do I track an MSC, ONE, COSCO or Yang Ming container by number?**
Paste the container number into `trackingNumbers` and run the Actor. The carrier is found from the prefix, and the row gives the current status and location, the ETA, the vessel and voyage and every event.

**Can I track containers from several carriers in one run?**
Yes. Mix numbers from MSC, ONE, COSCO and Yang Ming in one list. Each number comes back as its own row with the carrier it was found at.

**Can I track a bill of lading or a booking number?**
Yes, for MSC, ONE and COSCO. The row lists every container on the bill or booking, each with its type and its own events.

**How do I get an alert when a container arrives or its ETA changes?**
Turn on `monitoringMode` and schedule the Actor hourly or daily. Numbers with a new event or a new ETA are returned in full, with `changes.newEvents`, `changes.etaBefore` and `changes.etaNow`, so you can send each `ok` row to email, Slack or a webhook. The other numbers come back as short check rows.

**How do I export container tracking to Excel or Google Sheets?**
Run the Actor and download the dataset as CSV or Excel, or connect it to Google Sheets with an Apify integration.

### Good to know

- Carriers: MSC, ONE (Ocean Network Express), COSCO Shipping and Yang Ming. Numbers from Maersk, CMA CGM, Hapag-Lloyd, Evergreen, ZIM, OOCL and HMM are not tracked; they come back as a free row saying no carrier in this Actor knows the number.
- Yang Ming is tracked by container number only. Bills of lading and booking numbers are tracked at MSC, ONE and COSCO.
- A COSCO container number gives the container's latest status. For its full voyage history, track its COSCO bill of lading or booking.
- On a COSCO bill of lading whose containers have since been loaded on a later shipment, their later status is left out of that bill's row, and the `note` says so.
- Times are the local times the carrier prints at each port, written as dates or `YYYY-MM-DDTHH:MM` without a time zone.
- The ETA is the carrier's own estimate, as the carrier shows it. Once the container has arrived, `delivered` and the last event tell you where it is.
- When a carrier refuses or times out, the number comes back as a free row saying it could not be read. Nothing is solved or bypassed; run it again later.
- This Actor is not affiliated with or endorsed by MSC, ONE, COSCO Shipping or Yang Ming.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `trackingNumbers` (type: `array`):

Container numbers (for example MSCU0203658), bills of lading or booking numbers from MSC, ONE (Ocean Network Express), COSCO or Yang Ming, one per line. The carrier is found from the number. To force a carrier for one number, write it in front: one:NB6BS4513400. Up to 500 numbers per run; each number found is one result.

## `carrier` (type: `string`):

Leave on Auto to find the carrier from each number (container prefix first, then each carrier in turn). Pick one carrier to ask only that carrier for every number.

## `monitoringMode` (type: `boolean`):

Return the full row only when a number has a new event or a changed ETA since it was last returned, with the changes listed. Other found numbers (the first watch run, no change) come back as short rows charged as a tracking check. Schedule it hourly or daily.

## `resetMonitoringState` (type: `boolean`):

Forget what was remembered for the numbers in this run. With watch mode on, this run starts a new watch for them; with it off, the memory is only cleared.

## Actor input object example

```json
{
  "trackingNumbers": [
    "MSCU0203658",
    "ONEU0024740",
    "CSNU1036292",
    "YMLU3560471"
  ],
  "carrier": "auto",
  "monitoringMode": false,
  "resetMonitoringState": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per tracking number: carrier, number type, current status, location and facility, last event time, ETA, vessel and voyage, ports of loading and discharge, transshipment ports, the full event history (time, description, location, vessel, voyage, actual or planned) and each container with its type. Watch-mode rows add the new events and the ETA before and after. In watch mode, numbers with nothing new (the first watch run, no change) come back as short rows charged as a tracking check. Numbers no carrier knows, numbers that could not be read and invalid numbers come back as their own rows and are not charged.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "trackingNumbers": [
        "MSCU0203658",
        "ONEU0024740",
        "CSNU1036292",
        "YMLU3560471"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/ocean-container-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "trackingNumbers": [
        "MSCU0203658",
        "ONEU0024740",
        "CSNU1036292",
        "YMLU3560471",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/ocean-container-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "trackingNumbers": [
    "MSCU0203658",
    "ONEU0024740",
    "CSNU1036292",
    "YMLU3560471"
  ]
}' |
apify call neverempty/ocean-container-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/ocean-container-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/13jmBcYe5kQS8zMcP/builds/Ran8zD8IAOP4qCbD7/openapi.json
