You need a reliable way to programmatically check port congestion for Rotterdam (NLRTM) so you can adjust vessel ETAs, allocate yard resources, or pre-book feeder capacity. By the end of this guide you will query the FreightPulse GET /api/v1/port-congestion endpoint for Rotterdam using Python, interpret the returned congestion fields correctly, and wire them into transportation operations that depend on lagged, daily AIS-derived metrics.
What this API returns for Rotterdam and how to use it
The FreightPulse port congestion endpoint provides a daily, lagged signal derived from IMF PortWatch (UN Global Platform AIS data). It compares the most recent 7-day average port calls to a 90-day baseline for the same port. This is not a live AIS wait-time feed and does not measure anchorage or berth queue length. For Rotterdam, you will query port=NLRTM and interpret three key fields:
- congestion: text category (e.g., low, moderate, high)
- metrics.ratio_vs_baseline: numeric ratio of recent vs baseline port calls
- as_of: YYYY-MM-DD date for the latest available reading (lagged ~6–7 days)
Operationally, consider this a macro congestion indicator suitable for planning buffers, flagging risk, or trend reporting—not for real-time dispatch decisions.
Authentication, quotas, and request format
Authentication uses an HTTP header: X-API-Key: YOUR_API_KEY. Guests without a key are allowed 100 requests per day per IP. Every response is wrapped in an envelope like {"success": true, "data": {...}} with a top-level meta.request_id for traceability. The endpoint and parameters for Rotterdam:
- Method: GET
- Endpoint: https://freightpulsehq.com/api/v1/port-congestion
- Query parameter: port=NLRTM
- Header: X-API-Key: YOUR_API_KEY
There is no separate, official fixture JSON published for NLRTM beyond the live response format shown below. Data updates daily but are lagged approximately 6–7 days relative to calendar time.
Quickstart with cURL and field mapping
Copy and run the cURL below to retrieve the most recent Rotterdam congestion snapshot:
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=NLRTM"
Official sample JSON (copying field names and values exactly):
{
"success": true,
"data": {
"results": [
{
"port": "Rotterdam",
"locode": "NL RTM",
"country": "The Netherlands",
"congestion": "moderate",
"as_of": "2026-10-09",
"metrics": {
"recent_avg_portcalls_per_day": 84,
"baseline_avg_portcalls_per_day": 84.5,
"ratio_vs_baseline": 0.99
}
}
],
"global_summary": {
"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": "~6-7 (daily updates, not real-time)",
"methodology": "congestion level is an approximation: last 7 days' avg port calls vs 90-day baseline for the same port. Does not measure vessel wait/anchorage time."
}
},
"meta": {
"request_id": "1fc29aad-ed56-47a8-bdaa-f7a4e6745c09"
}
}
How to read this:
- data.results[0].congestion: operational label you can map to buffer policies.
- data.results[0].metrics.ratio_vs_baseline: numeric signal for trend dashboards and threshold alerts.
- data.results[0].as_of: the date of the lagged daily update (YYYY-MM-DD). Treat it as a daily timestamp; time-of-day is not provided.
- data.meta.data_lag_days: explicitly states the 6–7 day lag and reminds you it is not real-time AIS.
Python integration for Rotterdam (NLRTM)
The snippet below queries Rotterdam once, parses the congestion category and ratio_vs_baseline, and applies a simple policy mapping to produce scheduling buffers. Adjust the thresholds to your internal standards; values here only demonstrate how to read and use the fields.
import os
import time
import requests
API_URL = "https://freightpulsehq.com/api/v1/port-congestion"
PORT = "NLRTM"
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")
def fetch_rotterdam_congestion(session: requests.Session) -> dict:
resp = session.get(
API_URL,
params={"port": PORT},
headers={"X-API-Key": API_KEY},
timeout=20
)
resp.raise_for_status()
payload = resp.json()
if not payload.get("success"):
raise RuntimeError("API returned success=false")
results = payload.get("data", {}).get("results", [])
if not results:
raise ValueError("No results for NLRTM in response")
return results[0]
def buffer_minutes_for(congestion: str, ratio: float) -> int:
# Example policy:
# - Start with ratio-based increments
# - Override with categorical floor if needed
ratio_buf = 0
if ratio < 0.95:
ratio_buf = 10
elif ratio < 1.05:
ratio_buf = 20
else:
ratio_buf = 35
cat_floor = {
"low": 10,
"moderate": 20,
"high": 45
}.get(congestion.lower(), 20)
return max(ratio_buf, cat_floor)
def main():
with requests.Session() as session:
data = fetch_rotterdam_congestion(session)
port = data.get("port")
locode = data.get("locode")
congestion = data.get("congestion")
as_of = data.get("as_of")
metrics = data.get("metrics", {})
ratio = metrics.get("ratio_vs_baseline")
print(f"Port: {port} ({locode}), as_of={as_of}")
print(f"Congestion: {congestion}, ratio_vs_baseline={ratio}")
minutes = buffer_minutes_for(congestion, float(ratio))
print(f"Recommended scheduling buffer: {minutes} minutes")
if __name__ == "__main__":
# Simple retry on transient network failures
for attempt in range(3):
try:
main()
break
except (requests.RequestException, ValueError) as e:
if attempt == 2:
raise
time.sleep(2 ** attempt)
Notes for production use:
- Timeout is set to 20s. Tune based on your network and orchestration environment.
- Basic exponential backoff handles transient failures. Consider circuit-breaking if your workflow is latency-sensitive.
- Cache responses daily by as_of date; new data arrives once per day, lagged ~6–7 days.
- If your pipeline is event-driven, trigger refresh after local midnight UTC or per your data ingestion window.
Operational guidance: Rotterdam-specific usage in transportation workflows
Because the Rotterdam signal is a smoothed 7-day average vs a 90-day baseline, it’s best applied to tactical planning and service-level buffers rather than to minute-by-minute berth management. Some practical patterns:
- ETA padding for long-haul arrivals: increase or decrease schedule slack based on ratio_vs_baseline thresholds.
- Yard labor rostering: on “moderate” and above, pre-assign swing shifts; fall back to standard staffing for “low.”
- Feeder and barge pre-booking: when ratio_vs_baseline dips below 1.0, anticipate closer-to-normal throughput; near or above 1.05, pre-book extra capacity.
- Exception dashboards: display congestion and as_of next to voyage milestones so planners understand the data’s lag and source.
Do not conflate this with live AIS wait times or queue lengths. If you need to react to sudden berth closures or weather in real time, pair this with your operational feeds and TOS alerts.
Data handling: dates, caching, and comparisons
Dates and intervals:
- as_of is a date string in YYYY-MM-DD. Treat it as daily granularity; time zone isn’t provided because the metric is a day-level aggregate.
- metrics.recent_avg_portcalls_per_day and metrics.baseline_avg_portcalls_per_day are counts of port calls per day over their respective windows (7-day recent vs 90-day baseline).
Caching and re-use:
- Cache by port + as_of. If the as_of value hasn’t changed since your last pull, you can reuse cached results safely for planning tasks.
- Because updates are daily and lagged, polling more than a few times per day provides no new information and only burns quota.
Comparisons and thresholds:
- ratio_vs_baseline near 1.0 indicates throughput close to the 90-day norm. Below 1.0 suggests fewer port calls recently; above 1.0 indicates more.
- Pick thresholds relevant to your SLAs. For example, trigger an “elevated risk” tag for Rotterdam when ratio_vs_baseline ≥ 1.05 or congestion is reported as high.
Error handling and edge cases
While integrating, plan for:
- Empty result sets: Handle the case where data.results is empty. In that situation, keep your last known good value and log the event.
- Network issues and timeouts: Use retries with exponential backoff. For scheduled jobs, alert only after multiple failures to limit noise.
- Data lag awareness: Always display as_of alongside congestion to prevent misuse as a live metric.
- Stale caches: Implement a max age in your cache tied to the daily cadence. For example, refresh once per day after your standard ingestion time.
Security, observability, and rollout checklist
Security:
- Store YOUR_API_KEY in a secrets manager or environment variable. Avoid embedding keys in source control or CI logs.
- Restrict egress only to required domains if your environment supports network policies.
Observability:
- Log meta.request_id from the API response to speed up support tickets and correlation.
- Emit custom metrics such as “rotterdam_congestion_ratio” and “rotterdam_as_of_age_days” for internal dashboards.
Rollout steps:
- Stage 1: Implement read + cache in a cron or orchestration job.
- Stage 2: Wire into ETA padding logic with conservative thresholds.
- Stage 3: Expose as_of and congestion to operations dashboards for Rotterdam-specific decisions.
Reference: official cURL and field names
Use the documented header and query parameter exactly as shown below. This is the same call demonstrated earlier, repeated here for easy copy-paste during implementation:
Key fields to persist for Rotterdam:
- congestion
- metrics.ratio_vs_baseline
- as_of
- locode and port (for display and joins)
FAQ
Is this live AIS wait-time data?
No. It is a lagged (~6–7 days) metric based on IMF PortWatch AIS-derived aggregates. It compares recent 7-day average port calls to a 90-day baseline and does not measure anchorage or berth wait times.
What’s the correct port code for Rotterdam?
Use port=NLRTM in the query parameter. The response may include a locode formatted as “NL RTM.”
How often should I poll the endpoint?
Once per day is sufficient. The dataset updates daily with a 6–7 day lag, so more frequent polling won’t yield fresher values and may consume your guest quota (100 requests/day per IP without a key).
How do I authenticate?
Send your key in the header X-API-Key: YOUR_API_KEY. All responses are wrapped in {"success": true, "data": {...}} so check the success flag before use.
Can I use these numbers to quote live freight rates?
No. Do not treat this as a current price signal. It’s a congestion indicator and should not be presented as today’s freight-rate USD fare.
Ready to integrate Rotterdam congestion into your transportation stack? Create an API key and start testing today: Register. For parameter details and endpoint behavior, see the Documentation.