You need a dependable way to tell stakeholders what “congestion” at Ningbo (CNNBO) actually means right now, without confusing it with live AIS wait times. By the end of this post you’ll be able to query FreightPulse for Ningbo’s latest PortWatch-backed congestion snapshot, understand the 7-day average vs 90-day baseline method, parse the response fields you need (congestion, ratio_vs_baseline, as_of), and ship a production-safe integration.
What “congestion” means here (and what it doesn’t)
The congestion label you’ll fetch for Ningbo is not a measure of vessel wait time or anchorage delays from live AIS. Instead, it is an activity classification derived from port-call volumes: a 7-day average compared to a 90-day baseline at the same port. Lower-than-usual calls can still coincide with real-world waiting offshore; the metric is about throughput, not queue length.
FreightPulse exposes this as an easy-to-read congestion/activity tag, alongside the actual numeric series in the payload (recent and baseline average port calls per day) so you can compute and log your own thresholds if needed. If you need live anchorage or queueing estimates, you would need a different data product; this endpoint intentionally reports PortWatch-based activity.
Data source and lag: IMF PortWatch
Ningbo’s congestion/activity in FreightPulse comes from IMF PortWatch, which aggregates UN Global Platform AIS data. The series is republished weekly, so there is a built-in lag. Plan around a ~6–7 day delay between “today” and the last as_of date, though the dataset’s metadata can vary within a broader 4–11 day range. Always check as_of in the API response before displaying a “today” badge in your UI.
Because of this lag, the label is a recent-history snapshot, not a live signal. If you have an on-call workflow or alerts, schedule updates on a weekly cadence and cache results to avoid treating the dataset as real-time telemetry.
How to query Ningbo (CNNBO)
Use the authenticated GET endpoint with the port code CNNBO. Authentication is a simple header with your API key.
- Auth: header X-API-Key: YOUR_API_KEY
- Envelope: responses are wrapped as {"success":true,"data":{...}}
- Scope: Port congestion/activity is IMF PortWatch-based, lagged ~6–7 days, comparing a 7-day average to a 90-day baseline
- Port: Ningbo (CNNBO)
- Never treat this as live AIS wait times or quote a freight-rates USD fare here
Copy-pasteable curl:
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=CNNBO"
Official JSON response example for CNNBO:
{
"success": true,
"data": {
"results": [
{
"port": "Ningbo",
"locode": "CN NBO",
"country": "China",
"activity": "moderate",
"congestion": "moderate",
"as_of": "2026-11-06",
"metrics": {
"recent_avg_portcalls_per_day": 48.9,
"baseline_avg_portcalls_per_day": 46.5,
"ratio_vs_baseline": 1.05
}
}
],
"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": "b3f4da66-d18f-402a-a7f2-689d4e5aefb8"
}
}
What to read from this payload:
- congestion: a categorical label (e.g., “moderate”) aligned to the activity metric; useful for quick UI badges
- metrics.ratio_vs_baseline: numeric ratio of 7-day average vs 90-day baseline; 1.05 means roughly +5% above baseline
- as_of: the reference date of the underlying PortWatch series; treat it as the “data freshness” timestamp
- metrics.recent_avg_portcalls_per_day and metrics.baseline_avg_portcalls_per_day: underlying daily-call averages (units: port calls/day)
Reading congestion fields in code
The example below fetches Ningbo’s record, checks the envelope, finds the first result, and emits the three fields you’ll likely present: congestion, as_of, and ratio_vs_baseline. It also shows a simple guard around missing fields and a basic cache hint.
Python example
import os
import time
import json
import requests
API_KEY = os.environ.get("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")
URL = "https://freightpulsehq.com/api/v1/port-congestion"
PARAMS = {"port": "CNNBO"}
HEADERS = {"X-API-Key": API_KEY}
def fetch_ningbo_congestion():
r = requests.get(URL, params=PARAMS, headers=HEADERS, timeout=20)
r.raise_for_status()
payload = r.json()
if not payload.get("success"):
raise RuntimeError("API returned success=false")
data = payload.get("data", {})
results = data.get("results", [])
if not results:
raise RuntimeError("No results for CNNBO")
row = results[0]
congestion = row.get("congestion") or row.get("activity")
as_of = row.get("as_of")
metrics = row.get("metrics", {})
ratio = metrics.get("ratio_vs_baseline")
# Basic validation
if congestion is None or as_of is None or ratio is None:
raise RuntimeError("Missing expected fields (congestion/as_of/ratio_vs_baseline)")
return {
"congestion": congestion,
"as_of": as_of,
"ratio_vs_baseline": ratio,
"recent_avg": metrics.get("recent_avg_portcalls_per_day"),
"baseline_avg": metrics.get("baseline_avg_portcalls_per_day"),
}
def main():
# Example: naive in-memory cache for ~12 hours, aligned with PortWatch weekly cadence
cache_path = "/tmp/cnnbo_congestion.json"
max_age_seconds = 12 * 3600
use_cache = False
if os.path.exists(cache_path):
age = time.time() - os.path.getmtime(cache_path)
use_cache = age < max_age_seconds
if use_cache:
with open(cache_path, "r") as f:
result = json.load(f)
else:
result = fetch_ningbo_congestion()
with open(cache_path, "w") as f:
json.dump(result, f)
# Display concise status for a UI widget
status = (
f"Ningbo congestion: {result['congestion']} | "
f"ratio_vs_baseline: {result['ratio_vs_baseline']} | "
f"as_of: {result['as_of']}"
)
print(status)
if __name__ == "__main__":
main()
Interpreting the 7-day vs 90-day comparison for CNNBO
The key metric is the ratio of recent average port calls per day (7-day window) to the port’s own 90-day baseline. A ratio around 1.0 means activity is near its typical level for the last quarter. A value above 1.0 suggests higher-than-baseline call activity; below 1.0 suggests fewer calls than usual.
In the sample, metrics.ratio_vs_baseline is 1.05 for Ningbo. That’s roughly +5% above baseline activity as of 2026-11-06. Whether you treat 1.05 as “moderate” depends on your alerting thresholds. The categorical congestion label already maps this into a human-friendly bucket, but many teams display both: a badge for at-a-glance consumption and the exact ratio for power users.
Practical integration details that prevent drift
- Freshness: The dataset is weekly with a lag—assume ~6–7 days and verify with the
as_offield. Avoid language like “live” or “today” in your UI without showing the as_of date. A “Last updated: 2026-11-06 (PortWatch)” label is clearer for users. - Units: The underlying metrics are port calls per day. No currency is involved in this endpoint.
- Caching: Cache for 12–24 hours to reduce noisy refreshes; your actual update cadence should align with PortWatch’s weekly republishes. If you detect an unchanged
as_of, you can skip UI updates. - Rate limits: Guests without a key are limited to 100 requests per day per IP. Use your API key in X-API-Key for production services.
- Error handling: Check the top-level
successflag and ensureresults[0]exists. Handle network timeouts and surface theas_ofdate even when you fall back to cached data. - Time zone: Treat
as_ofas a date marker for the PortWatch series rather than a precise timestamp; do not infer intraday freshness from it. - Pagination: This endpoint returns a concise result set for the specified port; no pagination parameters are required in the provided contract.
UI patterns that work for Ningbo status
- Two-line tile: “Ningbo (CNNBO): moderate” and “as_of 2026-11-06; ratio 1.05.” This keeps the classification and the numeric reality together.
- Color mapping: If you choose to color-code, make sure it maps to the documented activity/congestion categories. Do not change the label semantics based on your own thresholds without also updating the legend.
- Tooltips: Explain that the measure is port-call activity (7-day vs 90-day) and not live anchorage or berth-wait time.
Field-by-field notes for CNNBO
- port and locode: Use these for display and cross-referencing. Locode appears with a space (“CN NBO”)—be careful if you’re matching against compact forms.
- congestion/activity: “congestion” is maintained for compatibility; “activity” is the underlying label. Code defensively by checking either and preferring congestion for the UI string shown here.
- metrics.recent_avg_portcalls_per_day: Float. Consider rounding to one decimal for UI, keep full precision in storage.
- metrics.baseline_avg_portcalls_per_day: Float. Same display and storage guidance as above.
- metrics.ratio_vs_baseline: Float. Useful for alerts, e.g., notify if it crosses 0.85 or 1.15 depending on your operational playbook.
- data.meta.source and data.meta.methodology: Good to surface in an “About this data” link or tooltip for auditability.
End-to-end flow you can ship this week
- Fetch CNNBO once daily using your API key and the endpoint shown above.
- Verify
success, parseresults[0], extractcongestion,as_of, andmetrics.ratio_vs_baseline. - Cache for 12–24 hours. If
as_ofhasn’t advanced, keep the current UI state and postpone alerts. - Render: “Ningbo: moderate” and “as_of 2026-11-06; 7d/90d ratio 1.05.”
- Surface an info tooltip explaining PortWatch lag and that this is not live AIS wait time.
FAQ
Is this live congestion or queue time for Ningbo?
No. It’s a PortWatch activity snapshot: a 7-day average of port calls versus a 90-day baseline. It does not measure anchorage or berth waiting time.
How fresh is the Ningbo data?
Expect a ~6–7 day lag, with PortWatch’s metadata indicating a 4–11 day window depending on the weekly republication. Always display the as_of date.
What fields should I store for analytics?
Store as_of, congestion, metrics.recent_avg_portcalls_per_day, metrics.baseline_avg_portcalls_per_day, and metrics.ratio_vs_baseline. Keep the raw JSON for audit if your retention policy allows.
Do I need pagination or additional parameters for CNNBO?
Not for this use case. The provided endpoint takes port=CNNBO and returns a concise result set for Ningbo.
Can I call this without an API key?
Yes, but guests are limited to 100 requests per day per IP. For production services, use the X-API-Key header.
Next steps: create an API key and wire the CNNBO call into your job queue. The docs cover additional fields and context you might want to expose in your UI. Register for access and start integrating today: Register. For implementation details and field references, see the Documentation.