NGA World Port Index Scraper: Pub 150, 2,951 Ports avatar

NGA World Port Index Scraper: Pub 150, 2,951 Ports

Pricing

from $1.00 / 1,000 port scrapeds

Go to Apify Store
NGA World Port Index Scraper: Pub 150, 2,951 Ports

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

Arman Hossain

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

10 days ago

Last modified

Categories

Share

World Port Index: Every port NGA tracks worldwide, over 110 fields covering depths, cranes, pilotage and repairs

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:

FieldWhat it holds
portNumberNGA's stable numeric port identifier
portNamePort name
unloCodeUN/LOCODE, where assigned (about 87% of ports)
countryCode, countryNameISO country code and name
regionName, regionNumberNGA's World Port Index region grouping
decimalLatitude, decimalLongitudeSigned decimal coordinates
latitude, longitudeThe same position as NGA's own DMS strings (e.g. 51°54'00"N)
harborSize, harborSizeLabelPub 150 code (L/M/S/V) and its decoded label
harborType, harborTypeLabelPub 150 code (e.g. RB) and its decoded label
shelter, shelterLabelShelter afforded, code and decoded label
chDepth, anDepth, cpDepth, otDepthChannel, anchorage, cargo pier and oil terminal depths, in meters
crFixed, crMobile, crFloating, cranesContainerCrane availability by type
ptCompulsory, ptAvailable, tugsAssist, tugsSalvagePilotage and tug service flags
repairCode, drydock, railwayRepair capability codes
portSecurity, etaMessageISPS-related fields as published by NGA
globalIdNGA'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 unloCode to 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

FieldTypeDefaultNotes
portNamesarray[]Keep ports whose name contains any of these (case-insensitive substring). Empty means every port.
countriesarray[]2-letter ISO country code (exact match) or country name (substring match).
regionNamesarray[]Substring match against NGA's World Port Index region (mostly country names, sometimes a coastline like ALASKA or BRAZIL NE COAST).
harborTypesarray[]Exact Pub 150 harbor type code(s): CN, CB, CT, RN, RB, RT, LC, OR, TH.
minChannelDepthinteger0Drop ports with a reported channel depth below this many meters, or with no reported depth. 0 keeps everything.
maxPortsinteger0Cap 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, regionNames and harborTypes all combine with AND against the same 2,951-port list.
  • chDepth, anDepth, cpDepth and otDepth are reported in meters as numeric strings, matching the source. About 12% of ports carry no chDepth at all (null); minChannelDepth drops those along with any port below the threshold.
  • decimalLatitude/decimalLongitude come straight from the source's own decimal coordinates, not a DMS parse, and are exact for every one of the 2,951 ports.
  • unloCode is 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 maxPorts value outside 0..2,951 (negative, non-numeric) is refused rather than treated as "unlimited": the run finishes with the failure recorded in RUN_SUMMARY and 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.