All the ships at sea,
instantaneously.
An open AIS network streamed in real-time over WebSockets.
- stations
- –
- vessels
- –
- Per Sec
- –
Real-time API
Subscribe to bounding boxes or vessels over WebSocket and get every message as it is heard, or fetch a GeoJSON snapshot of what is in a box right now, plus stations and live stats. API reference
Open source, open data
An open AIS network powered by MIT-license code, and data re-served under each source's own terms, with the source named on every event. GitHub
Free for personal use
Read without an account; a one-click token covers a boat, a dashboard, or a side project. Feeding earns more. See tiers and limits
Stream vessel data in your favorite language
Open a WebSocket, send one subscribe frame, and every message inside the box arrives as JSON with its source, station, raw sentence, and decoded fields. No token needed.
const ws = new WebSocket("wss://ais.openwaters.io/v1/stream");
ws.onopen = () =>
ws.send(JSON.stringify({
type: "subscribe",
bbox: [[59, 10, 60, 11]]
}));
ws.onmessage = ({ data }) => {
const ev = JSON.parse(data);
console.log(ev.mmsi, ev.msg_type, ev.lat, ev.lon);
};import asyncio, json, websockets
URL = "wss://ais.openwaters.io/v1/stream"
SUB = {"type": "subscribe", "bbox": [[59, 10, 60, 11]]}
async def main():
async with websockets.connect(URL) as ws:
await ws.send(json.dumps(SUB))
async for frame in ws:
ev = json.loads(frame)
print(ev["mmsi"], ev["msg_type"],
ev.get("lat"), ev.get("lon"))
asyncio.run(main())url := "wss://ais.openwaters.io/v1/stream"
c, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
c.WriteJSON(map[string]any{
"type": "subscribe",
"bbox": [][]float64{{59, 10, 60, 11}},
})
for {
var ev struct {
MMSI int `json:"mmsi"`
MsgType string `json:"msg_type"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
}
if err := c.ReadJSON(&ev); err != nil {
log.Fatal(err)
}
fmt.Println(ev.MMSI, ev.MsgType, ev.Lat, ev.Lon)
}use futures_util::{SinkExt, StreamExt};
use tokio_tungstenite::{connect_async, tungstenite::Message};
#[tokio::main]
async fn main() {
let url = "wss://ais.openwaters.io/v1/stream";
let (mut ws, _) = connect_async(url).await.unwrap();
let sub = r#"{"type":"subscribe","bbox":[[59,10,60,11]]}"#;
ws.send(Message::text(sub)).await.unwrap();
while let Some(Ok(msg)) = ws.next().await {
let Message::Text(frame) = msg else { continue };
let ev: serde_json::Value =
serde_json::from_str(&frame).unwrap();
println!("{} {} {} {}",
ev["mmsi"], ev["msg_type"], ev["lat"], ev["lon"]);
}
}# live stream
SUB='{"type":"subscribe","bbox":[[59,10,60,11]]}'
echo "$SUB" | websocat -n wss://ais.openwaters.io/v1/stream
# snapshot of what is in the box right now
curl "https://ais.openwaters.io/v1/vessels?bbox=59,10,60,11"Limits, but only to keep the lights on
Everything re-served here is open data or volunteer data its contributor chose to share. These limits exist to keep the service healthy and reward the people who feed it.
| AnonymousFor trying things out. | PersonalFor a boat, a hobby project, or a developer’s laptop. | FeederAwarded to a station that contributes data. | CommercialFor products, fleets, or those who need support. | |
|---|---|---|---|---|
| Cost | Free | Free | Free | Paid |
| Connections/IP Address | 2 | 2 | 5 | 10 or more |
| Messages/sec | 20 | 50 | 200 | Unlimited |
| Subscribed area | ~10°×10° | ~20°×20° | Unlimited | Unlimited |
| Vessels followed by MMSI | 10 | 50 | 200 | As agreed |
| Sending data | — | |||
| Raw NMEA feed | — | — | ||
| Get a token | Start feeding | Contact us |
Limits apply per token and per network address: at most 8 concurrent streams from one address, however many tokens are behind it. Over a per-second rate the stream thins and stays up; over a connection, area, or MMSI-list limit you are refused with a message that says so. Following vessels by MMSI is bounded by the list, not by area, so a world-wide MMSI subscription is allowed in every tier. Need more, or unsure which tier you are? hello@openwaters.io.
Put your receiver on the map.
Whatever your antenna hears is re-served to everyone, and deduplicated against every other station.
AIS-catcher
Authenticated HTTP, works behind any NAT. Get a token in your browser, add one flag.
AIS-catcher -H https://ais.openwaters.io/v1/receive USERPWD x:<token> GZIP on INTERVAL 15Signal K
One plugin, both directions: shares what your receiver hears and shows AIS traffic when it hears none. No account; it mints its own token.
npm install signalk-aiscastdocker-shipfeeder
Already feeding the aggregators from a container? Add one more UDP destination.
host ais.openwaters.io, port 10110Any NMEA forwarder
Plain !AIVDM sentences over UDP, no token. Your station is a keyed hash of your address, never the address itself.
AIS-catcher -u ais.openwaters.io 10110Watch your station arrive at GET /v1/stations. Named stations with higher limits, and anything else: hello@openwaters.io. Developers: CONTRIBUTING.md.
Coverage
Every event says which of these it came from.
| Source | Where | Freshness |
|---|---|---|
| Kystverket | Norwegian coast, 40–60 nm out | live |
| Fintraffic Digitraffic | Finnish waters | live |
| Volunteer receivers | wherever they are | live |
| AISHub aggregate | worldwide terrestrial, ~50k vessels | 1–6 min (their snapshot refreshes every ~5 min) |
| aisstream.io | worldwide, when it is up | live; frequently down |
License
Licensing is per source, and the aggregate is not relicensed: each event is re-served under the terms of the source it came from, which is why source is on every event and every vessel. If you display or redistribute the data, carry the source's attribution through.
| Source | License | What you must do |
|---|---|---|
| Kystverket | NLOD 2.0 | Credit: “Contains data under the Norwegian licence for Open Government data (NLOD) distributed by the Norwegian Coastal Administration.” NLOD is not sublicensable: you are bound by it directly. |
| Fintraffic Digitraffic | CC BY 4.0 | Credit: “Source: Fintraffic / digitraffic.fi, license CC 4.0 BY.” |
| Volunteer receivers | beta | Contributed for re-serving by this server; a written feeder agreement with an open license on the aggregate is the next stage. Credit “aiscast volunteer receivers” for now. |
| AISHub aggregate | AISHub membership | Their published terms grant “use” only. Treat as view-only: fine to display, do not build a product on these events alone. May be withdrawn; every event carries source: aishub. |
| aisstream.io | no published terms | Best effort, may disappear. source: aisstream. |
The server, viewer, and plugin are MIT. The full position on licensing, privacy, and how the project funds itself without relicensing data is in the repo's PLAN.md.