You need reliable AIS yacht tracking you can ship this sprint: live positions, short history, marina proximity, and light-touch fleet oversight for charters or owners’ reps. By the end of this guide you’ll call a live yacht track, list nearby vessels around a marina, search by name/IMO/MMSI, and batch-monitor a small fleet—using one API key and a single base URL.
Why developers use this API for yacht tracking
vessels-api.com provides a focused REST interface for maritime data that works well for yachts and superyachts:
- 18 REST endpoints covering vessel search, live tracking, fleet operations, port intelligence, IMO CII emissions, and a premium near real-time AIS feed
- One API key in X-API-Key; one base URL; no OAuth; no per-endpoint auth differences
- Consistent JSON envelope on every response: {status, success, message, data}
- Global AIS coverage with near real-time refresh rates
- 7-day free trial on all plans; scales from indie apps to enterprise fleets
Base URL for all calls: https://vessels-api.com/api/V1
Authentication: X-API-Key: YOUR_API_KEY
Endpoints you’ll use most for yachts
- GET /vessels/track — live position, optional 168h history, active route, predicted ETA, and weather
- GET /vessels/nearby — vessels within a radius of a marina or anchorage
- GET /vessels/search — fuzzy name lookup, or by IMO/MMSI with filters
- POST /vessels/fleet — batch positions/routes for multiple yachts at once
- GET /ports/data and GET /port/expected-arrivals — port-level context for dockage windows and local activity
Everything returns a consistent, typed JSON envelope and standardized fields like speed_knots, course_degrees, and timestamp_utc (ISO 8601, UTC).
Live yacht tracking with /vessels/track
Use this endpoint when you need the current fix plus optional short history, the active route, and a predicted ETA. For yachts, that powers owner dashboards, trip playback, and basic anomaly detection.
Official live cURL example
The following sample is a documented live fixture you can copy to verify your integration pipeline and field names:
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
{
"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
}
}
What you’ll use:
- data.current_position.latitude/longitude (decimal degrees, WGS84)
- data.current_position.speed_knots (kn)
- data.current_position.course_degrees (0–359)
- data.current_position.timestamp_utc (ISO 8601, UTC) and age_minutes (staleness helper)
- data.route fields for simple trip cards (departure_port, destination_port, avg_speed_knots)
- data.last_port_visits to show recent history without scraping logs
Query params:
- imo or mmsi is required
- hours: history window, default 24, max 168 (7 days)
- include_route, include_predicted_eta, include_weather: add-on toggles
Python example: render a yacht detail card
import requests
from datetime import datetime
API_KEY = "YOUR_API_KEY"
MMSI = "258785000"
BASE = "https://vessels-api.com/api/V1"
r = requests.get(
f"{BASE}/vessels/track",
params={"mmsi": MMSI, "hours": 48, "include_predicted_eta": "true"},
headers={"X-API-Key": API_KEY},
timeout=20,
)
r.raise_for_status()
payload = r.json() # {status, success, message, data}
data = payload["data"]
v = data["vessel"]
pos = data["current_position"]
route = data.get("route") or {}
pred_eta = data.get("predicted_eta")
def iso(ts):
return datetime.fromisoformat(ts.replace("Z", "+00:00")).isoformat() if ts else None
card = {
"name": v.get("name") or f"MMSI {v['mmsi']}",
"imo": v.get("imo"),
"mmsi": v.get("mmsi"),
"lat": pos["latitude"],
"lon": pos["longitude"],
"speed_kn": pos["speed_knots"],
"cog": pos["course_degrees"],
"fix_time_utc": pos["timestamp_utc"],
"age_min": pos.get("age_minutes"),
"from_port": route.get("departure_port"),
"to_port": route.get("destination_port"),
"avg_spd_kn": route.get("avg_speed_knots"),
"eta_pred": pred_eta["eta"] if isinstance(pred_eta, dict) else None,
}
print(card)
Tip: check age_minutes to grey out stale fixes and surface a “No recent AIS” state in your UI.
Show all yachts near a marina or anchorage with /vessels/nearby
For tender rendezvous, chase-boat ops, or marina awareness, query by lat/lon and a radius in nautical miles. You can filter by ship_type if you want to focus your view (e.g., pleasure craft classifications when available).
Response fields to expect in data.vessels:
- imo, mmsi, name, ship_type
- position.latitude, position.longitude, position.timestamp_utc
- distance_nm (from your query center), speed_knots, course_degrees, navigational_status
Usage notes:
- radius is in nautical miles (default 50, max 200)
- limit defaults to 50; increase if your marina sits near a busy TSS
- Cache by quantizing the center to a grid and reusing results for short refresh windows
Find a yacht by name, IMO, or MMSI with /vessels/search
Whether your user types “Attessa” or pastes an MMSI, search returns a normalized list with dimensions, tonnage, and build year—handy for capabilities displays and LOA-aware slip matching.
Key details:
- query matches name (fuzzy), IMO, or MMSI
- Optional filters: ship_type, flag, min_dwt/max_dwt, min_teu/max_teu, year_built_from/year_built_to
- Pagination via page and per_page (max 100); response includes pagination.current_page, per_page, total, last_page
Fields of interest in data.vessels[]: name, imo, mmsi, flag, vessel_type, length_m, width_m, gross_tonnage, deadweight_tonnage, year_built. For yacht UX, length_m and year_built help you set expectations on onboard features and approach constraints.
Track a charter roster with one POST: /vessels/fleet
If you maintain 5–50 yachts for an owner or a charter house, consolidate polling with a single batch request that returns current positions and optional routes.
Response highlights:
- data.fleet: aggregate counts (total_vessels, vessels_at_sea, vessels_in_port) for at-a-glance status
- data.vessels[]: per-yacht blocks including imo, mmsi, name, position, route
Implementation tips:
- Maintain a canonical map keyed by mmsi or imo; merge delta updates by timestamp_utc
- Show an “In port” badge if speed_knots ≈ 0 and navigational_status indicates moored/anchored
- Poll this batch endpoint less frequently than high-priority individuals to optimize quota
Port context for arrivals, services, and turnarounds
Marinas and nearby commercial ports affect fuel bunkering windows and service availability. Two lightweight calls give you the snapshot you need in yacht ops planning:
- GET /ports/data?port=ARBUE — identifiers, coordinates, timezone, vessels_in_port, vessels_expected
- GET /port/expected-arrivals?port=ARBUE — list of expected arrivals with ETA and departure_port
Example:
Use cases for yachts:
- Pre-empt congestion near a service yard by watching vessels_expected and expected_arrivals
- Localize your onboard services directory with the port timezone
Data model, reliability, and errors
All responses return the same envelope: status, success, message, data. That makes error handling straight-forward in middleware and telemetry, regardless of endpoint.
- 200 OK: request succeeded; parse data
- 400/422: missing or invalid parameters, or out of range; check your query/body
- 401: invalid or missing X-API-Key
- 404: vessel or port not found
- 429: rate limit exceeded; back off and retry later
- 500: server error; implement retry with jitter
Units and time:
- Positions: latitude/longitude in decimal degrees (WGS84)
- Speed: knots
- Distances: nautical miles
- Timestamps: ISO 8601 in UTC (e.g., 2026-07-30T13:00:10+00:00)
Polling and caching patterns that work for yachts
Yacht AIS dynamics vary: in open water, fixes can be sparse; at marinas, noise increases. Practical guidance:
- High-priority single yacht: GET /vessels/track every 30–120 seconds; gate UI refresh by age_minutes and timestamp_utc
- Fleet roster: POST /vessels/fleet every 2–5 minutes; render aggregates immediately, detail on demand
- Marina radar: GET /vessels/nearby; cache responses for 30–60 seconds based on a quantized lat/lon key
- History playback: request hours=1–6 windows during scrubbing, up to hours=168 for week-in-review
ESG signal for superyachts: CII snapshots
For sustainability-aligned programs and port clearances, you can query indicative emissions via GET /vessels/green. It returns distance_nm, estimated_emissions (co2_tons, co2_per_nm), and a cii score/rating (A–E) for period windows like 24h/7d/30d/1y.
Present this as a simple badge in the yacht profile with the year and regulation_reference (IMO MEPC.339(76)).
JavaScript: lightweight browser fetch for a yacht position chip
async function loadYachtChip(mmsi) {
const url = new URL("https://vessels-api.com/api/V1/vessels/track");
url.searchParams.set("mmsi", mmsi);
url.searchParams.set("hours", "24");
const res = await fetch(url.toString(), {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json(); // {status, success, message, data}
const pos = json.data.current_position;
return {
lat: pos.latitude,
lon: pos.longitude,
kn: pos.speed_knots,
cog: pos.course_degrees,
ts: pos.timestamp_utc,
stale: pos.age_minutes !== null && pos.age_minutes > 30
};
}
// usage:
loadYachtChip("258785000").then(console.log).catch(console.error);
Security, environments, and tooling
- Send your key in the X-API-Key header only; never embed raw keys in client apps without a proxy
- All endpoints share the same base URL; only /vessels/fleet uses POST, everything else uses GET
- Use the machine-checkable catalog at the MCP to introspect schemas and speed local testing
Useful links:
Common yacht tracking builds
- Owner dashboard: card with current position, speed/course, last port visits, and a route summary
- Marina radar: map layer from /vessels/nearby filtered to recreational types; sort by distance_nm
- Charter control: /vessels/fleet aggregates at-sea vs in-port; alert on speed_knots spikes at anchor
- ESG badge: /vessels/green 30d window with cii.rating and co2_per_nm
FAQ
How fresh is the AIS data?
Coverage is global with near real-time refresh rates. Use timestamp_utc and age_minutes to evaluate staleness per vessel.
What’s the maximum history window in /vessels/track?
Use hours up to 168 (7 days). The default is 24.
Do I need different auth for different endpoints?
No. Send the same X-API-Key header to the single base URL for all endpoints. Only /vessels/fleet uses POST; all others use GET.
How do I page through search results?
Pass page and per_page (max 100). The response includes pagination.current_page, total, and last_page to drive your UI.
What’s the unit for radius in /vessels/nearby?
Nautical miles. The default is 50 NM; the maximum is 200 NM.
Next steps
Stand up a working yacht tracker in under an hour: copy the official track cURL above, wire up /vessels/nearby for your marina layer, and graduate to /vessels/fleet as your roster grows. Keep the consistent JSON envelope and UTC timestamps in mind to simplify parsing and caching. When you’re ready, start building with your key and the API reference.
Register your key, read the Documentation, and explore the MCP catalog to ship your yacht tracking features.



