You need a reliable way to quantify current congestion at the Port of Savannah without building your own AIS pipeline. By the end of this guide, you will be able to query and interpret Savannah’s congestion signal using FreightPulse’s IMF PortWatch feed, integrate it into code, and ship an alert or dashboard that uses a 7-day average versus a 90-day baseline—without mistaking it for live vessel wait times.
What Savannah “congestion” measures (and what it doesn’t)
FreightPulse exposes a congestion indicator that compares the last 7 days’ average port calls to a 90-day baseline for the same port. This comes from IMF PortWatch, which aggregates UN Global Platform AIS data. The feed updates daily and is generally lagged by about 6–7 days. It is a robust, comparable trend signal across time.
It is not a live AIS anchorage or berth-wait metric, does not provide minute-by-minute queue lengths, and it should not be used as a same-day operational dispatch signal. Treat it as a statistically smoothed port traffic indicator, useful for supply chain planning, capacity-risk monitoring, and routing heuristics that tolerate a few days of data latency.
Quick facts for implementers
- 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: Savannah (USSAV)
- curl: curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=USSAV"
- Show the key fields you will use: congestion, ratio_vs_baseline, as_of.
How to query Savannah congestion
Use a simple GET request with your API key in the X-API-Key header. The port parameter for Savannah is USSAV. Responses are wrapped in a top-level success flag and a data object containing results, plus a meta block that describes the methodology and data lag.
If you don’t provide a key, the service allows a guest quota (100 requests/day per IP), suitable for light prototyping. For production and higher frequency polling, register for your own key.
cURL request
Copy and run this call as-is (replace YOUR_API_KEY with your token if you have one):
curl -H "X-API-Key: YOUR_API_KEY" "https://freightpulsehq.com/api/v1/port-congestion?port=USSAV"
Official JSON response
Below is the official sample for USSAV. Use it to shape your parser and unit tests.
{
"success": true,
"data": {
"results": [
{
"port": "Savannah",
"locode": "US SAV",
"country": "United States",
"congestion": "moderate",
"as_of": "2026-10-02",
"metrics": {
"recent_avg_portcalls_per_day": 5.9,
"baseline_avg_portcalls_per_day": 5.9,
"ratio_vs_baseline": 0.99
}
}
],
"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": "82989f45-8c00-4078-9a0c-fba1d369456f"
}
}
Fields to read for operational logic:
- data.results[0].congestion: A qualitative band such as low, moderate, or high.
- data.results[0].metrics.ratio_vs_baseline: Recent 7-day average port calls divided by the 90-day baseline (unitless ratio).
- data.results[0].as_of: ISO-8601 date for the metric window, lagged ~6–7 days.
Interpreting the Savannah signal
For USSAV, you will typically compare ratio_vs_baseline against internal thresholds to trigger routing checks or capacity warnings. A ratio near 1.0 suggests activity is close to its recent baseline, while materially lower or higher values can indicate atypical slowdowns or surges in port calls. The congestion band condenses this into a discrete label for fast triage, but you should keep the underlying ratio for alert tuning and backtesting.
Remember that “congestion” here is derived from port-calls volume, not berth occupancy or anchorage wait. Port calls can be affected by schedule changes, blank sailings, and seasonality. Use the ratio as one feature among others (e.g., terminal advisories, inland dray capacity signals) if your workflow is sensitive to same-week volatility.
Python: fetch, parse, and act on USSAV congestion
The snippet below requests USSAV congestion, extracts the key fields, and demonstrates a minimal decision rule. It also sketches a daily cache pattern to avoid redundant requests given the data’s cadence.
import os
import json
import time
import requests
API_URL = "https://freightpulsehq.com/api/v1/port-congestion"
PORT_CODE = "USSAV"
API_KEY = os.getenv("FREIGHTPULSE_API_KEY", "YOUR_API_KEY") # replace in production
# Simple daily cache (file-based). Data is lagged ~6-7 days and updates daily.
CACHE_FILE = "ussav_congestion_cache.json"
CACHE_TTL_SECONDS = 12 * 60 * 60 # refresh at most twice per day
def load_cache():
if not os.path.exists(CACHE_FILE):
return None
try:
mtime = os.path.getmtime(CACHE_FILE)
if time.time() - mtime > CACHE_TTL_SECONDS:
return None
with open(CACHE_FILE, "r", encoding="utf-8") as f:
return json.load(f)
except Exception:
return None
def save_cache(payload):
try:
with open(CACHE_FILE, "w", encoding="utf-8") as f:
json.dump(payload, f)
except Exception:
pass
def fetch_ussav_congestion():
headers = {"X-API-Key": API_KEY}
params = {"port": PORT_CODE}
resp = requests.get(API_URL, headers=headers, params=params, timeout=20)
resp.raise_for_status()
return resp.json()
def extract_fields(doc):
# Expecting the official envelope: {"success": true, "data": {...}}
if not doc.get("success"):
raise ValueError("API request did not succeed")
results = doc.get("data", {}).get("results", [])
if not results:
raise ValueError("No results for USSAV")
r0 = results[0]
congestion_band = r0.get("congestion")
as_of = r0.get("as_of") # ISO date (lagged ~6-7 days)
metrics = r0.get("metrics", {})
ratio = metrics.get("ratio_vs_baseline")
recent_avg = metrics.get("recent_avg_portcalls_per_day")
baseline_avg = metrics.get("baseline_avg_portcalls_per_day")
return {
"congestion": congestion_band,
"as_of": as_of,
"ratio_vs_baseline": ratio,
"recent_avg": recent_avg,
"baseline_avg": baseline_avg,
}
def compute_advice(ratio, congestion_band):
# Example rule: flag checks if ratio deviates materially from baseline
if ratio is None:
return "no_data"
if ratio < 0.85:
return "risk_downturn_check_routing"
if ratio > 1.15:
return "surge_monitor_terminal_capacity"
# Optionally incorporate the qualitative band for human-facing messages
return f"normal_monitoring_{congestion_band or 'unknown'}"
def main():
cached = load_cache()
if cached:
doc = cached
else:
doc = fetch_ussav_congestion()
save_cache(doc)
fields = extract_fields(doc)
action = compute_advice(fields["ratio_vs_baseline"], fields["congestion"])
print(json.dumps({
"port": "USSAV",
"as_of": fields["as_of"],
"congestion": fields["congestion"],
"ratio_vs_baseline": fields["ratio_vs_baseline"],
"action": action
}, indent=2))
if __name__ == "__main__":
main()
Notes for production:
- Timeouts and retries: Use a bounded retry strategy (e.g., 3 attempts with jittered backoff) to handle transient network issues.
- Caching: Because the dataset is updated daily and lagged, polling more often than a few times per day is rarely useful.
- Guest quota: Without a key, you are limited to 100 requests/day per IP. Use your own key in the X-API-Key header for sustained integrations.
Dashboards and alerting patterns that work for USSAV
As a general pattern, persist the last N daily observations for USSAV and compute week-over-week and month-over-month comparisons. The provided ratio_vs_baseline already normalizes for recent history, but tracking its drift helps you detect inflection points earlier than waiting for a band transition from, say, moderate to high.
To minimize alert fatigue, prefer banded alerts augmented with a hysteresis margin on the ratio. For example, do not clear an alert until the ratio crosses back through a tighter threshold neighborhood, or require two consecutive daily updates to confirm a state change. Because the “as_of” date is daily and lagged, the maximum alert resolution is daily—avoid intra-day checks for this specific signal.
Field references and units
- congestion: qualitative band computed from the 7-day average vs 90-day baseline for the same port.
- metrics.ratio_vs_baseline: unitless ratio (recent 7-day avg / 90-day baseline).
- metrics.recent_avg_portcalls_per_day: average daily port calls over the recent 7-day window.
- metrics.baseline_avg_portcalls_per_day: 90-day average daily port calls.
- as_of: ISO-8601 date string representing the reference date of the published statistic. Updates daily with ~6–7 days lag.
There is no currency field in this endpoint. Do not attempt to infer freight rates from this data, and do not present any rate in USD as “today’s price” on the basis of a congestion readout.
Error handling and envelope parsing
Always check the top-level success flag before reading data.results. If success is false or data is missing, handle it as a soft failure and preserve the last good observation in your cache. For observability, log the meta.request_id so you can correlate issues with support if needed.
The endpoint returns an array under data.results. For single-port queries like USSAV, parse the first element. If you later query multiple ports, iterate this array and store each record keyed by its port or UN/LOCODE.
When to prefer the band vs. the ratio
Use the congestion band for human-facing UIs and quick filters. It communicates a state effectively (e.g., “moderate”) and aligns well with triage workflows. Use ratio_vs_baseline for automated decision rules and analytics. The continuous ratio allows you to set custom thresholds, compare across time windows, and perform backtests on alert precision/recall.
For Savannah specifically, baselining via the ratio helps account for seasonal shifts in scheduled port calls. If your planning horizon is weekly, align your alert logic to weekly cadences and refresh your cache once or twice per day.
Security, quotas, and deployment hygiene
- Keys: Store your FreightPulse key in a secret manager or environment variable. Do not hardcode in client-side code.
- Guest access: 100 requests/day per IP are allowed without a key, sufficient for basic evaluation.
- Server polling: Since data updates daily, a single daily poll per environment is typically enough. Fan out to internal services from your cache or message bus.
- Versioning: If you wrap a microservice around this call, expose only the fields you use (congestion, ratio_vs_baseline, as_of) to reduce downstream coupling.
End-to-end checklist for USSAV integration
- Make a GET request to /api/v1/port-congestion with port=USSAV and X-API-Key header.
- Verify success is true, then read data.results[0].
- Extract congestion, metrics.ratio_vs_baseline, and as_of.
- Cache the full JSON daily; avoid over-polling.
- Implement a decision rule on the ratio with hysteresis; log meta.request_id for traceability.
- Render the congestion band in UI and route the ratio to your alerting logic.
FAQ
Is this live AIS wait-time data?
No. It is an IMF PortWatch signal based on AIS-derived port calls, published daily with a ~6–7 day lag. It does not measure anchorage or berth waiting times.
How often should I refresh Savannah’s congestion?
Daily is appropriate. The underlying dataset is updated daily and lagged; polling more frequently yields little benefit. Cache and serve internally to minimize calls.
Which fields should I store?
At minimum: congestion, metrics.ratio_vs_baseline, as_of, and optionally recent_avg_portcalls_per_day and baseline_avg_portcalls_per_day for diagnostics and dashboards.
What happens without an API key?
Guests can call the endpoint up to 100 requests/day per IP. For sustained usage and production, include X-API-Key with your token.
Can I use this to quote freight rates today?
No. This endpoint provides a congestion indicator, not prices. Do not present any freight rate as “today’s price” based on this signal.
Spin up your Savannah congestion integration in minutes: get an API key and read the full endpoint reference. Register for access, then review the Documentation for field definitions and usage patterns.