Home → Blog

You need to programmatically check port congestion for Hong Kong so your systems can route freight and set customer expectations. By the end of this guide, you will query the FreightPulse Port Congestion API for Hong Kong (HKHKG) in Python, parse the congestion level and baseline ratio, and build a small utility that is safe to run in production with caching and basic monitoring.

What this API tells you (and what it does not)

The FreightPulse Port Congestion endpoint for Hong Kong returns an aggregate congestion signal derived from IMF PortWatch. It reports a 7-day average of port calls compared with a 90-day baseline for the same port. Data refreshes daily but is lagged about 6–7 days; it is not a real-time AIS wait-time feed and does not measure anchorage delays. Use it for macro congestion context rather than minute-by-minute berth operations.

Illustration: API to Check Congestion at Hong Kong

The response arrives in a consistent envelope: {"success": true, "data": { ... }}. For application logic, focus on these fields in the Hong Kong result:

Freight rates in USD are not part of this endpoint; do not treat congestion data as a live price or quote.

Authentication, rate limits, and reliability basics

Authentication is via an HTTP header: X-API-Key: YOUR_API_KEY. Guests without a key can make up to 100 requests per day per IP. For server-to-server integrations, set your key in a secure environment variable and forward it from a backend service rather than a browser.

Given the daily update cadence and 6–7 day lag, you should cache results at least until the next local midnight UTC or for 24 hours, whichever is simpler for your scheduler. Do not poll this endpoint on minute-scale intervals; you will not obtain fresher values and you could exhaust guest quota.

Endpoint and parameters for Hong Kong (HKHKG)

The Hong Kong port is identified as HKHKG. The base read is a simple GET with the port code as a query parameter:

No pagination or additional parameters are required for this lookup. The response contains a data.results array with one entry for the requested port, plus optional global summaries and methodology metadata.

Quick start: cURL you can copy-paste

Use this exact call to retrieve congestion for Hong Kong. Replace the API key header with your own key if you have one; otherwise, the request will count against the guest limit.

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

Official response example and field map

The following is an official sample response for Hong Kong, returned in the standard envelope. Copy these values exactly if you need a fixture for local tests.

{
"success": true,
"data": {
"results": [
{
"port": "Hong Kong",
"locode": "HK HKG",
"country": "Hong Kong SAR",
"congestion": "moderate",
"as_of": "2026-10-09",
"metrics": {
"recent_avg_portcalls_per_day": 39,
"baseline_avg_portcalls_per_day": 38.1,
"ratio_vs_baseline": 1.02
}
}
],
"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": "763861a3-7f0b-464e-b129-bb65bb6db16f"
}
}

What you will actually use in code:

Keep in mind the methodology and data_lag_days in data.meta. This is important for setting user expectations and for scheduling cache refreshes.

Python integration: fetch, parse, and handle edge cases

The snippet below demonstrates how to call the endpoint for HKHKG, extract the congestion level, ratio_vs_baseline, and the as_of date, and wrap it with basic validation. It also shows where to insert a trivial cache layer if your stack supports one.

Python example using requests

import os
import time
import json
from typing import Optional, Dict

import requests

FREIGHTPULSE_API = "https://freightpulsehq.com/api/v1/port-congestion"
PORT_CODE = "HKHKG"
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY") # use real key in prod

# Simple TTL cache (in-memory). Replace with Redis/Memcached in production.
_cache: Dict[str, Dict] = {}
CACHE_TTL_SECONDS = 12 * 60 * 60 # 12 hours is fine for a ~daily, lagged series

def get_cached(key: str) -> Optional[Dict]:
entry = _cache.get(key)
if not entry:
return None
if time.time() - entry["ts"] > CACHE_TTL_SECONDS:
_cache.pop(key, None)
return None
return entry["value"]

def set_cached(key: str, value: Dict):
_cache[key] = {"ts": time.time(), "value": value}

def fetch_hkhkg_congestion(session: Optional[requests.Session] = None) -> Dict:
cache_key = f"port_congestion:{PORT_CODE}"
cached = get_cached(cache_key)
if cached:
return cached

s = session or requests.Session()
headers = {"X-API-Key": API_KEY}
params = {"port": PORT_CODE}

resp = s.get(FREIGHTPULSE_API, headers=headers, params=params, timeout=20)
# Handle HTTP errors explicitly
try:
resp.raise_for_status()
except requests.HTTPError as e:
raise RuntimeError(f"HTTP error {resp.status_code} from FreightPulse") from e

# Parse JSON envelope: {"success": true, "data": {...}}
try:
payload = resp.json()
except json.JSONDecodeError as e:
raise RuntimeError("Invalid JSON payload from FreightPulse") from e

if not isinstance(payload, dict) or not payload.get("success"):
# Even if HTTP 200, ensure success flag is True
raise RuntimeError(f"FreightPulse request not successful: {payload!r}")

data = payload.get("data", {})
results = data.get("results", [])
if not results:
# The API should return a single result for HKHKG; fail gracefully if empty
raise RuntimeError("No results returned for HKHKG")

row = results[0]

# Extract the fields we actually use
congestion = row.get("congestion")
as_of = row.get("as_of")
metrics = row.get("metrics", {})
ratio = metrics.get("ratio_vs_baseline")

if congestion is None or as_of is None or ratio is None:
raise RuntimeError(f"Missing required fields in FreightPulse response: {row!r}")

# Optionally pass through additional context for observability
meta = data.get("meta", {})
source = meta.get("source")
lag = meta.get("data_lag_days")

result = {
"port": row.get("port"),
"locode": row.get("locode"),
"country": row.get("country"),
"congestion": congestion,
"ratio_vs_baseline": ratio,
"as_of": as_of,
"source": source,
"data_lag_days": lag
}

set_cached(cache_key, result)
return result

if __name__ == "__main__":
info = fetch_hkhkg_congestion()
# Example formatted output for logs or CLI
print(
f"HKHKG congestion: {info['congestion']} "
f"(ratio_vs_baseline={info['ratio_vs_baseline']}, as_of={info['as_of']})"
)

Notes:

Interpreting congestion and ratio_vs_baseline for decisioning

Use congestion as a coarse label for dashboards and customer notices. The ratio_vs_baseline is better suited for thresholds in automation because it is numeric:

Because the measure is based on port calls rather than berth dwell or anchorage time, always label UI elements accordingly (for example, “IMF PortWatch congestion, 7-day vs 90-day baseline”). Avoid implying real-time wait times.

Scheduling, caching, and operational hygiene

Practical scheduling guidance for HKHKG congestion pulls:

The as_of field is a date (YYYY-MM-DD) and does not include a timezone. Treat it as a daily observation in UTC. If you localize dates, ensure your UI copy remains clear that the underlying data is lagged by several days relative to “today.”

Error handling and safe fallbacks

Patterns to avoid outages when integrating this endpoint:

You can also log meta.request_id (when present at the top-level meta) alongside your request correlation ID. This makes vendor support easier when debugging intermittent network issues.

Testing locally with fixtures

For unit tests that should not call the network, stub the response with the official sample above since it includes the correct envelope, field names, and types. Focus your assertions on:

This keeps your parsing logic stable even if the live ratio or congestion label changes over time. Remember that while examples for some ports exist, there is no separate published fixture dedicated to HKHKG beyond the standard example shown here, so rely on the field names exactly as documented.

Security, keys, and environments

Store the API key in an environment variable on the server and inject it into your process manager (systemd, Docker secrets, Kubernetes Secrets). Never embed keys in client-side code or public repos. For staging environments, use a distinct key so that you can audit and rotate without touching production traffic.

If you must operate without a key, you can still make up to 100 requests per day per IP as a guest. Use aggressive caching and avoid parallel test runs that could drain the guest quota unexpectedly.

Operational metrics to monitor

Track these signals to keep the integration healthy:

Anomalies in days_lag suggest upstream data staleness and are useful for alerting before customers notice stale labels in your UI.

Developer workflow tips for HKHKG only

Keep a small module focused on the HKHKG port, especially if your product must stay scoped to Hong Kong. Avoid dropping in examples from other ports like USLAX—field names are the same, but reusing foreign fixtures can cause confusion in tests and monitoring labels. Make your logging strings explicitly reference HKHKG so that dashboards remain port-specific.

Going further: simple normalization layer

If your system consumes multiple signals, consider a compact normalization step that maps the qualitative “congestion” to a numeric score while preserving the original text. For example, map low=0, moderate=1, high=2 in your code, but always store the raw congestion string and ratio_vs_baseline for auditability. This lets you run deterministic threshold checks without losing context.

Reference: cURL and JSON fields you will use

As a quick reference, here are the key artifacts once more for Hong Kong. Use the curl exactly as shown, and in code read congestion, metrics.ratio_vs_baseline, and as_of.

Primary fields to consume in your application:

Practical caveats that save time

FAQ

Is this congestion value real-time?
No. It is a daily-refreshed series based on IMF PortWatch with an effective lag of about 6–7 days. It compares a recent 7-day average of port calls against a 90-day baseline.

What fields should I persist?
Persist congestion, metrics.ratio_vs_baseline, and as_of for the port. Optionally store data.meta.source and data.meta.data_lag_days for transparency in user interfaces.

How often should I call the endpoint?
Once per day is sufficient. Cache results for 12–24 hours and display the as_of date to users so they understand the observation window.

Can I use this to estimate vessel wait times at anchorage?
No. The methodology does not measure vessel wait or anchorage time. It is based on port call activity levels, not dwell time.

Do I need an API key?
You can call the API without a key, but guests are limited to 100 requests per day per IP. For production, register for a key and send it in the X-API-Key header.

Next steps

Get a key and wire this into your job scheduler so your system refreshes HKHKG congestion daily, caches the result, and surfaces the as_of date in your UI. If you have questions on envelope structure or field semantics, check the API reference.

Register for an API key, then read the Documentation to finalize your integration.