Home → Blog

You’re trying to understand how congested Rotterdam (NLRTM) is and wire that signal into a dashboard or automated workflow. By the end of this guide, you’ll query FreightPulse for Rotterdam, interpret the congestion/activity fields correctly, and ship production-safe code that polls, caches, and alerts on changes without confusing this metric for live AIS wait times.

What this dataset actually measures for Rotterdam (NLRTM)

FreightPulse sources port activity/congestion from IMF PortWatch. The series is lagged (roughly 6–7 days) and represents a 7-day average of port calls versus a 90-day baseline at the same port. It does not measure live vessel wait or anchorage time, and it is not a same-day AIS feed.

Illustration: Rotterdam (NLRTM) Congestion

In the Rotterdam context, treat this as a standardized activity index that helps you detect whether port calls are running below, near, or above recent norms. A lower activity reading can coincide with fewer port calls because of disruptions or deferred arrivals; it does not necessarily mean berthing is fast or slow at that moment.

Rotterdam-specific facts you should copy into your implementation notes

Endpoint and authentication for NLRTM

Use a simple GET against the FreightPulse port-congestion endpoint with the UN/LOCODE for Rotterdam: NLRTM. Include your API key via the X-API-Key header. You can test without a key (guest), but the limit is 100 requests per day per IP, which is unsuitable for production polling.

Production guidance: schedule a single daily pull per port (or less) because the series is lagged and updates weekly. Cache locally and fan out to analytics, alerting, or your UI from your cache rather than re-calling the API every page view.

Official sample: cURL and JSON for Rotterdam (copy, run, and adapt)

Run this cURL command as-is (replace YOUR_API_KEY if you have one). The endpoint and field names must remain exactly as below.

curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=NLRTM"

Below is the official JSON sample for Rotterdam. Use it to structure your parsing and tests.

{
    "success": true,
    "data": {
        "results": [
            {
                "port": "Rotterdam",
                "locode": "NL RTM",
                "country": "The Netherlands",
                "activity": "moderate",
                "congestion": "moderate",
                "as_of": "2026-11-06",
                "metrics": {
                    "recent_avg_portcalls_per_day": 81,
                    "baseline_avg_portcalls_per_day": 82.5,
                    "ratio_vs_baseline": 0.98
                }
            }
        ],
        "global_summary": {
            "ports_high_activity": 0,
            "ports_moderate_activity": 1,
            "ports_low_activity": 0,
            "ports_high_congestion": 0,
            "ports_moderate": 1,
            "ports_low": 0
        },
        "meta": {
            "source": "IMF PortWatch (portwatch.imf.org) - UN Global Platform AIS data",
            "data_lag_days": "4-11 (PortWatch republishes the daily series once a week; check as_of)",
            "methodology": "activity is port-call volume, not congestion: last 7 days' avg calls vs a 90-day baseline for the same port. low means fewer calls than usual, which can be a closure or ships waiting offshore. It does not measure vessel wait or anchorage time. congestion is a deprecated alias of activity."
        }
    },
    "meta": {
        "request_id": "907190a9-e1be-4f11-81c1-59948777362e"
    }
}

Field highlights you will actually use:

Interpreting congestion and activity for Rotterdam

For Rotterdam, use the ratio_vs_baseline to quantify deviation: values near 1.0 imply activity close to normal. Values below 1.0 indicate fewer port calls than the 90-day baseline; above 1.0 indicates more. The congestion field mirrors activity and is bucketed, so it’s useful for simple status UIs but less precise for threshold alerts.

Do not conflate these values with live berth waiting time. A “low” bucket may stem from operational slowdowns, holidays, weather, or routing choices that reduce port calls; it doesn’t directly encode queue length offshore.

Production calling patterns and caching

Python example: fetch, parse, and normalize Rotterdam congestion

This example calls the same endpoint, validates the envelope, and extracts congestion, ratio_vs_baseline, and as_of for NLRTM. It also demonstrates a simple cache freshness check based on as_of.

import os
import json
import time
from urllib import request, error

API_URL = "https://freightpulsehq.com/api/v1/port-congestion?port=NLRTM"
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")

def fetch_nlrtm(timeout=20):
req = request.Request(API_URL, headers={"X-API-Key": API_KEY})
with request.urlopen(req, timeout=timeout) as resp:
if resp.status != 200:
raise RuntimeError("HTTP {} from FreightPulse".format(resp.status))
data = json.loads(resp.read().decode("utf-8"))
if not data.get("success"):
raise RuntimeError("API indicated success=false")
return data

def parse_rotterdam(payload):
results = payload.get("data", {}).get("results", [])
if not results:
raise ValueError("No results for NLRTM")
row = results[0]
as_of = row.get("as_of")
congestion = row.get("congestion") or row.get("activity")
ratio = None
metrics = row.get("metrics") or {}
if "ratio_vs_baseline" in metrics:
ratio = metrics.get("ratio_vs_baseline")
meta = payload.get("data", {}).get("meta", {})
lag = meta.get("data_lag_days")
return {
"port": row.get("port"),
"locode": row.get("locode"),
"as_of": as_of,
"congestion_bucket": congestion,
"ratio_vs_baseline": ratio,
"data_lag_days": lag,
}

def should_update(last_as_of, new_as_of):
return (new_as_of is not None) and (new_as_of != last_as_of)

if __name__ == "__main__":
# Example in-memory cache key
cache = {"as_of": None, "payload": None}
try:
payload = fetch_nlrtm()
parsed = parse_rotterdam(payload)
if should_update(cache["as_of"], parsed["as_of"]):
cache["as_of"] = parsed["as_of"]
cache["payload"] = parsed
print("Updated NLRTM:", json.dumps(parsed, indent=2))
else:
print("No change; current as_of={}, bucket={}".format(parsed["as_of"], parsed["congestion_bucket"]))
except error.HTTPError as e:
print("HTTPError {}: {}".format(e.code, e.read().decode("utf-8", "ignore")))
except Exception as e:
print("Error:", e)

Practical details that will save you time

Alerting and dashboard hints for Rotterdam

cURL you can paste into CI pipelines

If you prefer shell-based health checks or ingestion steps in cron/CI, run the official command and write the JSON to disk for an ETL step:

Validate that success is true and that the data.results array is non-empty. Then parse as_of and ratio_vs_baseline for downstream use.

Rotterdam integration checklist

Where to go next

If you need field-by-field reference material or historical retrieval patterns, check the documentation. When you’re ready to run production polling beyond the guest limit, register for an API key so your backend can ingest Rotterdam’s series on schedule.

Documentation · Register

FAQ

Is this live congestion or AIS-based waiting time for Rotterdam?

No. This is an IMF PortWatch-derived activity series (7-day average port calls vs a 90-day baseline). It is lagged and republished weekly. It does not measure live anchorage or berth waiting time.

What fields should I log for downstream analysis?

Log as_of, congestion (or activity), metrics.ratio_vs_baseline, and your ingestion timestamp. Also keep data.meta.data_lag_days so you can audit staleness over time.

How often should I poll Rotterdam (NLRTM)?

Daily or weekly is adequate. Because the data is lagged and refreshed weekly, frequent polling won’t add new signal.

Can I use the guest quota in production?

Not recommended. Guest is capped at 100 requests per day per IP. Use an API key in a backend service and cache results.

What should my dashboard display?

Show the bucket (low/moderate/high), ratio_vs_baseline with two decimals, and the as_of date. Include a note that this is IMF PortWatch activity and is not live AIS wait time.

Ready to integrate Rotterdam’s congestion/activity signal into your stack with proper caching and alerts? Get an API key and move your ingestion off the guest tier today: Register.