Solutions · Vessels API
Live ship positions, tracks and area alerts
Query every vessel reporting in an area, look one up by MMSI, replay its track with the sea state along the way, and get a webhook the moment a ship enters or leaves the waters you care about.
- Terminal and pilot planning
- Security and exclusion-zone monitoring
- Offshore asset proximity alerts
# Vessels by bbox or point + radius_nm, filtered by type, status or name q curl "https://www.seasignals.com/v1/vessels?bbox=3.5,51.5,4.5,52.2&type=cargo" \ -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/vesselsVessels by bbox or point + radius_nm, filtered by type, status or name qGET
/v1/vessels/{mmsi}One vessel: identity, position, speed, course, destination and current portGET
/v1/vessels/{mmsi}/trackTrack between from and to, with join=waves for sea state along the way
Webhooks
Or let the events come to you
Subscribe a URL once and SeaSignals POSTs a signed JSON event whenever something changes. Checked every 5 minutes.
EVENT
vessel.entered_areaA vessel matching your filters started reporting inside your bboxEVENT
vessel.left_areaIt left the bbox, or stopped reporting for an hourFilters
bbox | Required. [west, south, east, north], at most 20° per side |
types | Vessel types, e.g. ["cargo", "tanker"] |
mmsi | Only these vessels |
curl -X POST https://www.seasignals.com/v1/webhooks \ -H "Authorization: Bearer $SS_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/hooks/seasignals", "product": "vessels", "events": [ "vessel.entered_area", "vessel.left_area" ], "filters": { "bbox": [ 3.5, 51.5, 4.5, 52.2 ], "types": [ "cargo", "tanker" ] } }'
{
"id": "evt_3kV9q2mXw8rT1yLp0aZc4nB7",
"type": "vessel.entered_area",
"created_at": "2026-10-29T09:00:12Z",
"webhook": "webhook:42",
"data": {
"type": "Feature",
"id": "vessel:mmsi:244123456",
"geometry": {
"type": "Point",
"coordinates": [
3.82,
51.97
]
},
"properties": {
"name": "NORDIC STAR",
"type": "cargo",
"sog_kn": 11.2,
"cog_deg": 84.0,
"destination": "NL RTM",
"position_time": "2026-10-29T08:41:12Z"
}
}
}
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 Vessels API free
The free trial includes every API and up to 5 webhooks, 1,000 requests a day and 14 days of history.