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.
For Hamburg (DEHAM), this is the ground truth for any automated workflow you build from the endpoint in this article. You should plan for:
- Data that updates daily, but reflects conditions with a ~6–7 day lag.
- Signals based on relative port-call volume, not per-vessel delays.
- Occasional temporary unavailability if the upstream IMF PortWatch service returns an error.
Exact API facts you’ll use
- Auth: header X-API-Key: YOUR_API_KEY. Guest without key: 100 requests/day per IP.
- Envelope: {"success":true,"data":{...}}
- Port congestion is IMF PortWatch, lagged ~6-7 days, 7-day avg vs 90-day baseline — not live AIS.
- Do not quote a freight-rates USD fare as today’s price.
- Port: Hamburg (DEHAM)
- curl: curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=DEHAM"
- No official congestion JSON is published for DEHAM. Show the curl and field names (congestion, ratio_vs_baseline, as_of). Do not reuse the USLAX 0.85 fixture.
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:
- congestion (alias of activity): the categorical or numeric signal for relative activity.
- ratio_vs_baseline: a floating-point ratio comparing the last 7 days’ average calls to the 90-day baseline.
- as_of: the effective date for the metric, reflecting the ~6–7 day lag.
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:
- It is a 7-day average compared to a 90-day baseline for Hamburg only.
- It is updated daily but lags real-world conditions by approximately 6–7 days.
- Avoid labeling charts as “live” or “real-time” for compliance and user clarity.
- If you also use live AIS sources elsewhere, keep them clearly separated from this PortWatch-derived signal.
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:
- Treat available: false as a non-fatal state. Keep the last known as_of datapoint and annotate the UI.
- Use backoff on retries (e.g., 15 minutes, then hourly), since the data cadence is daily anyway.
- Surface the response meta.methodology and meta.data_lag_days somewhere in your app to set expectations.
- Log payload.meta.request_id to correlate support inquiries.
Practical integration details that save time
- Authentication: Always send X-API-Key: YOUR_API_KEY. Guests are limited to 100 requests/day per IP; production apps should register for a key.
- Cadence and caching: Data updates daily with ~6–7 day lag. Cache responses for at least a few hours; daily caching is typically sufficient for this signal.
- Envelope: The top-level shape is {"success":true,"data":{...}} with results inside data.results. Check success before parsing.
- Single-port queries: This endpoint returns data scoped to the requested port (DEHAM). Treat pagination as not applicable for a single-port call.
- Fields to read: congestion (alias activity), ratio_vs_baseline, as_of. Handle the case where these fields are absent during temporary outages.
- Global context: data.global_summary aggregates categories across ports; it’s optional for a Hamburg-only UI but useful if you later expand your dashboard.
- Units and labels: ratio_vs_baseline is a dimensionless ratio. Label it clearly (e.g., “7d/90d port-call ratio”). Do not conflate it with minutes, hours, or USD.
- Time references: as_of reflects the effective date for the 7-day window; show this near the chart title to prevent misinterpretation as real-time data.
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
- Smoke test: Run the endpoint hourly and alert if the HTTP code is not 200 or if success is false.
- Availability tracking: Count consecutive available:false responses. After a threshold, display a user-facing banner.
- Cache policy: Persist the last valid activity triple (congestion, ratio_vs_baseline, as_of). Invalidate when a newer as_of arrives.
- Change detection: Diff ratio_vs_baseline day-over-day and trigger alerts only after a material change to avoid flapping.
Compliance reminders for Hamburg dashboards
- Do not market this as live anchorage or live wait times; it’s a lagged activity signal.
- Label the panel as “7-day average vs 90-day baseline (IMF PortWatch, ~6–7 day lag).”
- Avoid quoting or implying current USD freight rates alongside this metric as “today’s price.” They are separate products and units.
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.