Your team needs a reliable way to check container congestion at Guangzhou (Nansha) without building an AIS pipeline. By the end of this guide you’ll query the FreightPulse Transportation API for CNGGZ, read congestion levels derived from IMF PortWatch (lagged ~6–7 days), and integrate it into a Python workflow that caches and alerts on changes.
What the congestion API returns for Guangzhou (Nansha) (CNGGZ)
The endpoint provides a JSON envelope with a success flag and a data object. For Guangzhou (Nansha), you’ll read these fields from the first result:
- congestion: qualitative level such as low, moderate, high.
- metrics.ratio_vs_baseline: numeric ratio comparing the last 7 days’ average port calls to the 90-day baseline.
- as_of: ISO date (YYYY-MM-DD) of the latest available computation.
Key facts before you integrate:
- Auth: send X-API-Key: YOUR_API_KEY. Guests without a key are limited to 100 requests/day per IP.
- Envelope: responses are inside {"success": true, "data": {...}}.
- Methodology and timeliness: congestion uses IMF PortWatch aggregates, lagged about 6–7 days. This is not live AIS wait-time measurement.
- Port identifier: Guangzhou (Nansha) uses code CNGGZ.
- Do not quote freight-rate USD fares from elsewhere as “today’s price” in the same flow; this endpoint does not return rates.
Quick start: cURL
Use the documented GET endpoint with the port code. Include the API key header exactly as shown.
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=CNGGZ"
Python integration: fetch, parse, and interpret congestion
The following Python example calls the same endpoint, validates the success envelope, reads congestion, ratio_vs_baseline, and as_of, and prints a concise summary you can plug into alerts or dashboards.
import os
import sys
import time
import json
from typing import Optional
import requests
API_URL = "https://freightpulsehq.com/api/v1/port-congestion"
PORT_CODE = "CNGGZ"
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")
def fetch_congestion(port: str) -> dict:
headers = {"X-API-Key": API_KEY}
params = {"port": port}
resp = requests.get(API_URL, headers=headers, params=params, timeout=20)
resp.raise_for_status()
payload = resp.json()
if not isinstance(payload, dict) or not payload.get("success"):
raise ValueError("API did not return success=true")
data = payload.get("data") or {}
results = data.get("results") or []
if not results:
raise ValueError(f"No congestion results for port={port}")
return results[0], data, payload.get("meta")
def summarize(result: dict) -> str:
port_name = result.get("port")
locode = result.get("locode")
as_of = result.get("as_of")
congestion = result.get("congestion")
metrics = result.get("metrics") or {}
ratio = metrics.get("ratio_vs_baseline")
recent = metrics.get("recent_avg_portcalls_per_day")
baseline = metrics.get("baseline_avg_portcalls_per_day")
return (
f"Port: {port_name} ({locode})\n"
f"As of: {as_of} (IMF PortWatch, ~6–7 day lag)\n"
f"Congestion: {congestion}\n"
f"7d avg port calls: {recent} vs 90d baseline: {baseline}\n"
f"Ratio vs baseline: {ratio}"
)
def main():
try:
result, data, meta = fetch_congestion(PORT_CODE)
print(summarize(result))
except requests.HTTPError as e:
print(f"HTTP error: {e}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
# Simple retry wrapper for transient network issues
for attempt in range(3):
try:
main()
break
except SystemExit as e:
if attempt == 2:
raise
time.sleep(2 ** (attempt + 1))
Notes:
- Timeout is set to 20 seconds; adjust based on your env.
- Implement daily caching because data is lagged and updates daily (not intra-day).
- Dates are calendar dates without timezones; treat them as reporting dates, not real-time stamps.
Official sample response for Guangzhou (Nansha)
Below is the official sample you can use to wire up your parser and tests.
{
"success": true,
"data": {
"results": [
{
"port": "Guangzhou (Nansha)",
"locode": "CN GGZ",
"country": "China",
"congestion": "moderate",
"as_of": "2026-10-09",
"metrics": {
"recent_avg_portcalls_per_day": 28,
"baseline_avg_portcalls_per_day": 27.6,
"ratio_vs_baseline": 1.01
}
}
],
"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": "13e05e18-f6f9-4238-99db-44211c2a6159"
}
}
Field highlights you will actually use:
- data.results[0].congestion: a label you can display in dashboards and alerts.
- data.results[0].metrics.ratio_vs_baseline: a numeric indicator for thresholding (e.g., trigger when ≥ 1.10).
- data.results[0].as_of: the effective date; couple it with your cache key to prevent stale re-alerting.
Operational guidance for transportation teams
Because the series is lagged ~6–7 days, use it for planning and network steering rather than “vessel at anchor now” decisions. If you need point-in-time anchorage or berth wait times, you would need a separate AIS stream; this endpoint intentionally focuses on IMF PortWatch aggregates.
- Cache and refresh cadence: fetch once daily during your planning window (e.g., 06:00 UTC) and cache by as_of date.
- Thresholds: treat ratio_vs_baseline near 1.0 as normal; step up interventions as it rises materially above 1.0.
- Units and scope: counts are port calls per day; there’s no currency here. Do not mix these with freight-rate USD quotes as “today’s price.”
- Weekends/holidays: the series is daily and aggregated; do not assume business-day-only behavior.
Auth, quotas, and envelopes
Authentication is via an HTTP header and a daily guest limit.
- Header: X-API-Key: YOUR_API_KEY
- Guest quota: 100 requests/day per IP without a key (use a key in production pipelines).
- Envelope: always check success before reading data, and handle empty results arrays defensively.
For full parameter and schema references, see the Documentation.
Error handling and resiliency
- HTTP errors: surface the status and body for observability; retry idempotent GETs with backoff for transient network failures.
- Schema drift: guard lookups (e.g., dict.get) and validate that metrics.ratio_vs_baseline is numeric before comparing.
- Empty data: treat an empty results array as “no signal today”; continue using the last cached as_of within a bounded staleness window you define.
- Rate limiting: if you batch multiple ports, space requests and reuse cached responses instead of hammering the endpoint.
Embedding CNGGZ congestion into transportation workflows
Common patterns for Guangzhou (Nansha):
- Procurement lead-time guardrails: if ratio_vs_baseline drifts above your threshold for two consecutive as_of dates, extend buffers on truck gate-in and drayage bookings for Nansha calls.
- Exception routing: when congestion is moderate or above, prefer services with flexibility on cutoff times at CNGGZ, or pre-position empties earlier in the week.
- Carrier comms: attach the congestion level and as_of date to your sailing notes so ops can separate planning signals from live AIS chatter.
FAQs
Is this live AIS wait-time data?
No. It is IMF PortWatch aggregates lagged roughly 6–7 days. It compares the recent 7-day average port calls against a 90-day baseline and does not measure anchorage/berth wait times.
What timezone are the dates in?
as_of is a calendar date (YYYY-MM-DD) for the dataset’s reporting day. Treat it as a date without a timezone and cache by that value.
How often should I poll the endpoint?
Daily is sufficient. The underlying data updates daily with a ~6–7 day lag; intra-day polling will not add fresher signal.
Can I combine this with spot freight rates?
Yes, but keep them separate in your UI. Do not present a spot USD rate as “today’s” price using this endpoint—this endpoint provides congestion signals only.
What happens if there are no results for CNGGZ?
Handle an empty results array gracefully and continue using the last cached as_of until the next daily refresh, with an expiration policy you control.
Ready to productionize your Guangzhou (Nansha) congestion checks? Create your key and start integrating today: Register.