Home → Blog

You need to know whether Hamburg’s container gateway is experiencing unusual activity, but “live AIS wait times” and social screenshots won’t cut it. By the end of this post, you’ll be able to query lagged, daily-updated IMF PortWatch data for Hamburg (DEHAM), interpret the 7-day average vs 90-day baseline signal correctly, and integrate it into your dashboards with resilient error handling.

What you can and can’t measure for Hamburg right now

FreightPulse exposes IMF PortWatch-derived port activity (often referred to as “congestion” as a legacy alias) with a ~6–7 day data lag. That means you’re not pulling live anchorage or per-ship wait times from AIS in real time; instead, you’re comparing the most recent seven days of port-call activity against a rolling 90-day baseline for the same port.

Illustration: Hamburg Congestion Today

For Hamburg (DEHAM), this is the ground truth for any automated workflow you build from the endpoint in this article. You should plan for:

Exact API facts you’ll use

How to query Hamburg (DEHAM)

Use the endpoint below to retrieve Hamburg’s activity signal. Authentication is via the X-API-Key header. Guests can make up to 100 requests/day per IP without a key, but production apps should include a key.

Live FreightPulse congestion for DEHAM.
Source: live

cURL:

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

JSON:

{
    "success": true,
    "data": {
        "results": [
            {
                "port": "Hamburg",
                "locode": "DE HAM",
                "country": "Germany",
                "available": false,
                "reason": "Temporary error querying IMF PortWatch, retry shortly"
            }
        ],
        "global_summary": {
            "ports_high_activity": 0,
            "ports_moderate_activity": 0,
            "ports_low_activity": 0,
            "ports_high_congestion": 0,
            "ports_moderate": 0,
            "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": "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": "bddb2605-ae98-48b1-a308-13d12945165e"
    }
}

What you’ll actually use when available:

For DEHAM at the time shown above, the response indicates available: false due to a temporary upstream error. Your integration should handle this by retrying later and surfacing a clear “data temporarily unavailable” state to users.

JavaScript example: robust fetch for Hamburg with fallbacks

The snippet below calls the same endpoint, extracts the Hamburg record, and safely reads the congestion, ratio_vs_baseline, and as_of fields when present. It also handles the temporary unavailability shown in the official sample by checking available and reason.

// Minimal, resilient fetch for Hamburg (DEHAM)
async function fetchHamburgActivity() {
const url = "https://freightpulsehq.com/api/v1/port-congestion?port=DEHAM";
const res = await fetch(url, {
headers: { "X-API-Key": "YOUR_API_KEY" }
});

if (!res.ok) {
throw new Error(`HTTP ${res.status} when querying DEHAM`);
}

const payload = await res.json();
// Envelope is { success: true, data: {...} }
const results = payload && payload.data && payload.data.results ? payload.data.results : [];
if (!Array.isArray(results) || results.length === 0) {
return { status: "no_data", message: "No DEHAM record in response" };
}

const deham = results.find(r => (r.locode || "").replace(/\s+/g, "").toUpperCase() === "DEHAM") || results[0];

// Handle temporary upstream error (available: false)
if (deham.available === false) {
return {
status: "temporarily_unavailable",
port: deham.port,
locode: (deham.locode || "").replace(/\s+/g, ""),
reason: deham.reason || "Upstream IMF PortWatch temporarily unavailable"
};
}

// When available, prefer the explicit activity fields if present
// Fields to read when provided by the API:
// - congestion (alias of activity)
// - ratio_vs_baseline
// - as_of
const activity = {
port: deham.port || "Hamburg",
locode: (deham.locode || "DE HAM").replace(/\s+/g, ""),
congestion: deham.congestion !== undefined ? deham.congestion : null,
ratio_vs_baseline: deham.ratio_vs_baseline !== undefined ? deham.ratio_vs_baseline : null,
as_of: deham.as_of || null
};

return { status: "ok", activity };
}

// Example usage
fetchHamburgActivity()
.then(console.log)
.catch(err => console.error("DEHAM query failed:", err));

Interpreting the Hamburg signal correctly

IMF PortWatch activity is about relative port-call volume, not anchorage or berth wait times. The endpoint’s methodology (included in the response meta) states that the “congestion” label is a deprecated alias for activity—low means fewer calls than usual. That can be consistent with either true congestion (ships waiting outside and not making calls) or a disruption/closure. Do not treat it as a direct measure of vessel wait duration.

Key points when you chart or alert on DEHAM:

Error handling and resilience patterns

As shown in the official JSON, the source can occasionally return available: false with a reason string like “Temporary error querying IMF PortWatch, retry shortly.” Build for this:

Practical integration details that save time

Building a Hamburg widget that’s hard to misuse

1) Make the lag explicit

Next to the headline number, include “Updated daily, ~6–7 day lag” and display as_of. This prevents users from assuming real-time anchorage metrics.

2) Show the ratio and the classification

Expose both ratio_vs_baseline and a concise label mapped from congestion/activity if provided by the API. Users benefit from both the numeric ratio and a qualitative band.

3) Avoid mixing incompatible signals

If you add a separate live AIS wait-time panel, keep it visually and semantically distinct. Name the PortWatch-derived panel “Port-call activity vs baseline,” not “Live congestion.”

4) Handle unavailability gracefully

When available: false, don’t blank the chart. Dim it, show the last cached datapoint (with as_of), and include the reason text from the API. Schedule a retry later.

Minimal Python alternative

If you prefer Python for server-side tasks, this minimal example mirrors the JavaScript logic and surfaces the same fields when available.

import os
import requests

API_KEY = os.environ.get("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")
URL = "https://freightpulsehq.com/api/v1/port-congestion?port=DEHAM"

def fetch_deham():
r = requests.get(URL, headers={"X-API-Key": API_KEY}, timeout=30)
r.raise_for_status()
payload = r.json()

if not payload.get("success", False):
return {"status": "error", "message": "API returned success=false"}

data = payload.get("data", {})
results = data.get("results", [])
if not results:
return {"status": "no_data", "message": "No results for DEHAM"}

deham = None
for r_ in results:
locode = (r_.get("locode", "")).replace(" ", "").upper()
if locode == "DEHAM":
deham = r_
break
deham = deham or results[0]

if deham.get("available") is False:
return {
"status": "temporarily_unavailable",
"reason": deham.get("reason", "Upstream IMF PortWatch temporarily unavailable"),
"port": deham.get("port", "Hamburg"),
"locode": (deham.get("locode", "DE HAM")).replace(" ", ""),
}

# Extract the key fields when provided
return {
"status": "ok",
"port": deham.get("port", "Hamburg"),
"locode": (deham.get("locode", "DE HAM")).replace(" ", ""),
"congestion": deham.get("congestion"),
"ratio_vs_baseline": deham.get("ratio_vs_baseline"),
"as_of": deham.get("as_of"),
}

if __name__ == "__main__":
print(fetch_deham())

Testing and monitoring your DEHAM integration

Compliance reminders for Hamburg dashboards

Where to go from here

If you manage multiple European gateways, replicate the same pattern for each port using its LOCODE parameter. Keep the handling for available:false identical across ports so your UX behaves consistently during upstream retries. If you later add live AIS analytics, render them on separate charts to keep semantics and user expectations clear.

FAQ

Is this a real-time anchorage or wait-time metric for Hamburg?
No. It’s a lagged port-call activity signal from IMF PortWatch: a 7-day average vs a 90-day baseline with ~6–7 days of lag. It does not measure time-at-anchor or berth delays.

What should I display when the API says available: false for DEHAM?
Keep the last cached activity datapoint on screen, annotate it as temporarily unavailable, show the reason string, and retry later. Don’t clear the chart unless you have no cached value.

Which fields should I read when data is available?
Read congestion (alias of activity), ratio_vs_baseline, and as_of. Use ratio_vs_baseline for numeric charts and as_of as the date label.

How often should I poll and cache?
Daily updates are sufficient given the data lag. If you poll more frequently for status, cache responses for several hours and only update visuals when as_of changes.

Do I need an API key?
You can make up to 100 requests/day per IP without a key, but for production reliability you should add an API key using the X-API-Key header.

Get a production key and stand up your Hamburg (DEHAM) widget in minutes. Register for access at Register, and keep the docs handy at Documentation.