# Matson Vessel Schedules: Sailings & Cargo Cut-offs (`arman-bd/matson-vessel-schedules-scraper`) Actor

Scrape Matson O/D sailings for Hawaii, Alaska, Guam, Micronesia, South Pacific and China: dates, transit time, vessel/voyage, port rotation and every cargo cut-off (dry, reefer, hazmat, docs).

- **URL**: https://apify.com/arman-bd/matson-vessel-schedules-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 90.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 sailing scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Matson Vessel Schedules Scraper: Sailings & Cargo Cut-offs

![Matson Sailings: Pacific lane sailings with depart and arrive times, transit days and booking cut-offs](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/matson-vessel-schedules-scraper.jpg)

Pulls Matson's own O/D vessel schedule: sailing dates, transit time, vessel and voyage, port rotation, and the full cargo cut-off matrix (dry, reefer, hazmat, hazmat paperwork, documentation, ITN, AMS, VGM, oversize and China-service customs), for any origin-destination pair on Matson's network.

Matson's Jones Act and Pacific trades have almost no independent schedule transparency: Hawaii, Alaska and Guam shippers mostly work from the carrier's own site, one lane at a time. This Actor reads the same structured data the interactive schedule tool itself uses and returns it as one record per sailing, across as many lanes as you give it in a single run: US mainland and inland points, Hawaii, Alaska, Guam, Micronesia, South Pacific and the China/CLX express strings.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/matson-vessel-schedules-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/matson-vessel-schedules-scraper.md
```

### What you get

One record per sailing.

| Field | What it holds |
|---|---|
| `lane` | The requested lane, as `ORIGIN-DESTINATION` |
| `originCode`, `originName` | Matson's origin location code and name |
| `destinationCode`, `destinationName` | Matson's destination location code and name |
| `destinationRegionCode`, `destinationRegion` | Matson's own region grouping for the destination (`HI` Hawaii, `NI` Hawaii neighbor island, `AK` Alaska, `GU` Guam, `FM`/`GO` Micronesia, `SP` South Pacific, `CN` China service, `ML` US mainland, `IR` US inland) |
| `vessel`, `voyageNumber`, `direction` | Parsed out of Matson's own vessel/voyage/direction code |
| `vesselCode`, `vvd` | Matson's short vessel code and the vessel-voyage-direction key |
| `rowNumber` | The sailing's position in Matson's own result order |
| `depart`, `arrive` | Departure and arrival, as Matson's site states them (its own local port time, not normalised to UTC) |
| `transitTime` | Transit time as Matson states it (e.g. `"4 Days"`) |
| `cutoffsIncluded` | Whether the cut-off matrix below was fetched for this sailing |
| `totalTransitDays`, `departureDetail`, `arrivalDetail`, `availability` | The same sailing described from the cut-off lookup: transit days, a fuller departure/arrival string, and cargo availability at destination |
| `dryCutoff`, `reeferCutoff`, `hazCutoff`, `hazPaperCutoff`, `inlandCutoff`, `docCutoff`, `itnCutoff`, `amsCutoff`, `vgmCutoff`, `oversizeCutoff`, `chinaCutoff` | Every cargo cut-off Matson publishes for the sailing. `"N/A"` where Matson itself has nothing to state (most lanes don't carry a China-service or hazmat cut-off, for example) |
| `cutoffNote` | Any free-text note Matson attaches to the cut-offs, usually `null` |
| `portRotation` | The sailing's leg-by-leg rotation: `[{ leg, from, to, vesselVoyage, transitTime, departureDate, arrivalDate }, …]`. One leg for a direct sailing, several for an indirect one (e.g. Honolulu to Ebeye via Guam) |
| `scrapedAt` | Run timestamp |

`RUN_SUMMARY` in the key-value store holds per-lane counts, the filters you used, and any lane that failed.

### Use cases

- **Cargo cut-off tracking.** The whole reason to call this Actor: know exactly when dry, reefer or hazmat cargo has to be at the terminal for a given sailing, across every lane you book.
- **Jones Act capacity monitoring.** Hawaii and Alaska shippers watching vessel assignments and transit times week to week, where alternative sources are thin.
- **CLX/CCX expedited China tracking.** Transpacific e-commerce and retail supply chains that need China-service transit times and cut-offs specifically.
- **Booking deadline alerts.** Feed `dryCutoff`/`reeferCutoff`/`hazCutoff` into a reminder a few hours ahead of the deadline for cargo already booked on a known `vvd`.
- **Lane comparison.** Pull several origin-destination pairs into one run and compare transit time and cut-off timing across them.

### Quick start

The default: Oakland to Honolulu for the next 30 days, cut-offs included.

```json
{
 "lanes": ["OAK-HON"]
}
```

One lane per named region, over a specific window:

```json
{
 "lanes": ["OAK-HON", "TAC-ANK", "LAX-GUM", "HON-EBY", "LAX-SHA"],
 "startDate": "2026-09-01",
 "endDate": "2026-10-01",
 "includeCutoffs": true,
 "maxSailingsPerLane": 10
}
```

A fast schedule-only pull, no cut-off lookups, for a lane you already know:

```json
{
 "lanes": ["LAX-GUM"],
 "includeCutoffs": false,
 "maxSailingsPerLane": 50
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `lanes` | array | `["OAK-HON"]` | One entry per lane, as `ORIGIN-DESTINATION` using Matson's own location codes (`OAK-HON`, `TAC-ANK`, `LAX-GUM`…). `/`, `>`, `,` and the word `to` also work as separators. An origin Matson doesn't recognise, or a destination it doesn't serve from that origin, is recorded in `RUN_SUMMARY.failures` rather than stopping the run. |
| `startDate` | string | today (UTC) | Earliest sailing to include, as `YYYY-MM-DD`. |
| `endDate` | string | `startDate` + 30 days | Latest sailing to include, as `YYYY-MM-DD`. Capped at 180 days after `startDate`; a longer request is clamped and `RUN_SUMMARY.filters.dateRangeClamped` is set to `true`. |
| `includeCutoffs` | boolean | `true` | Look up the full cut-off matrix and port rotation for every sailing found. One extra request per sailing; turn it off for a faster, cheaper schedule-only pull. |
| `maxSailingsPerLane` | integer | `15` | Cap on sailings saved (and charged for) per lane, from 1 to 100. This endpoint doesn't paginate, so this is the only thing bounding a busy lane. |

Nothing is required. Running with no input returns Oakland to Honolulu for the next 30 days.

### Output example

A real record, cut-offs included:

```json
{
 "lane": "OAK-HON",
 "originCode": "OAK",
 "originName": "OAKLAND",
 "destinationCode": "HON",
 "destinationName": "HONOLULU",
 "destinationRegionCode": "HI",
 "destinationRegion": "Hawaii",
 "vessel": "MANULANI",
 "voyageNumber": "269",
 "direction": "W",
 "vesselCode": "MLI",
 "vvd": "MLI269W",
 "rowNumber": 0,
 "depart": "Tuesday 08/25/2026 02:00",
 "arrive": "Saturday 08/29/2026 06:00",
 "transitTime": "4 Days",
 "cutoffsIncluded": true,
 "totalTransitDays": "4 days",
 "departureDetail": "OAK on Tuesday 08/25 at 02:00",
 "arrivalDetail": "HON on Saturday 08/29 at 06:00",
 "availability": "Monday 08/31 at 08:00",
 "dryCutoff": "Mon 23:00 08/24",
 "reeferCutoff": "Mon 23:00 08/24",
 "hazCutoff": "Mon 16:30 08/24",
 "hazPaperCutoff": "Mon 12:00 08/24",
 "inlandCutoff": "N/A",
 "docCutoff": "Mon 12:00 08/24",
 "itnCutoff": "N/A",
 "amsCutoff": "N/A",
 "vgmCutoff": "N/A",
 "oversizeCutoff": "N/A",
 "chinaCutoff": "N/A",
 "cutoffNote": null,
 "portRotation": [
  {
   "leg": 1,
   "from": "OAKLAND",
   "to": "HONOLULU",
   "vesselVoyage": "MANULANI 269 W",
   "transitTime": "4 days",
   "departureDate": "08-25-2026",
   "arrivalDate": "08-29-2026"
  }
 ],
 "scrapedAt": "2026-08-21T22:51:08.362Z"
}
```

An indirect sailing carries more than one leg in `portRotation`, Honolulu to Ebeye via Guam, for example, comes back as two legs rather than one.

### Writing a lane

`lanes` accepts a range of separators for the same pair:

| You write | Actor reads |
|---|---|
| `OAK-HON` | Oakland → Honolulu |
| `OAK/HON`, `OAK,HON`, `OAK>HON` | Oakland → Honolulu |
| `OAK to HON` | Oakland → Honolulu |

Codes are Matson's own 3-4 letter location codes, case-insensitive. The full origin list spans US mainland ports (`OAK`, `LAX`, `SEA`, `TAC`…), US inland points served by rail (`ATLA`, `CHIC`, `DALL`…), Hawaii (`HON`, `HIL`, `KAH`…), Alaska (`ANK`, `DUT`, `KDK`…), Guam (`GUM`), Micronesia (`EBY`, `KWJ`, `MAJ`, `UUK`…), the South Pacific (`APW`, `PPG`, `SUV`…) and the China/CLX-CCX string (`SHA`, `NGB`, `XMN`, `PUS`…). The Actor reads this list fresh from Matson's own schedule tool on every run, so a lane naming a real but currently-unserved pair is recorded in `RUN_SUMMARY.failures` rather than guessed at.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~matson-vessel-schedules-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "lanes": ["OAK-HON", "LAX-GUM"],
 "maxSailingsPerLane": 10
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/matson-vessel-schedules-scraper').call({
 lanes: ['OAK-HON', 'TAC-ANK'],
 startDate: '2026-09-01',
 endDate: '2026-10-01',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const s of items) console.log(`${s.lane} ${s.vessel} ${s.depart} → ${s.arrive}, dry cutoff ${s.dryCutoff}`);
```

### Notes

- `depart`/`arrive` and every cut-off are Matson's own strings, in the port's local time, exactly as its schedule tool states them. They are not normalised to a single timezone or parsed into a machine date, since the source itself mixes date formats between the sailing list and the cut-off matrix (`"Tuesday 08/25/2026 02:00"` vs `"Mon 23:00 08/24"`).
- `"N/A"` in a cut-off field is Matson's own answer, not a missing value: most lanes carry only a handful of the eleven cut-off types.
- Cut-off lookups are per-sailing. A lookup that fails for one sailing is recorded in `RUN_SUMMARY.failures` and that sailing is still saved, with `cutoffsIncluded: false` and its cut-off fields `null`, one bad lookup never drops the row.
- Destinations are validated against Matson's own served-destination list for that origin before a lane is searched, so a real code paired the wrong way (e.g. asking Guam for a destination it doesn't ship to) is reported by name rather than silently returning nothing.
- Requests are serialised, one at a time, across the whole run.
- The origin code list is read fresh from Matson's schedule page every run rather than hard-coded, since the carrier adds and drops ports.

### FAQ

**Do I need a Matson account?** No. You supply no credentials.

**How far ahead can I search?** Matson runs fixed weekly (or less frequent) strings and only loads sailings a few months out. `endDate` is capped at 180 days after `startDate`.

**Why does a lane I know is real return nothing?** Two different things produce an empty result, and `RUN_SUMMARY` tells them apart. A destination not in `RUN_SUMMARY.failures` for that lane means the pair is valid but has no sailing scheduled in your window, widen `startDate`/`endDate`. A lane named in `failures` means the origin or destination code itself wasn't recognised or served.

**Can I get rates or bookings?** No. This is schedule and cut-off data only, read the same way Matson's own interactive tool does.

**What does `portRotation` add over `depart`/`arrive`?** For a direct sailing, nothing new. For an indirect one, it's the only place the intermediate port and its own transit leg show up, `depart`/`arrive` only ever describe the first and last leg.

**Can I plug it into something else?** Yes. Apify API, the client libraries, webhooks, scheduled runs, dataset exports to JSON, CSV or Excel, or MCP. The output is structured JSON.

# Actor input Schema

## `lanes` (type: `array`):

Which Matson trade lanes to pull, one per entry, written as ORIGIN-DESTINATION using Matson's own 3-4 letter location codes, e.g. 'OAK-HON' for Oakland to Honolulu. '/', '>', ',' and the word 'to' also work as separators. Origins span Matson's whole network: US mainland and inland points, Hawaii, Alaska, Guam, Micronesia, South Pacific and the China/CLX express strings. An unrecognised code, or an origin-destination pair Matson does not serve, is recorded in RUN\_SUMMARY.failures instead of stopping the run.

## `startDate` (type: `string`):

Earliest sailing to include, as YYYY-MM-DD. Leave empty to start from today, on the Actor's server clock (UTC).

## `endDate` (type: `string`):

Latest sailing to include, as YYYY-MM-DD. Leave empty for 30 days after 'Start date'. The window is capped at 180 days from 'Start date' to keep one run bounded; a longer request is clamped and RUN\_SUMMARY.filters.dateRangeClamped is set to true.

## `includeCutoffs` (type: `boolean`):

Look up the full cut-off matrix for every sailing found: dry, reefer, hazmat, hazmat paperwork, inland, documentation, ITN, AMS, VGM, oversize and China-service customs cut-offs, plus the port rotation. This is one extra request per sailing, so turning it off is faster and cheaper when you only need the sailing list.

## `maxSailingsPerLane` (type: `integer`):

Cap on sailings saved, and charged for, per lane. Matson does not paginate this endpoint, so this is what bounds both the dataset size and, when cut-offs are on, the number of extra look-ups a busy lane triggers.

## Actor input object example

```json
{
  "lanes": [
    "OAK-HON",
    "TAC-ANK",
    "LAX-GUM",
    "HON-EBY",
    "LAX-SHA"
  ],
  "startDate": "2026-09-01",
  "endDate": "2026-10-01",
  "includeCutoffs": true,
  "maxSailingsPerLane": 15
}
```

# Actor output Schema

## `items` (type: `string`):

Every sailing the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# 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 = {
    "lanes": [
        "OAK-HON"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/matson-vessel-schedules-scraper").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 = { "lanes": ["OAK-HON"] }

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/matson-vessel-schedules-scraper").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 '{
  "lanes": [
    "OAK-HON"
  ]
}' |
apify call arman-bd/matson-vessel-schedules-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/matson-vessel-schedules-scraper"
        }
    }
}

```

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/CXxlb7OE7dodEGcYz/builds/cfWcabsgtK98R0OE3/openapi.json
