You need reliable, developer-friendly congestion signals for Hamburg (DEHAM) to drive ETAs, replans, and customer messaging—but most feeds mix in live AIS waits or stale anecdotes. By the end of this guide, you’ll be able to query FreightPulse’s IMF PortWatch–based congestion for DEHAM, handle the current “no data available” condition safely, and ship an integration that degrades gracefully without blocking your workflows.
What this dataset is (and isn’t)
FreightPulse Port Congestion uses IMF PortWatch and is lagged by approximately 6–7 days. Values represent a 7-day average relative to a 90-day baseline. This is not a live AIS queue-length or wait-time feed. Do not treat it as real-time vessel waiting times or a same-day operational clock.
When data is available for a port, you’ll consume it from a single JSON envelope. When it isn’t available, you should detect the condition and either keep your last-known value or show a neutral state as appropriate for your application.
Endpoint and authentication
Use a simple GET to the congestion endpoint with the port code set to DEHAM (Hamburg). Authentication is via an API key header. Guests without a key are allowed 100 requests per day per IP address.
- Method: GET
- URL: https://freightpulsehq.com/api/v1/port-congestion?port=DEHAM
- Auth header: X-API-Key: YOUR_API_KEY
- Envelope: {"success": true, "data": {...}}
Reference links: Register, Documentation
Field names you will use when DEHAM data is available
When the Hamburg series is available, read these fields from each result row:
- congestion
- ratio_vs_baseline
- as_of
Interpretation:
- congestion: A congestion index derived from IMF PortWatch methodology, aligned to a 7-day average. Units are not specified; consume it as an index value.
- ratio_vs_baseline: A unitless ratio that compares the 7-day average to the 90-day baseline for context (e.g., 1.10 would indicate 10% above baseline). Do not invent or reuse a ratio from another port.
- as_of: Timestamp for the reading. Treat timestamps as provided by the API and display their timezone indicator if present; do not assume local time.
Note: Do not quote any freight-rates USD figures as today’s price in this context—the endpoint is a congestion signal only.
Official sample for DEHAM (current behavior)
The following official cURL and JSON are the live sample for DEHAM at the time of writing. Copy exactly; do not alter the URL or field names.
cURL:
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=DEHAM"
JSON:
{
"success": true,
"data": {
"results": [],
"available": false,
"reason": "Error querying IMF PortWatch"
},
"meta": {
"request_id": "3119293e-45b7-4932-bd6a-ef5734c9125d"
}
}
What this means:
- data.available is false for DEHAM right now; there are no result rows.
- data.results is an empty array. Your code should guard against this and avoid dereferencing nonexistent fields.
- data.reason provides a human-readable diagnostic you can log, but don’t hard-code business logic to its text.
- meta.request_id can be logged for support correlation.
Production-grade handling for Hamburg (DEHAM)
Since DEHAM is currently unavailable, your application should be resilient:
- Zero results path: If data.available is false or data.results.length is 0, bypass calculations that require congestion or ratio_vs_baseline.
- Fallback display: Keep showing your last-known value from a prior successful response for DEHAM (if you’ve cached one) and label it with its as_of and “lagged ~6–7 days”. If you have no cached value, display “No congestion data available (IMF PortWatch).”
- Retry cadence: Poll on your normal schedule—because this is a lagged dataset, there’s little value in retrying multiple times per minute. Once or twice daily is typical.
- Alerting: Alert only after a sustained outage window you define (e.g., 24–48 hours of unavailable) to avoid noise.
Python example: request, parse, and robustly handle empty results
This sample demonstrates an end-to-end call for Hamburg and safe parsing that respects the current “available: false” state. When data becomes available, it will read congestion, ratio_vs_baseline, and as_of from each row.
import os
import sys
import time
import json
import urllib.request
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY")
URL = "https://freightpulsehq.com/api/v1/port-congestion?port=DEHAM"
def fetch_deham_congestion():
req = urllib.request.Request(URL, headers={"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 summarize(payload):
# Base envelope guards
if not isinstance(payload, dict) or not payload.get("success", False):
return {"status": "error", "message": "Request failed or invalid envelope."}
data = payload.get("data", {})
available = data.get("available", None)
results = data.get("results", [])
# Unavailable or empty path
if available is False or not results:
reason = data.get("reason", "Unavailable")
return {
"status": "no_data",
"available": False,
"reason": reason,
"results_count": len(results),
"request_id": (payload.get("meta") or {}).get("request_id")
}
# Available path: map only the fields we need
parsed = []
for row in results:
# Defensive extraction; ignore extra fields if present
parsed.append({
"congestion": row.get("congestion"),
"ratio_vs_baseline": row.get("ratio_vs_baseline"),
"as_of": row.get("as_of")
})
return {
"status": "ok",
"available": True,
"results": parsed,
"request_id": (payload.get("meta") or {}).get("request_id")
}
if __name__ == "__main__":
try:
payload = fetch_deham_congestion()
summary = summarize(payload)
print(json.dumps(summary, indent=2))
except Exception as e:
print(json.dumps({"status": "exception", "error": str(e)}))
sys.exit(1)
Notes:
- Keep the key in X-API-Key: YOUR_API_KEY or an environment variable. Do not hard-code a real key in source control.
- The sample returns a minimal object you can pass to UI or logging. Extend as needed.
- When data becomes available, you’ll get congestion, ratio_vs_baseline, and as_of per row; the ratio is dimensionless and compares the 7-day average vs the 90-day baseline.
Display and messaging patterns for Hamburg
Given the lagged nature (~6–7 days), choose copy that sets clear expectations:
- If available: “Hamburg congestion index (7-day avg vs 90-day baseline), as of [timestamp].”
- If unavailable: “No IMF PortWatch congestion data available for DEHAM at this time.”
For programmatic alerts, emit a single daily summary with the last known as_of; do not roll this up into “live wait time” messaging. Keep the signal distinct from AIS-based monitors if you run both in your stack.
Caching, schedules, and rate considerations
Practical integration tips that will save time:
- Caching: Because the series is lagged, a daily fetch is generally sufficient. Cache by port code and as_of timestamp to avoid redundant updates to downstream stores.
- Idempotency: If your ETL or job runner processes the same as_of twice, store writes should be idempotent keyed by (port, as_of).
- Guest limit: Without an API key, you may be rate-limited to 100 requests/day per IP. Use an API key in production and centralize calls through your server.
- Timezones: Treat as_of as authoritative; surface the timezone indicator if present. Do not coerce to local user time without labelling.
- Error handling: Log meta.request_id so Support can correlate any anomalies you encounter.
Building a minimal Hamburg congestion service
Interface
Expose a function or endpoint in your app: getHamburgCongestion(). It should:
- Call the FreightPulse endpoint for DEHAM.
- Return a stable object with keys: status, available, results (array of {congestion, ratio_vs_baseline, as_of}) when present, and request_id.
- Handle empty results without throwing.
Storage
- Table columns: port (string), as_of (timestamp), congestion (numeric), ratio_vs_baseline (numeric), source (“IMF PortWatch”), inserted_at (timestamp).
- Primary key: (port, as_of) to ensure idempotent upserts.
Scheduling
- Run daily once the lag window has likely updated. Consider a morning UTC schedule and alert only on multi-day unavailability.
UI/UX guardrails for Hamburg
Because DEHAM is currently unavailable per the official sample, validate empty states in your UI:
- Charts: If results is empty, render an empty-state card with a short explanation and a “Last checked [timestamp]” note.
- Tables: Show zero rows with a caption that the series is not available for DEHAM yet. Avoid showing misleading zeros.
- Exports: For CSV/JSON exports, include headers but zero rows when unavailable; document that the source is IMF PortWatch with a 6–7 day lag.
Operational boundaries and disclaimers
- Scope: This guide and endpoint are scoped to Hamburg (DEHAM). Do not substitute values from other ports.
- No live AIS: Do not treat this as live wait-time telemetry or infer queue lengths from it.
- No pricing: Do not quote freight-rate USD fares here; this is not a rates endpoint.
- Fields: Use only the fields noted above. If new fields appear, treat them as optional until documented.
Quick checklists
Before shipping
- Can the code handle data.available = false?
- Is the UI readable when results = []?
- Is caching keyed by port + as_of?
- Are logs capturing meta.request_id on every call?
- Is your scheduler set to a daily cadence?
When data becomes available
- Verify congestion, ratio_vs_baseline, and as_of parse correctly.
- Annotate any charts with “7-day average vs 90-day baseline; lagged ~6–7 days.”
- Write integration tests for at least one row in results.
FAQ
Q1: Why is DEHAM unavailable right now?
A: The official sample indicates an error querying IMF PortWatch for Hamburg. The API reports available: false and provides a reason message. Your code should not depend on the specific text of reason.
Q2: How often should I poll DEHAM?
A: Because the series is lagged ~6–7 days, daily polling is sufficient. More frequent polling won’t yield meaningfully fresher data.
Q3: What should I show if results is empty?
A: Keep your last-known value (if any) labeled with its as_of and a note that it’s lagged, or display a neutral “No IMF PortWatch congestion data for DEHAM” state. Do not display zeros as if they were measurements.
Q4: What are the exact fields to read when data arrives?
A: congestion, ratio_vs_baseline, and as_of. The ratio is a dimensionless comparison of the 7-day average vs the 90-day baseline.
Q5: Can I combine this with live AIS waits?
A: You can, but keep labels clear. This API is not live AIS. If you blend signals, present each source distinctly so users don’t mistake a lagged index for a live wait time.
Next steps
Create a key, wire up the single GET call for DEHAM, and ship a UI that clearly communicates the 7-day average vs 90-day baseline with a 6–7 day lag. When the Hamburg series becomes available, your integration will already be production-ready. Get started now: Register and review the Documentation.