NGA World Port Index Scraper: Pub 150, 2,951 Ports
Pricing
from $1.00 / 1,000 port scrapeds
NGA World Port Index Scraper: Pub 150, 2,951 Ports
Scrape the full NGA World Port Index (Pub 150): 2,951 ports and terminals with depths, cranes, pilotage, repair facilities, ISPS security and UN/LOCODE. Filter by name, country, region, type or depth.
Pricing
from $1.00 / 1,000 port scrapeds
Rating
0.0
(0)
Developer
Arman Hossain
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
10 days ago
Last modified
Categories
Share

Pulls the full World Port Index (NGA Publication 150): every port and terminal the US National Geospatial-Intelligence Agency tracks worldwide, one record per port, with over 110 fields covering identity, depths, cranes, pilotage, tugs, repair facilities, ISPS security status, communications and UN/LOCODE. With no input at all you get the complete current edition, 2,951 ports.
Pub 150 is the reference data behind commercial port databases used across maritime logistics, vessel routing and marine insurance. This Actor reads the structured feed directly, so there's no browser, no proxy and no login required to use it.
Agent skill: SKILL.md
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/nga-world-port-index-scraper.md
What you get
One dataset record per port, matching the current World Port Index edition. A sample of the most-used fields:
| Field | What it holds |
|---|---|
portNumber | NGA's stable numeric port identifier |
portName | Port name |
unloCode | UN/LOCODE, where assigned (about 87% of ports) |
countryCode, countryName | ISO country code and name |
regionName, regionNumber | NGA's World Port Index region grouping |
decimalLatitude, decimalLongitude | Signed decimal coordinates |
latitude, longitude | The same position as NGA's own DMS strings (e.g. 51°54'00"N) |
harborSize, harborSizeLabel | Pub 150 code (L/M/S/V) and its decoded label |
harborType, harborTypeLabel | Pub 150 code (e.g. RB) and its decoded label |
shelter, shelterLabel | Shelter afforded, code and decoded label |
chDepth, anDepth, cpDepth, otDepth | Channel, anchorage, cargo pier and oil terminal depths, in meters |
crFixed, crMobile, crFloating, cranesContainer | Crane availability by type |
ptCompulsory, ptAvailable, tugsAssist, tugsSalvage | Pilotage and tug service flags |
repairCode, drydock, railway | Repair capability codes |
portSecurity, etaMessage | ISPS-related fields as published by NGA |
globalId | NGA's persistent GUID for the port |
The full field list runs past 110 entries; every field the source publishes is passed through. Most of the remaining fields are single-letter Pub 150 flags: Y (yes), N (no) or U (unknown/not reported), covering things like ice/swell exposure, cargo handling by type, medical facilities, fuel and water supply, and communications. See the Output example below for the complete shape; NGA's own Pub 150 front matter documents the authoritative meaning of each code.
RUN_SUMMARY in the key-value store holds per-run counts, the filters you used, and any failure.
Use cases
- Port risk pricing. Marine insurers and P&I clubs pulling berth depth, shelter and repair capability per port for underwriting.
- Voyage and routing planning. Vessel routing and husbandry systems checking max vessel length/beam/draft and pilotage requirements before nominating a port.
- Reference data sync. Logistics SaaS platforms keeping a local ports table current against the weekly-corrected Pub 150 edition.
- UN/LOCODE enrichment. Joining shipment or AIS data against
unloCodeto attach depth, crane and facility data a bare LOCODE list doesn't carry. - Country or region coverage checks. Filtering to one country or NGA region to audit terminal capability across a trade lane.
Quick start
The entire World Port Index, nothing filtered:
{}
Every port in the Netherlands with at least a 10 m channel:
{"countries": ["NL"],"minChannelDepth": 10}
River-basin ports named like "port", capped at 50 rows:
{"portNames": ["port"],"harborTypes": ["RB"],"maxPorts": 50}
Input
| Field | Type | Default | Notes |
|---|---|---|---|
portNames | array | [] | Keep ports whose name contains any of these (case-insensitive substring). Empty means every port. |
countries | array | [] | 2-letter ISO country code (exact match) or country name (substring match). |
regionNames | array | [] | Substring match against NGA's World Port Index region (mostly country names, sometimes a coastline like ALASKA or BRAZIL NE COAST). |
harborTypes | array | [] | Exact Pub 150 harbor type code(s): CN, CB, CT, RN, RB, RT, LC, OR, TH. |
minChannelDepth | integer | 0 | Drop ports with a reported channel depth below this many meters, or with no reported depth. 0 keeps everything. |
maxPorts | integer | 0 | Cap on saved ports after filtering. 0 means no limit. |
Nothing is required. Running with no input returns the full 2,951-port dataset.
Filters combine with AND. countries and regionNames are not the same axis: countries matches the port's own country, regionNames matches NGA's World Port Index grouping, which for a few entries splits one country across several region names (e.g. Alaska ports are grouped separately from the rest of the US).
Output example
{"portNumber": 31140,"portName": "Rotterdam","unloCode": "NL RTM","countryCode": "NL","countryName": "Netherlands","regionNumber": 30980,"regionName": "NETHERLANDS","navArea": "I","alternateName": null,"anDepth": "11","chDepth": "11","chartNumber": "37243","cht": "U","cmAir": "Y","cmRadio": "Y","cmRadioTel": "Y","cmRail": "Y","cmTelegraph": "Y","cmTelephone": "Y","cpDepth": "6","crFixed": "Y","crFloating": "Y","crMobile": "Y","cranesContainer": "U","degauss": "Y","dirtyBallast": "Y","dnc": "coa21a, gen21a, h2131140","dodWaterBody": "North Sea; North Atlantic Ocean","drydock": "M","entranceWidth": null,"erIce": "N","erOther": "Y","erSwell": "N","erTide": "N","etaMessage": "Y","firstPortOfEntry": "Y","garbageDisposal": "Y","globalId": "{05C7AD68-A575-481D-90C0-FD17AD560F42}","goodHoldingGround": "Y","harborSize": "L","harborType": "RB","harborUse": "UNK","latitude": "51°54'00\"N","lifts0": "Y","lifts100": "Y","lifts25": "Y","lifts50": "Y","lngTerminalDepth": null,"loAnchor": "N","loBeachMoor": "N","loBreakBulk": "U","loContainer": "U","loDangCargo": "U","loIceMoor": "N","loLiquidBulk": "U","loLongTerm": "U","loMedMoor": "N","loOilTerm": "U","loOther": "U","loRoro": "U","loSolidBulk": "U","loWharves": "Y","longitude": "4°29'00\"E","maxVesselBeam": null,"maxVesselDraft": null,"maxVesselLength": null,"medFacilities": "Y","offMaxVesselBeam": null,"offMaxVesselDraft": null,"offMaxVesselLength": null,"otDepth": "13","overheadLimits": "Y","portSecurity": "U","ptAdvisable": "Y","ptAvailable": null,"ptCompulsory": "Y","ptLocalAssist": null,"publicationNumber": "Sailing Directions Pub. 192 (Enroute) - North Sea","qtOther": "Y","qtPratique": "Y","qtSanitation": "Y","railway": "S","repairCode": "A","s101Enc": "","s121WaterBody": "","s57Enc": null,"searchAndRescue": "U","shelter": "F","srDiving": "U","srElectRepair": "Y","srElectrical": "Y","srIceBreaking": "U","srLongshore": "Y","srNavigEquip": "Y","srSteam": "Y","suAviationFuel": "U","suDeck": "Y","suDiesel": "Y","suEngine": "Y","suFuel": "Y","suProvisions": "Y","suWater": "Y","tide": 1,"tss": "U","tugsAssist": "Y","tugsSalvage": "U","turningArea": "Y","ukcMgmtSystem": "U","usRep": "U","vts": "U","xcoord": 4.483333000000073,"ycoord": 51.900000000000034,"decimalLatitude": 51.900000000000034,"decimalLongitude": 4.483333000000073,"harborSizeLabel": "Large","harborTypeLabel": "River basin","shelterLabel": "Fair","scrapedAt": "2026-08-21T22:47:49.786Z"}
API example
curl -X POST "https://api.apify.com/v2/acts/arman-bd~nga-world-port-index-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"countries": ["NL"],"minChannelDepth": 10}'
JavaScript example
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('arman-bd/nga-world-port-index-scraper').call({harborTypes: ['RB', 'CN'],minChannelDepth: 12,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const p of items) console.log(`${p.portName} (${p.unloCode ?? p.countryCode}): channel ${p.chDepth ?? '?'} m, ${p.harborTypeLabel}`);
Notes
- One run reads the entire dataset in a single request, then filters and caps in-memory, so
portNames,countries,regionNamesandharborTypesall combine with AND against the same 2,951-port list. chDepth,anDepth,cpDepthandotDepthare reported in meters as numeric strings, matching the source. About 12% of ports carry nochDepthat all (null);minChannelDepthdrops those along with any port below the threshold.decimalLatitude/decimalLongitudecome straight from the source's own decimal coordinates, not a DMS parse, and are exact for every one of the 2,951 ports.unloCodeis present for about 87% of ports. Ports without one (mostly smaller anchorages and terminals) still carry every other field.- Pub 150 is corrected weekly; this Actor always reads the current edition, so re-running later can pick up NGA's own corrections.
- A
maxPortsvalue outside 0..2,951 (negative, non-numeric) is refused rather than treated as "unlimited": the run finishes with the failure recorded inRUN_SUMMARYand nothing saved, so a bad cap can never be billed as a full pull.
FAQ
Do I need an API key? No. The source is a public US government dataset (17 USC 105, public domain).
How current is the data? It tracks NGA's live World Port Index edition, corrected weekly. There is no historical or versioned endpoint; each run reflects the current edition.
Can I get just one port? Yes: set portNames to the port's name. Matching is substring, so a distinctive fragment (e.g. "Rotterdam") is enough; a common fragment (e.g. "port") returns several.
Why do some fields show "U" instead of yes/no? U is Pub 150's own "unknown / not reported" value, distinct from N (reported as absent). Pass both through unchanged rather than guessing which one a missing report means.
Does this include berth-level or terminal-level detail below the port record? No. Pub 150 is a port-level index; it does not break a port down into individual berths or terminals.
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.