You’re trying to understand how congested Port Klang is today, but you keep running into conflicting dashboards and “live AIS wait time” claims. By the end of this post, you’ll know exactly how to pull the latest FreightPulse congestion signal for Port Klang (MYPKG), what it measures, its 6–7 day lag, how to interpret the 7-day average vs 90-day baseline, and how to embed it safely in your workflow with a minimal Python client.
What the Port Klang congestion signal actually measures
FreightPulse’s congestion signal for Port Klang is sourced from IMF PortWatch, built on UN Global Platform AIS data. It is a daily-updated, lagged indicator:
- Lag: roughly 6–7 days (not real-time).
- Metric: recent 7-day average port-calls compared against a 90-day baseline for the same port.
- Interpretation: activity is a proxy for how “normal” port throughput looks. Lower activity can coincide with closures, weather, diversions, or ships waiting offshore, but it does not directly measure anchorage or berth wait times.
- Terminology: congestion is a deprecated alias for activity in this endpoint.
This is not a live AIS wait-time feed. Use it to assess whether Port Klang’s throughput is running above, near, or below its usual cadence over the last quarter, with a short lag that makes it suitable for monitoring and reporting rather than moment-to-moment dispatching.
Endpoint, auth, and envelope
Access the congestion signal for Port Klang via a single GET endpoint. Authentication uses an API key in the X-API-Key header. Guests without a key may make up to 100 requests per day per IP. Responses are wrapped in an envelope that always includes a top-level success boolean and a data object.
- Method: GET
- Auth: header X-API-Key: YOUR_API_KEY
- Port: Port Klang (MYPKG)
- Envelope: {"success": true, "data": {...}}
Official documentation is here: Documentation.
Copy-pasteable cURL request
The following call requests the PortWatch-based congestion signal for Port Klang (MYPKG). Keep the URL and header exactly as shown:
Live FreightPulse congestion for MYPKG.
Source: live
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=MYPKG"
Official JSON response and field meanings
Below is the official response format, including a results array with Port Klang. Values are provided from the live source example:
{
"success": true,
"data": {
"results": [
{
"port": "Port Klang",
"locode": "MY PKG",
"country": "Malaysia",
"activity": "moderate",
"congestion": "moderate",
"as_of": "2026-10-30",
"metrics": {
"recent_avg_portcalls_per_day": 30.3,
"baseline_avg_portcalls_per_day": 31.7,
"ratio_vs_baseline": 0.95
}
},
{
"port": "Pelabuhan Sungai Udang",
"locode": "MY PKG",
"country": "Malaysia",
"activity": "moderate",
"congestion": "moderate",
"as_of": "2026-10-30",
"metrics": {
"recent_avg_portcalls_per_day": 2,
"baseline_avg_portcalls_per_day": 1.9,
"ratio_vs_baseline": 1.08
}
}
],
"global_summary": {
"ports_high_activity": 0,
"ports_moderate_activity": 2,
"ports_low_activity": 0,
"ports_high_congestion": 0,
"ports_moderate": 2,
"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": "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": "a19d01f2-877d-40b9-b625-ab6c51df6d8c"
}
}
What you will actually use:
- data.results[].port and data.results[].locode: Identify the record for Port Klang (port: "Port Klang", locode includes "MY PKG").
- data.results[].as_of: The data date, updated daily with a ~6–7 day lag (format: YYYY-MM-DD).
- data.results[].metrics.recent_avg_portcalls_per_day: 7-day average port calls per day (unit: calls/day).
- data.results[].metrics.baseline_avg_portcalls_per_day: 90-day baseline average calls/day.
- data.results[].metrics.ratio_vs_baseline: recent divided by baseline, ratio unitless (1.0 = baseline; below 1.0 = below baseline activity).
- data.results[].activity and data.results[].congestion: categorical labels aligned with the ratio; congestion is a deprecated alias of activity.
- data.meta.data_lag_days and data.meta.methodology: summarise source and caveats for display disclaimers.
How to interpret Port Klang’s numbers today
Use the ratio_vs_baseline to express where Port Klang stands versus its 90-day norm. A ratio near 1.0 means activity is around baseline. A ratio well below 1.0 means reduced port-call volume; this can correspond to weather disruptions, holidays, or offshore waiting—but it does not quantify berth/anchorage wait times.
Best-practice display:
- Badge: “Port Klang activity: moderate” (or the returned category).
- Context line: “7-day avg vs 90-day baseline (IMF PortWatch), lag ~6–7 days; not live AIS wait times.”
- Numbers: “30.3 vs 31.7 calls/day (0.95x baseline) as of 2026-10-30”.
Avoid wording like “live congestion” or “current wait time”. Keep the lag disclaimer visible anywhere you chart or alert on this signal.
Python example: fetch and render Port Klang’s congestion
This script calls the same endpoint, filters for the Port Klang row, and formats a compact status line for a dashboard. Replace YOUR_API_KEY with your token. If you do not have one, the endpoint can be called as a guest with daily request limits per IP.
import os
import requests
from datetime import datetime
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")
URL = "https://freightpulsehq.com/api/v1/port-congestion?port=MYPKG"
headers = {"X-API-Key": API_KEY}
def fetch_port_klang_status():
resp = requests.get(URL, headers=headers, 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", [])
# Prefer exact port name match to avoid collisions in multi-port arrays
port_row = next((r for r in results if r.get("port") == "Port Klang"), None)
if not port_row:
raise ValueError("Port Klang row not found in results")
metrics = port_row.get("metrics", {})
as_of = port_row.get("as_of") # e.g., "2026-10-30" (date string, lagged ~6–7 days)
activity = port_row.get("activity")
congestion = port_row.get("congestion") # deprecated alias of activity
recent = metrics.get("recent_avg_portcalls_per_day")
baseline = metrics.get("baseline_avg_portcalls_per_day")
ratio = metrics.get("ratio_vs_baseline")
# Build a compact line you can drop into logs or a status widget
line = (
f"Port Klang (MYPKG) — activity: {activity} (alias: {congestion}); "
f"7d avg {recent} vs 90d baseline {baseline} calls/day "
f"[{ratio}x]; as_of {as_of}; source: IMF PortWatch (lag ~6–7 days)"
)
return line
if __name__ == "__main__":
try:
print(fetch_port_klang_status())
except Exception as e:
print(f"Error: {e}")
Notes:
- Timeout: 20 seconds is conservative; tune for your environment.
- Dates: as_of is a date string (YYYY-MM-DD) with daily cadence. Treat it as local-naive or cast to a date object; no timezone offset is provided because times are not included.
- Caching: since the feed is lagged and updates daily, cache responses for 12–24 hours unless you must reflect the daily roll.
- Pagination: this endpoint returns an array under data.results; no pagination parameters are required in the provided interface.
Embedding disclaimers and UI guardrails
Because stakeholders often conflate port “congestion” with “wait time,” add guardrails wherever you show this data:
- Label: “Activity (7d avg vs 90d baseline)” rather than “Congestion” if you want strict clarity.
- Disclaimer tooltip: “Source: IMF PortWatch. Daily updates with ~6–7 day lag. Not a live AIS wait-time metric.”
- Thresholds: derive your own thresholds on ratio_vs_baseline to map into badges; e.g., low < 0.85, moderate 0.85–1.15, high > 1.15. If you implement thresholds, disclose they are your own logic.
Error handling and operational considerations
Plan for intermittent network errors and schema stability:
- Transport errors: retry idempotently on 429/5xx with exponential backoff. Respect guest limits (100/day/IP) or use an API key to avoid rate surprises.
- Envelope invariants: check success is true; then read data.results[].
- Port filtering: when results includes multiple Malaysian facilities referencing “MY PKG”, filter by port == "Port Klang".
- Version drift: avoid hard-coding categorical strings beyond display; your logic should primarily key on ratio_vs_baseline.
What to log for audits
For traceability, log the following alongside your derived badges:
- as_of date and the exact ratio_vs_baseline.
- recent_avg_portcalls_per_day and baseline_avg_portcalls_per_day.
- source string and data_lag_days from data.meta, to preserve provenance.
This makes it easy to reconcile dashboards with downstream analytics later.
Putting it together for Port Klang today
If your dashboard refreshes daily, call the endpoint once per day, cache the response, and render:
- Title: “Port Klang activity (IMF PortWatch, lagged ~6–7 days)”
- Metrics: “30.3 vs 31.7 calls/day (0.95x) as of 2026-10-30”
- Badge: “moderate”
- Footnote: “Not live AIS wait times.”
This communicates both the magnitude and the measurement caveat without over-claiming real-time precision.
Frequently asked questions
Is this the same as live anchorage or berth wait time?
No. This is a lagged activity signal (7-day average port calls vs a 90-day baseline) derived from IMF PortWatch. It does not measure vessel wait or anchorage time.
How fresh is the “as_of” date?
The feed updates daily but is typically ~6–7 days behind the current calendar date. Always display the as_of field to avoid confusion.
What units are the metrics in?
recent_avg_portcalls_per_day and baseline_avg_portcalls_per_day are calls per day. ratio_vs_baseline is unitless (recent / baseline).
Can I poll this more than once per day?
You can, but it’s unnecessary. The underlying data rolls daily with a lag. Cache for 12–24 hours to reduce load and stay within guest limits if you don’t use an API key.
Should I rely on the “congestion” field?
congestion is a deprecated alias of activity. For logic and alerts, key on metrics.ratio_vs_baseline and show activity as a human-readable label.
Ready to integrate Port Klang into your ops dashboard with a few lines of code? Get an API key in minutes: Register. For more details on fields and usage, see the Documentation.