You need reliable AIS-backed tracking for naval units, with code you can ship today. By the end of this guide, you’ll query, track, and monitor naval vessels in real time, build a proximity picture around points of interest, and roll up fleet-level insights—entirely via a consistent REST interface at a single base URL and a single API key.
Why this API fits naval tracking workflows
For operations centers, coastal surveillance, and mission support, you need predictable responses, low-latency AIS, and endpoints that compose into dashboards and alarms. This API provides:
- 18 REST endpoints covering search, live tracking, fleet operations, port intelligence, and IMO CII.
- One API key in the X-API-Key header across all endpoints—no OAuth or per-endpoint auth variance.
- Consistent response envelope on every call: {status, success, message, data}.
- Global AIS coverage with near real-time refresh characteristics.
- A scope that fits developers, startups, fleet managers, port operators, and ESG teams.
Base URL for everything is https://vessels-api.com/api/V1. Every endpoint below inherits that base and the same header: X-API-Key: YOUR_API_KEY.
Core building blocks for naval vessel tracking
The typical workflow for naval tracking includes four steps: identify the unit, lock onto its live track, generate situational awareness around an area, and aggregate multiple assets into an operational picture. We’ll use these endpoints:
- GET /vessels/search — Identify by name, IMO, or MMSI.
- GET /vessels/track — Live position, last 24–168 hours, route, ETA, and recent port calls.
- GET /vessels/nearby — Proximity picture around a coordinate.
- POST /vessels/fleet — Batch pull positions and routes for many assets.
- GET /vessels/analytics — Voyage statistics by vessel or fleet, e.g., total distance and port calls.
1) Identify a naval unit with /vessels/search
Search accepts a free-text query that matches name (fuzzy), IMO, or MMSI. You can layer filters like flag, ship_type, deadweight range, build year window, etc. Pagination is included, with per_page up to 100.
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
Response highlights:
{
"status": 200,
"success": true,
"message": "OK",
"data": {
"vessel": {
"imo": "9122556",
"mmsi": "258785000",
"name": null
},
"current_position": {
"latitude": 53.33708,
"longitude": 7.17993,
"speed_knots": 0,
"course_degrees": 212,
"heading_degrees": null,
"navigational_status": 5,
"timestamp_utc": "2017-01-24T04:07:06+00:00",
"age_minutes": 5134940,
"destination": null,
"eta": null
},
"predicted_eta": null,
"position_history": [],
"route": {
"departure_port": "HERACLIO",
"departure_time": "2026-04-27T21:35:36+00:00",
"destination_port": "HERACLIO",
"eta": null,
"distance_nm": null,
"avg_speed_knots": 17.1
},
"last_port_visits": [
{
"port_id": "156",
"port_name": "EMDEN",
"arrival_time": "2026-07-30T13:00:10+00:00",
"departure_time": null,
"duration_hours": null
},
{
"port_id": "10",
"port_name": "HERACLIO",
"arrival_time": "2026-04-20T12:00:00+00:00",
"departure_time": "2026-04-23T08:00:00+00:00",
"duration_hours": 68
}
],
"weather": null
}
}
Key fields:
- imo, mmsi — stable identifiers you’ll use in follow-on calls.
- vessel_type — useful for filtering naval/military classes in your UI.
- pagination — build “Load more” or infinite scroll without guessing totals.
2) Lock onto the live track with /vessels/track
Once you have an IMO or MMSI, use /vessels/track to retrieve current AIS position, optional history up to 168 hours, the active route, predicted ETA, and recent port visits (timestamps in UTC). The official sample below is for MMSI 258785000 and a 48-hour window. Copy the cURL and JSON exactly as shown when testing your parser.
How to use it:
- current_position.latitude/longitude — plot on your map; speed_knots, course_degrees inform motion vectors.
- timestamp_utc — always UTC; use to color-code “stale” positions. age_minutes quantifies staleness directly.
- route — departure/destination, ETA if present, and avg_speed_knots for transit estimates.
- last_port_visits — useful for operational history and anomaly detection.
Python: consume the live track and build alerts
import requests
from datetime import datetime, timezone
API_KEY = "YOUR_API_KEY"
MMSI = "258785000"
url = "https://vessels-api.com/api/V1/vessels/track"
params = {"mmsi": MMSI, "hours": 48}
headers = {"X-API-Key": API_KEY}
r = requests.get(url, params=params, headers=headers, timeout=20)
r.raise_for_status()
payload = r.json()
if not payload.get("success"):
raise RuntimeError(payload.get("message"))
data = payload["data"]
pos = data["current_position"]
vessel = data["vessel"]
lat = pos["latitude"]
lon = pos["longitude"]
speed = pos["speed_knots"]
course = pos["course_degrees"]
ts = pos["timestamp_utc"] # ISO-8601 UTC string
print(f"MMSI {vessel['mmsi']} @ {lat:.5f},{lon:.5f} | {speed} kn | COG {course} | {ts}")
# Example alert: recent position > 15 kn heading southwest-ish (180–270)
is_recent = pos.get("age_minutes") is not None and pos["age_minutes"] <= 10
heading_w_sector = course is not None and 180 <= course <= 270
if is_recent and speed is not None and speed > 15 and heading_w_sector:
print("ALERT: High-speed westbound movement within last 10 minutes")
# Route context
route = data.get("route") or {}
if route:
dep = route.get("departure_port")
dest = route.get("destination_port")
eta = route.get("eta")
print(f"Route: {dep} → {dest} | ETA {eta}")
3) Build a proximity picture with /vessels/nearby
To monitor naval activity around a choke point or exclusion zone, query vessels within a radius (nautical miles). You can tighten by ship_type and control the response size with limit.
Response snippet:
Use distance_nm to sort by nearest contact and position.timestamp_utc to prioritize recent updates.
4) Roll up an operational picture with /vessels/fleet
For task groups or patrol squadrons, fetch multiple tracks and routes in a single POST. This reduces round-trips and keeps your dashboard coherent at refresh time.
Response snippet:
Use the top-level fleet rollups for KPIs (assets at sea vs. in port) and the per-vessel objects to populate your map and lists.
5) Quantify movement with /vessels/analytics
Analytics summarizes voyage activity over 24h, 7d, 30d, or 90d windows. Switch modes via type=vessel|port|fleet. For a single naval unit by MMSI:
Response snippet:
These aggregates support readiness reports and trend charts without maintaining your own track cache.
Operational patterns for naval tracking
Fleet dashboard
- Use /vessels/search during setup to register each hull by IMO or MMSI.
- Refresh with /vessels/fleet every 15–60 seconds, depending on your display needs.
- Render position trails by calling /vessels/track with hours=1–6 for a compact breadcrumb.
- Compute mission KPIs from /vessels/analytics (distance, time at sea, port calls).
Area monitoring and deconfliction
- Poll /vessels/nearby for a patrol box; alert when new contacts enter radius or exceed speed thresholds.
- Classify contacts by ship_type and recent movement patterns (speed_knots, course_degrees).
Logistics ETA context
- Read route.destination_port and eta when present from /vessels/track to set offload or rendezvous windows.
- Fallback to avg_speed_knots and distance_nm for rough ETA where explicit eta is null.
Compliance and sustainability
For mixed fleets that include auxiliaries, use GET /vessels/green to fetch IMO CII estimates over 24h, 7d, 30d, or 1y. This can feed ESG dashboards alongside core operational data.
The response includes estimated_emissions.co2_tons and cii.rating (A–E), which you can map to badges or thresholds.
Implementation details that save time
- Authentication: Always send X-API-Key: YOUR_API_KEY. No OAuth flow is required.
- Envelope: Every response is {status, success, message, data}. Check success before using data.
- Timestamps: Fields such as timestamp_utc, departure_time, arrival_time, and eta are UTC ISO-8601.
- Units: Distances are nautical miles (nm); speed_knots is knots; headings and courses are degrees.
- Pagination: /vessels/search returns pagination with per_page up to 100. Keep page cursors in state.
- Parameter limits: /vessels/track hours defaults to 24 and max is 168. /vessels/nearby radius defaults to 50 and max is 200 NM.
- Error handling:
- 400 for missing/invalid parameters, 401 for missing/invalid API key.
- 404 if vessel or port not found; guard user input on IMO/MMSI/port codes.
- 422 for out-of-range parameters (e.g., hours or radius over max). Clamp client-side.
- 429 for rate limit exceeded; implement exponential backoff and jitter.
- 500 for server error; retry with backoff and circuit-breaker patterns.
- Batching: Prefer /vessels/fleet to reduce N calls to 1 for map refresh cycles.
- Caching: Cache stable lookups (e.g., /vessels/search and static particulars) longer than live positions. Keep live track TTL short (e.g., 15–30s) to avoid stale plots.
JavaScript example: fused situational picture
This Node.js snippet fetches a live track, creates a proximity ring around it, and prints a compact tactical summary. It uses the official MMSI fixture for consistency.
import fetch from "node-fetch";
const API_KEY = "YOUR_API_KEY";
const MMSI = "258785000";
const base = "https://vessels-api.com/api/V1";
async function getTrack(mmsi) {
const url = new URL(base + "/vessels/track");
url.searchParams.set("mmsi", mmsi);
url.searchParams.set("hours", "48");
const r = await fetch(url.toString(), { headers: { "X-API-Key": API_KEY }});
const j = await r.json();
if (!j.success) throw new Error(j.message);
return j.data;
}
async function getNearby(lat, lon, radiusNm = 20, limit = 50) {
const url = new URL(base + "/vessels/nearby");
url.searchParams.set("latitude", String(lat));
url.searchParams.set("longitude", String(lon));
url.searchParams.set("radius", String(radiusNm));
url.searchParams.set("limit", String(limit));
const r = await fetch(url.toString(), { headers: { "X-API-Key": API_KEY }});
const j = await r.json();
if (!j.success) throw new Error(j.message);
return j.data;
}
(async () => {
const track = await getTrack(MMSI);
const pos = track.current_position;
const lat = pos.latitude;
const lon = pos.longitude;
console.log(`PRIMARY ${MMSI} @ ${lat},${lon} | ${pos.speed_knots} kn | COG ${pos.course_degrees} | ${pos.timestamp_utc}`);
const nearby = await getNearby(lat, lon, 20, 25);
console.log(`Contacts within ${nearby.radius_nm}nm: ${nearby.total}`);
// Show top 5 nearest contacts
nearby.vessels
.slice(0, 5)
.forEach((v, i) => {
const p = v.position || {};
console.log(`#${i+1} ${v.mmsi || v.imo} ${v.name || ""} | ${v.distance_nm}nm | ${v.speed_knots} kn | COG ${v.course_degrees} | ${p.timestamp_utc}`);
});
})().catch(err => {
console.error("Error:", err.message);
});
Putting it together: a minimal naval ops dashboard
Start with a small Express or Flask service that refreshes /vessels/fleet every 20 seconds to render fleet pins and stats. When a user clicks a hull, load /vessels/track with hours=6 to draw the immediate breadcrumb. In parallel, call /vessels/nearby centered on the current position with radius=20–30nm to populate a contact list ranked by distance_nm and recency.
For reporting, schedule a job to call /vessels/analytics weekly with period=7d per MMSI. Persist the statistics block to your warehouse to power charts on distance sailed, time in port, and port_calls_count. If your fleet includes auxiliaries subject to reporting, run /vessels/green monthly with period=1y to track changes in CII rating over the year.
Resources
FAQ
What identifiers should I store for each naval unit?
Store both IMO and MMSI when available. Use MMSI for live AIS tracking and IMO for static particulars. The API accepts either in /vessels/track.
How fresh are timestamps?
timestamp_utc is UTC ISO-8601. age_minutes quantifies recency in minutes. Use both to set UI freshness states and alert thresholds.
How do I avoid hitting rate limits?
Batch with /vessels/fleet where possible, cache stable responses (e.g., /vessels/search), and back off on 429 with exponential retry. Clamp parameters to documented maxima (e.g., hours ≤ 168, radius ≤ 200).
Can I filter nearby results to only show military ships?
Use the ship_type filter on /vessels/nearby if you want to focus on specific types. Always handle mixed traffic gracefully in the UI.
What JSON shape can I expect across endpoints?
All endpoints return the same envelope: {status, success, message, data}. The data object then contains endpoint-specific structures like vessel, current_position, route, statistics, etc.
If you’re building a naval situational display, the endpoints above give you the essentials with minimal boilerplate. Get an API key, call the live track fixture to validate your parser, and ship your first map in an afternoon. Start here: Register and keep the Documentation and MCP open as you iterate.



