Solutions · Port Delays API
Anchorage queues and overstays, port by port
See which ports are backed up right now: how many vessels are waiting at anchor, how long they've waited, and which ships are staying far longer than usual. Get a webhook when a port's delays start or clear.
- Re-routing and slow steaming decisions
- Supply-chain risk dashboards
- Freight and commodity market signals
# Every covered port's delay level, worst first; filter with level curl "https://seasignals.com/v1/delays?level=moderate" \ -H "Authorization: Bearer $SS_KEY"
Endpoints
Query it whenever you need it
GeoJSON responses with typed IDs, ISO 8601 times and units in every field name. Errors come back as {"error": {"code", "message"}}.
GET
/v1/delaysEvery covered port's delay level, worst first; filter with levelGET
/v1/delays/{locode}One port, with the waiting and overstaying vesselsGET
/v1/ports/{locode}/congestionQueue, dwell and anchorage counts over time
Webhooks
Or let the events come to you
Subscribe a URL once and SeaSignals POSTs a signed JSON event whenever something changes. Re-evaluated every 10 minutes. Moderate from 3 vessels waiting or a 6 h median wait; severe from 10 or 24 h.
EVENT
delay.startedA watched port reached your level (moderate or severe)EVENT
delay.clearedIt dropped back below itFilters
ports | UN/LOCODEs; all covered ports when left out |
min_level | moderate (default) or severe |
curl -X POST https://seasignals.com/v1/webhooks \ -H "Authorization: Bearer $SS_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/hooks/seasignals", "product": "delays", "events": [ "delay.started", "delay.cleared" ], "filters": { "ports": [ "NLRTM", "SGSIN" ], "min_level": "severe" } }'
{
"id": "evt_3kV9q2mXw8rT1yLp0aZc4nB7",
"type": "delay.started",
"created_at": "2026-10-29T09:00:12Z",
"webhook": "webhook:42",
"data": {
"port": "port:NLRTM",
"name": "Rotterdam",
"level": "severe",
"waiting": 12,
"median_wait_hours": 26.4,
"max_wait_hours": 51.0,
"in_port": 64,
"overstaying": 3,
"typical_stay_hours": 21.5,
"updated_at": "2026-10-29T09:00:00Z"
}
}
Delivery
Signed, retried, logged
- Signed: every request carries
SeaSignals-Signature, an HMAC-SHA256 of the timestamp and body with your webhook secret. - Retried: anything but a 2xx within 10 seconds is retried after 1 min, 5 min, 30 min, 2 h and 6 h.
- Logged:
GET /v1/webhooks/{id}shows recent deliveries, andPOST /v1/webhooks/{id}/testsends a test event. - Safe to retry: use the event
idto ignore a delivery you've already handled.
// header: SeaSignals-Signature: t=1761729600,v1=5f2b… const [t, v1] = header.split(",").map((p) => p.split("=")[1]); const expected = crypto.createHmac("sha256", process.env.SS_WEBHOOK_SECRET) .update(`${t}.${rawBody}`).digest("hex"); const ok = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)) && Date.now() / 1000 - Number(t) < 300;
import hashlib, hmac, os, time t, v1 = (part.split("=")[1] for part in header.split(",")) expected = hmac.new(os.environ["SS_WEBHOOK_SECRET"].encode(), f"{t}.{raw_body}".encode(), hashlib.sha256).hexdigest() ok = hmac.compare_digest(v1, expected) and time.time() - int(t) < 300
t, v1 = header.split(",").map { _1.split("=").last } expected = OpenSSL::HMAC.hexdigest("SHA256", ENV["SS_WEBHOOK_SECRET"], "#{t}.#{raw_body}") ok = ActiveSupport::SecurityUtils.secure_compare(v1, expected) && Time.now.to_i - t.to_i < 300
Try the Port Delays API free
The free trial includes every API and up to 5 webhooks, 1,000 requests a day and 14 days of history.