Home → Blog

You’re trying to factor Antwerp’s port congestion into routing, ETA buffers, or procurement dashboards, but you need a reliable, automatable signal rather than anecdotal “vessel wait time” chatter. By the end of this guide you’ll be able to programmatically fetch, parse, cache, and operationalize FreightPulse’s Antwerp congestion data (BEANR) using a single endpoint and two fields you actually need: congestion and ratio_vs_baseline, plus the as_of date for traceability.

What the Antwerp congestion metric actually measures

FreightPulse’s port congestion series is sourced from IMF PortWatch and refreshed daily with a lag of roughly 6–7 days. It is not a live AIS wait-time feed. Instead, congestion is computed as the last 7 days’ average port calls compared with a 90-day baseline for the same port. That ratio is then bucketed into a readable congestion label (for example, low, moderate, high). The methodology focuses on throughput signal (port calls), not anchorage or berth waiting time.

Illustration: Antwerp Port Congestion

For operational use, read this as: “How busy is Antwerp, relative to its own recent normal?” It’s an early-warning trend, not a live queue length.

Endpoint, authentication, and envelope

Use a single GET endpoint to query Antwerp (BEANR):

The “data” object includes a results array with the Antwerp record, plus a meta block describing source and methodology. The primary fields you’ll consume are:

Units and conventions:

Antwerp congestion via cURL and JSON

The following request and JSON are the official sample for BEANR. You can copy this cURL as-is and swap in your API key if you have one.

Live FreightPulse congestion for BEANR.
Source: live

cURL:

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

JSON:

{
"success": true,
"data": {
"results": [
{
"port": "Antwerp",
"locode": "BE ANR",
"country": "Belgium",
"congestion": "moderate",
"as_of": "2026-10-02",
"metrics": {
"recent_avg_portcalls_per_day": 55.9,
"baseline_avg_portcalls_per_day": 53.7,
"ratio_vs_baseline": 1.04
}
}
],
"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": "f6015aa0-a2fa-4533-9c60-fd4d0528e26b"
}
}

What to read from this payload:

Parse Antwerp congestion in Python

This example calls the same endpoint, checks the top-level envelope, and extracts congestion, ratio_vs_baseline, and as_of. Replace YOUR_API_KEY with your real key if you have one; otherwise omit the header and rely on the guest quota (100 requests/day per IP).

import os
import sys
import json
import urllib.request

API_URL = "https://freightpulsehq.com/api/v1/port-congestion?port=BEANR"
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY") # or leave header out if using guest quota

def fetch_antwerp_congestion():
req = urllib.request.Request(API_URL)
# Use API key header if provided; guest users may omit this line
if API_KEY and API_KEY != "YOUR_API_KEY":
req.add_header("X-API-Key", API_KEY)
else:
# If you're intentionally testing the guest path, comment this next line out
req.add_header("X-API-Key", API_KEY)

with urllib.request.urlopen(req, timeout=20) as resp:
body = resp.read().decode("utf-8")
return json.loads(body)

def main():
try:
payload = fetch_antwerp_congestion()
except Exception as e:
print(f"Request failed: {e}", file=sys.stderr)
sys.exit(1)

if not isinstance(payload, dict) or not payload.get("success"):
print("Unexpected response shape or success=false", file=sys.stderr)
print(json.dumps(payload, indent=2))
sys.exit(2)

data = payload.get("data", {})
results = data.get("results", [])
if not results:
print("No results for BEANR", file=sys.stderr)
sys.exit(3)

antwerp = results[0]
congestion = antwerp.get("congestion")
as_of = antwerp.get("as_of")

metrics = antwerp.get("metrics", {})
ratio = metrics.get("ratio_vs_baseline")
recent = metrics.get("recent_avg_portcalls_per_day")
baseline = metrics.get("baseline_avg_portcalls_per_day")

print(f"Antwerp congestion: {congestion}")
print(f"As of date: {as_of}")
print(f"Ratio vs 90-day baseline: {ratio}")
print(f"Recent avg port calls/day: {recent}")
print(f"Baseline avg port calls/day: {baseline}")

# Example: interpret the ratio for routing buffers
if isinstance(ratio, (int, float)):
if ratio > 1.1:
print("Signal: activity is well above baseline; consider extra dwell buffers.")
elif ratio < 0.9:
print("Signal: activity is below baseline; buffers may be relaxed (still monitor).")
else:
print("Signal: near baseline; follow normal ops with routine monitoring.")

if __name__ == "__main__":
main()

Integration checklist and time-savers

Applying Antwerp’s ratio and label to operations

Use the categorical congestion label for quick UI signals and basic triage (for example, moderate may display amber). The ratio_vs_baseline is where the operational nuance lives. For instance, 1.04 implies Antwerp’s recent port-call activity is about 4% above its 90-day baseline. In practice, you can bind alerting thresholds around 0.9 and 1.1 to capture meaningful deviations, with hysteresis to avoid flapping.

If you drive ETAs or booking buffers from this signal, define a simple mapping, e.g. “+1 day hold when ratio > 1.1 and congestion not low,” or adapt contract SLAs for terminal handling. Keep in mind the lag: a sharp event that occurred within the last few days might not be fully reflected yet. Pair this dataset with your own exceptions feed (missed connections, rolled bookings) to validate the thresholds you set.

Field-by-field notes for BEANR

Quality guardrails and what not to do

Operational patterns that work

FAQ

Is the Antwerp congestion number real-time?

No. It is lagged by roughly 6–7 days and updated daily. It measures 7-day average port calls vs a 90-day baseline, not live anchorage or berth wait times.

How often should I poll the endpoint?

Once per day is sufficient, as the data updates daily and reflects a lagged snapshot. Cache the response and key on as_of.

What happens if I call without an API key?

Guest access is available and limited to 100 requests per day per IP. For higher volume or multiple ports, set the X-API-Key: YOUR_API_KEY header.

Which fields should I use in production?

congestion (label), metrics.ratio_vs_baseline (numeric), as_of (date). Optionally show recent_avg_portcalls_per_day and baseline_avg_portcalls_per_day for analyst transparency.

Can I infer dwell time or queue length from this?

No. The methodology is throughput-based and not a measure of vessel waiting. Treat it as a relative busyness signal.

Get a key and wire this into your pipeline in minutes. Read the Documentation and grab your account here: Register.