Singapore Port Statistics Scraper: MPA Throughput & Vessel Data
Pricing
from $1.20 / 1,000 port statistic scrapeds
Singapore Port Statistics Scraper: MPA Throughput & Vessel Data
Scrape monthly container throughput, vessel arrivals and shipping tonnage for the Port of Singapore from MPA's open data on data.gov.sg, back to 1995.
Pricing
from $1.20 / 1,000 port statistic scrapeds
Rating
0.0
(0)
Developer
Arman Hossain
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share

Pulls the Maritime and Port Authority of Singapore's monthly port statistics from Singapore's official open data portal: container throughput, vessel arrivals (total and broken down by vessel type), and Singapore Registry of Ships tonnage. The series run monthly back to 1995 for the Port of Singapore, the world's second-largest container port and its largest bunkering port.
This Actor needs no browser, no proxy and no login to run. Point it at one or more of the four built-in series, or at any other data.gov.sg CKAN table by resource id, and it returns clean, typed rows.
Agent skill: SKILL.md
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/singapore-port-statistics-scraper.md
What you get
| Field | What it holds |
|---|---|
dataset | Which series the row came from: container_throughput, vessel_arrivals_total, vessel_arrivals_by_type, registered_vessels_tonnage, or a custom resource id |
datasetLabel | Human-readable name for the series (Custom for a caller-supplied resource id) |
resourceId | The underlying data.gov.sg resource id this row came from |
id | The source's own row id, stable across runs |
month | The reporting month, YYYY-MM |
vesselType | Vessel category (e.g. Container, Tanker, Tug); only set on vessel_arrivals_by_type rows, otherwise null |
containerThroughputThousandTeu | Container throughput for the month, in thousand TEUs; only set on container_throughput rows |
numberOfVessels | Vessel count for the month; set on every series except container_throughput |
grossTonnageThousandGt | Gross tonnage for the month, in thousand GT; set on every series except container_throughput |
scrapedAt | Run timestamp |
RUN_SUMMARY in the key-value store holds per-dataset row counts, the totals available at the source, the filters you used, and any dataset that failed to fetch.
Use cases
- Trade and transhipment analysis. Container throughput is a standard proxy for Asia-Pacific trade flow and transhipment activity.
- Port-equity and comparables research. Feed PSA Singapore volumes into a comparables model against other major container ports.
- Bunker and commodity desks. Vessel arrival counts and gross tonnage track activity at the world's largest bunkering port.
- Macro dashboards. A monthly, decades-long series with no gaps back to 1995 for trade-cycle and shipping-cycle tracking.
- Fleet and registry research. Singapore Registry of Ships counts and tonnage for flag-state and maritime-registry studies.
Quick start
Every built-in series, full history:
{}
Just container throughput, capped to the most recent 24 months:
{"datasets": ["container_throughput"],"maxRecordsPerDataset": 24}
A specific window across two series:
{"datasets": ["container_throughput", "vessel_arrivals_total"],"fromMonth": "2015-01","toMonth": "2024-12"}
Input
| Field | Type | Default | Notes |
|---|---|---|---|
datasets | array | all four | Which built-in series to pull: container_throughput, vessel_arrivals_total, vessel_arrivals_by_type, registered_vessels_tonnage. An empty list means all four. |
resourceIds | array | [] | Any other data.gov.sg CKAN resource id (the d_... string from a dataset's API panel), read the same way and labelled Custom. |
fromMonth | string | none | Earliest month to keep, YYYY-MM. Applied after fetching, since the source has no date-range parameter. |
toMonth | string | none | Latest month to keep, YYYY-MM. |
maxRecordsPerDataset | integer | 0 (no limit) | Cap rows saved per dataset, keeping the most recent months first when the cap is smaller than the full series. |
Nothing is required. Running with no input returns the full history of all four series, about 4,100 rows in total.
Output example
A container throughput record:
{"dataset": "container_throughput","datasetLabel": "Container throughput","resourceId": "d_da030f7028200d19ffcbe4a2d71af39c","id": 377,"month": "2026-05","vesselType": null,"containerThroughputThousandTeu": 3942.74,"numberOfVessels": null,"grossTonnageThousandGt": null,"scrapedAt": "2026-08-21T22:47:54.897Z"}
A vessel-arrivals-by-type record, same shape, different fields populated:
{"dataset": "vessel_arrivals_by_type","datasetLabel": "Vessel arrivals, by vessel type","resourceId": "d_8f264219109e61fffa87ac64dd5a9a65","id": 3016,"month": "2026-05","vesselType": "Miscellaneous","containerThroughputThousandTeu": null,"numberOfVessels": 883,"grossTonnageThousandGt": 13723.56,"scrapedAt": "2026-08-21T22:47:54.897Z"}
Reading a dataset key
datasets value | Series |
|---|---|
container_throughput | Monthly container throughput, thousand TEUs |
vessel_arrivals_total | Monthly vessel arrivals: count and total gross tonnage |
vessel_arrivals_by_type | The same arrivals, broken down into several rows per month by vessel type |
registered_vessels_tonnage | Singapore Registry of Ships: monthly registered ship count and gross tonnage |
vessel_arrivals_by_type carries roughly 8 rows per month (one per vessel category), so its total row count is much larger than the other three series, which are one row per month.
API example
curl -X POST "https://api.apify.com/v2/acts/arman-bd~singapore-port-statistics-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"datasets": ["container_throughput"],"fromMonth": "2015-01"}'
JavaScript example
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('arman-bd/singapore-port-statistics-scraper').call({datasets: ['container_throughput', 'vessel_arrivals_total'],fromMonth: '2020-01',});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const row of items) console.log(row.month, row.dataset, row.containerThroughputThousandTeu ?? row.numberOfVessels);
Notes
- Numbers arrive from the source as strings and are cast to numbers here; a value the Actor cannot parse comes through as
nullrather than a bad number. container_throughput,vessel_arrivals_totalandregistered_vessels_tonnageare one row per month.vessel_arrivals_by_typeis several rows per month, one per vessel category, somaxRecordsPerDatasetcaps rows, not months, on that series.- Datasets are read one at a time with a short delay between them, and a failed fetch is retried automatically before it is recorded as a failure.
monthhas no timezone; it is the calendar month MPA published the figures for, not a timestamp.- The latest month available typically lags the calendar by 2-3 months, since MPA needs time to finalise the figures.
- One bad dataset key or resource id is recorded in
RUN_SUMMARY.failureswhile the rest of the run continues. The run only fails outright if every requested dataset failed.
FAQ
Do I need an API key? No. The Actor supplies no credentials and needs none.
Can I get daily or weekly figures? No. MPA publishes these series monthly; there is no finer granularity at the source.
Why is containerThroughputThousandTeu null on most rows? Each series has its own metric. Container throughput only appears on container_throughput rows; vessel counts and tonnage appear on the other three.
What does "thousand TEU" and "thousand GT" mean? Thousand twenty-foot equivalent units (the standard container-volume measure) and thousand gross tons (a vessel-size measure), which is how MPA reports these figures.
Can I pull a table this Actor doesn't already know about? Yes, if it is a data.gov.sg CKAN datastore resource: pass its resource id in resourceIds. Field names vary by table, so only fields matching the four built-in series' conventions (month, container_throughput, number_of_vessels, gross_tonnage, vessel_type) are recognised; anything else comes through as null.
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.