Home → Blog

You’re trying to quantify how congested Colombo (LKCMB) is right now so you can plan sailings, buffers, and allocations with a defensible number. By the end of this post, you’ll be able to call the FreightPulse congestion endpoint for Colombo, parse the response, and wire that signal into your planning or alerting systems.

What the congestion metric measures (and what it does not)

FreightPulse’s port congestion is derived from IMF PortWatch and updated daily with a lag of roughly 6–7 days. It compares the last 7 days’ average port calls to a 90‑day baseline for the same port. This is not live AIS wait-time data and does not report anchorage or berth delays directly.

Illustration: Colombo Port Congestion

Use this feed when you want a stable, comparable indicator of throughput vs. recent history. Do not treat it as a real-time queue length. If you need minute-by-minute vessel positions or ETAs, pair this with a live AIS source in your stack.

Quickstart: fetch Colombo congestion

The endpoint is a simple GET with an auth header. You’ll pass the UN/LOCODE for Colombo: LKCMB.

Auth: include X-API-Key. Without a key, guests are limited to 100 requests per day per IP.

Copy-paste the cURL below to get started.

Live FreightPulse congestion for LKCMB.
Source: live

cURL:

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

JSON:

{
"success": true,
"data": {
"results": [
{
"port": "Colombo",
"locode": "LK CMB",
"country": "Sri Lanka",
"congestion": "moderate",
"as_of": "2026-10-02",
"metrics": {
"recent_avg_portcalls_per_day": 10.1,
"baseline_avg_portcalls_per_day": 11,
"ratio_vs_baseline": 0.92
}
}
],
"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": "ff7f7d1f-b3bc-4feb-ba98-e8fa1c966935"
}
}

You will actually use these fields:

Colombo-specific notes you should build around

Colombo’s code is LKCMB (UN/LOCODE rendered as “LK CMB” in the response). The congestion label is derived from the ratio vs. baseline, so the categorical level and the numeric ratio should be stored together. If you need to present a single signal to users, prefer the ratio for calculations and keep the label for UI display and thresholds.

Do not quote or store a freight-rate USD fare alongside this signal as a “current price.” This endpoint only provides congestion metrics; rates require a separate data source.

Implement it in Python

The snippet below calls the same endpoint and extracts the congestion label, as_of date, and ratio_vs_baseline for Colombo. It includes light validation around the response envelope. Replace YOUR_API_KEY with your key when you have one; unauthenticated use is capped per IP as noted earlier.

import os
import requests
from typing import Optional, Dict, Any

API_URL = "https://freightpulsehq.com/api/v1/port-congestion"
PORT_CODE = "LKCMB"
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")

def fetch_colombo_congestion(session: Optional[requests.Session] = None) -> Dict[str, Any]:
s = session or requests.Session()
headers = {"X-API-Key": API_KEY}
params = {"port": PORT_CODE}

resp = s.get(API_URL, headers=headers, params=params, timeout=20)
resp.raise_for_status()
payload = resp.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 returned for LKCMB")

r0 = results[0]
metrics = r0.get("metrics", {})

# Extract fields you will use downstream
out = {
"port": r0.get("port"),
"locode": r0.get("locode"),
"country": r0.get("country"),
"congestion": r0.get("congestion"),
"as_of": r0.get("as_of"),
"ratio_vs_baseline": metrics.get("ratio_vs_baseline"),
"recent_avg_portcalls_per_day": metrics.get("recent_avg_portcalls_per_day"),
"baseline_avg_portcalls_per_day": metrics.get("baseline_avg_portcalls_per_day"),
# Optional: keep the original envelope if you want to log/debug
"_request_id": (payload.get("meta") or {}).get("request_id"),
"_source": (data.get("meta") or {}).get("source"),
}

# Basic sanity checks to fail fast if data is malformed
if out["as_of"] is None or out["congestion"] is None or out["ratio_vs_baseline"] is None:
raise ValueError("Missing one of required fields: as_of, congestion, ratio_vs_baseline")

return out

if __name__ == "__main__":
try:
result = fetch_colombo_congestion()
print(f"Colombo ({result['locode']}): congestion={result['congestion']}, "
f"ratio_vs_baseline={result['ratio_vs_baseline']}, as_of={result['as_of']}")
except Exception as e:
# For production, branch on HTTP status codes (e.g., 401/403/429) and implement backoff/retry
print(f"Error: {e}")

How to interpret and operationalize the numbers

ratio_vs_baseline is unitless. A value of 1.0 means the last week’s average port calls equals the 90‑day baseline. Lower than 1.0 suggests fewer port calls relative to baseline; higher than 1.0 suggests more. The categorical congestion label maps to ranges of this ratio and is useful for dashboards, while the ratio is better for analytics and alerts.

Port calls per day are counts of vessel port-call events aggregated to a daily rate. These are not dwell times in hours. If your SLA logic expects time-based thresholds, you should calibrate them separately using historical outcomes tied to the ratio or supplement with an AIS wait-time source.

Practical details that save you time

Test checklist for a production integration

Field guide to the response

Core path for Colombo:

Global and meta context:

Origin Catalog Facts (copy-and-ship)

Colombo integration playbook

1) Fetch on a daily cadence

Schedule a daily job (e.g., UTC 06:00) to call the endpoint and persist the latest as_of, congestion, and ratio. Daily fetches are sufficient given the update cadence and lag.

2) Normalize and store

Persist a normalized row keyed by as_of and port code (LKCMB). Store the categorical label and the numeric ratio, plus recent and baseline port calls so you can re-derive the ratio if needed.

3) Alerting thresholds

Tie alerts to the numeric ratio. Example thresholds: notify at ratio < 0.95, escalate at ratio < 0.90 for two consecutive updates. Keep thresholds configurable per port since local baselines differ by season.

4) UI display

Show the congestion label and the ratio with the as_of date and the “~6–7 day lag” disclosure from data.meta.data_lag_days. Add a help icon that links to methodology so operators understand what the number means.

5) Downstream consumers

Expose a simple internal API or table that joins Colombo’s congestion ratio with lanes that call at Colombo, enabling procurement and scheduling systems to adjust buffers or short-term allocations.

FAQ

Is this live AIS wait-time data?
No. It is IMF PortWatch derived, updated daily with a ~6–7 day lag, and reflects 7‑day average port calls vs a 90‑day baseline. It does not measure anchorage or berth wait times.

What identifier should I use for Colombo?
Use LKCMB as the port parameter. The response shows “LK CMB” as the locode text.

How often should I refresh?
Daily is appropriate. Cache results for 24 hours and refresh when as_of changes. The feed is not real-time.

What are the units?
Port-call metrics are ships per day. ratio_vs_baseline is unitless. The as_of field is a YYYY-MM-DD date string.

Can I use this to quote today’s freight rates?
No. Do not quote a USD rate from this endpoint. It provides congestion metrics only.

Get access and build your Colombo monitor

Start with a free account to get an API key, then wire the endpoint above into your data workflow for Colombo. Read the full API reference and methodology notes, and keep both the categorical label and numeric ratio in your downstream systems.

Register • Documentation