Solutions · Marine Life API
Whale, dolphin, shark and turtle sightings
Recorded sightings of cetaceans, sharks and rays, and sea turtles by area, group and date, with the most-seen species. Get a webhook when new sightings are added near your route or operation.
- Vessel speed and route planning in whale areas
- Offshore wind and survey mitigation
- Research and conservation monitoring
# Sightings by bbox or point + radius_nm, group and since date curl "https://www.seasignals.com/v1/wildlife/sightings?lat=51.95&lon=3.9&radius_nm=50&group=cetacean" \ -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/wildlife/sightingsSightings by bbox or point + radius_nm, group and since date
Webhooks
Or let the events come to you
Subscribe a URL once and SeaSignals POSTs a signed JSON event whenever something changes. Checked daily. Locations are rounded to about 11 km to protect sensitive species.
EVENT
wildlife.sightingA new sighting was recorded inside your areaFilters
bbox | [west, south, east, north], or use lat, lon and radius_nm |
lat | |
lon | |
radius_nm | 1 to 1,000 nm |
groups | cetacean, shark_ray, turtle |
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": "wildlife", "events": [ "wildlife.sighting" ], "filters": { "bbox": [ 2.0, 51.0, 5.0, 53.0 ], "groups": [ "cetacean" ] } }'
{
"id": "evt_3kV9q2mXw8rT1yLp0aZc4nB7",
"type": "wildlife.sighting",
"created_at": "2026-10-29T09:00:12Z",
"webhook": "webhook:42",
"data": {
"type": "Feature",
"id": "occurrence:8a41c2",
"geometry": {
"type": "Point",
"coordinates": [
3.9,
52.0
]
},
"properties": {
"group": "cetacean",
"scientific_name": "Phocoena phocoena",
"vernacular_name": "Harbour porpoise",
"date": "2026-10-27"
}
}
}
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 Marine Life API free
The free trial includes every API and up to 5 webhooks, 1,000 requests a day and 14 days of history.