You’re building a cruise operations dashboard and need reliable AIS positions, ETAs, port calls, and fleet rollups without juggling multiple data sources. By the end of this guide you’ll be able to search for cruise ships, stream live positions with route and port histories, compute voyage analytics, monitor expected arrivals at cruise terminals, and surface emissions metrics—using one REST API with one key.
What matters for cruise ship tracking
Cruise applications have a unique mix of needs: accurate live position and course for passenger visibility, consistent ETAs to coordinate shore-side tours and provisioning, fast identification of ships near a port during peak embark/debark, and increasingly, emissions summaries for ESG disclosures.
- Live tracking with historical breadcrumbs for on-map replay.
- Expected arrivals and recent activity for cruise terminals.
- Fleet aggregation for operations teams (who is at sea vs. at berth).
- CII emissions scoring to support compliance reporting.
vessels-api.com exposes these capabilities across 18 REST endpoints powered by global AIS coverage, delivered with a single API key and a consistent response envelope: {status, success, message, data}.
API model and developer essentials
Base URL: https://vessels-api.com/api/V1. Authenticate every request with X-API-Key: YOUR_API_KEY. There are no per-endpoint auth differences and no OAuth flows. All endpoints return JSON with the same top-level envelope so you can centralize error handling.
- Methods: GET for most endpoints; POST for /vessels/fleet (batch fleet rollups).
- Units: positions in decimal degrees, speed in knots, distance in nautical miles, durations in hours, timestamps in UTC ISO 8601.
- Pagination: search endpoints accept page and per_page (max 100) to curate results.
- Coverage: near real-time AIS refresh rates across global waters.
Useful links to keep handy: Register, Documentation, and the MCP for model context and integration patterns.
Track a cruise ship in real time
Use GET /vessels/track to pull the current position, optional position history (up to 168 hours), active route, predicted ETA, port visits, and weather data for a single vessel. For cruise use cases, route and last_port_visits are critical for validating an itinerary, while current_position drives your consumer map.
Official cURL sample (documented MMSI 258785000):
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
Official JSON:
{
"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:18+00:00",
"age_minutes": 5139260,
"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 actually use:
- data.current_position.latitude/longitude: plot on a map; units are decimal degrees.
- data.current_position.speed_knots and course_degrees: show state and heading.
- data.current_position.timestamp_utc: normalize your cache invalidation; timestamps are UTC ISO 8601.
- data.route.departure_port, destination_port, avg_speed_knots: validate itinerary vs. schedule.
- data.last_port_visits: power a “Recent Ports” panel for passengers or ops.
Note: depending on AIS availability, predicted_eta and weather may be null; always null-check optional fields in your client.
Python: request live position and route for a cruise ship
This snippet calls the same endpoint, pulls the current position, and gracefully handles optional fields. You can adapt it to poll at a fixed interval and update your map layer.
import os
import requests
from datetime import datetime, timezone
API_KEY = os.getenv("VESSELS_API_KEY", "YOUR_API_KEY")
BASE_URL = "https://vessels-api.com/api/V1"
def get_track(mmsi: str, hours: int = 48):
url = f"{BASE_URL}/vessels/track"
params = {"mmsi": mmsi, "hours": hours}
headers = {"X-API-Key": API_KEY}
r = requests.get(url, headers=headers, params=params, timeout=20)
r.raise_for_status()
payload = r.json()
if not payload.get("success"):
raise RuntimeError(payload.get("message", "Unknown error"))
return payload["data"]
def format_track(data: dict):
vessel = data.get("vessel", {})
pos = data.get("current_position", {}) or {}
route = data.get("route", {}) or {}
# Parse timestamp if present
ts_str = pos.get("timestamp_utc")
ts = None
if ts_str:
ts = datetime.fromisoformat(ts_str.replace("Z", "+00:00")).astimezone(timezone.utc)
return {
"imo": vessel.get("imo"),
"mmsi": vessel.get("mmsi"),
"lat": pos.get("latitude"),
"lon": pos.get("longitude"),
"speed_kn": pos.get("speed_knots"),
"course_deg": pos.get("course_degrees"),
"timestamp_utc": ts.isoformat() if ts else None,
"departure_port": route.get("departure_port"),
"destination_port": route.get("destination_port"),
"avg_speed_knots": route.get("avg_speed_knots"),
}
if __name__ == "__main__":
data = get_track("258785000", hours=48)
summary = format_track(data)
print(summary)
Find cruise ships by name, MMSI, or IMO
Before tracking, you often need to resolve a fuzzy name to a unique MMSI/IMO. Use GET /vessels/search with query, and optionally filter by ship_type, flag, tonnage, or build year. For cruise, filtering by ship_type can cut noise when your users search for popular names reused across fleets.
cURL sample:
Key parameters you’ll lean on:
- query: accepts vessel name (fuzzy), IMO, or MMSI.
- ship_type: narrow to “Passenger” (cruise), ferries, or other classes relevant to your UI.
- page/per_page: paginate to 100 results per page for autocomplete experiences.
The data.vessels array includes identifiers and particulars (imo, mmsi, name, flag, vessel_type, gross_tonnage, deadweight_tonnage, year_built, length_m, width_m). Use these for detail panels and to construct follow-on calls to /vessels/track or /vessels/analytics.
Build a live cruise fleet dashboard
Operations teams need a single snapshot: fleet totals, how many ships are at sea or in port, and current position/route for each. Instead of looping over individual vessels, call POST /vessels/fleet once with a batch.
POST body includes a vessels array (each with imo or mmsi) and flags to include_positions/routes. This is ideal for a timed job feeding a real-time dashboard.
cURL sample:
What to render:
- data.fleet.vessels_at_sea vs. vessels_in_port: a single glance KPIs row.
- data.vessels[].position: plot all positions in one request roundtrip.
- data.vessels[].route: show current voyage on selection, reusing your single-ship UI.
For per-vessel performance metrics over a window, complement the fleet call with GET /vessels/analytics. It aggregates distance, average speed, and port calls across 24h, 7d, 30d, or 90d.
cURL sample:
Use data.statistics.total_distance_nm and avg_speed_knots to create a “last 7 days” panel for each ship. Port call counts and total_time_in_port_hours are helpful when forecasting turnaround windows across congested cruise terminals.
Coordinate with cruise terminals using expected arrivals
Cruise terminals care about the next 24–72 hours of arrivals for gangway planning, provisioning, and shore excursion scheduling. Use GET /port/expected-arrivals to build an arrivals board for a port. Pair this with /vessels/track by linking each expected arrival to a detail page showing current position and ETA.
cURL sample:
The response includes data.expected_arrivals with mmsi, imo, name, vessel_type, eta, and departure_port. For cruise-specific terminals with variable berthing windows, sorting by ETA and grouping by vessel_type allows mixed cruise/ferry boards without confusing your users.
To enrich port-level UI, GET /ports lists 248 ports with IDs and coordinates, and GET /ports/data returns live counts for a single port (vessels_in_port, vessels_expected). If you’re building a global map of cruise hubs, use /ports once at startup to populate an index of selectable terminals.
ESG and compliance: CII emissions for cruise ships
ESG/compliance teams increasingly require a quantified view of emissions. GET /vessels/green provides an IMO CII score with A–E rating (A best), aligned to IMO MEPC.339(76). For a cruise ship, correlate distance_nm and co2_per_nm across periods (24h, 7d, 30d, 1y) to flag itineraries that may underperform.
cURL sample:
Use data.cii.rating and data.cii.score for dashboards, and retain period to clearly label any report. These fields are stable across the API and benefit from the shared JSON envelope to streamline your error handling.
Nearby cruise ships around a destination
For event-driven apps (e.g., “Which cruise ships are within 30 NM of our port right now?”), GET /vessels/nearby returns vessels within a given radius from a lat/lon. Combine it with ship_type to limit to passenger vessels, ideal for high-traffic destinations during cruise season.
cURL sample:
The response includes data.vessels with distance_nm, position, speed_knots, and navigational_status—enough to build a proximity alert list and a quick “arriving soon” badge for your terminal UI.
Implementation details that save time
Polling, caching, and UI updates
- Use the current_position.timestamp_utc to decide whether to redraw map markers. If the timestamp hasn’t changed since last poll, skip UI updates.
- Cache /ports results (static catalog) and invalidate only on app restart or a daily cron.
- For /vessels/track with history, limit hours to the window your UI actually renders (e.g., 24h) to reduce payload size.
Error handling and envelopes
- Every response includes {status, success, message, data}. Check success and handle 4xx/5xx with the status code.
- Common codes: 400 invalid/missing parameter, 401 invalid or missing X-API-Key, 404 not found, 422 out-of-range, 429 rate limit exceeded, 500 server error.
- On 429, back off and retry with jitter. Maintain idempotent reads: GETs are safe to retry.
Pagination and filtering
- /vessels/search supports page and per_page up to 100. Always propagate pagination cursors in your UI component state.
- Use ship_type and flag filters up-front to cut payloads and accelerate end-user search.
Data consistency across endpoints
- Identifiers: prefer MMSI or IMO from /vessels/search and pass the same to /vessels/track, /vessels/analytics, and /vessels/green.
- Timezones: all timestamp fields are UTC; handle display-localization purely in your front-end.
- Routes: route fields are consistent between single-vessel and fleet batch, so you can reuse renderers.
Putting it together: a minimal cruise tracking flow
- Search the ship: call /vessels/search with the cruise brand ship name, filter ship_type=Passenger to reduce duplicates. Store the returned mmsi and imo.
- Track it live: poll /vessels/track every N seconds. Render lat/lon, speed_knots, course_degrees, and route. Null-check predicted_eta; if null, show a fallback ETA based on scheduled arrival.
- Show itinerary context: render last_port_visits and route.departure_port/destination_port in your voyage sidebar.
- Build a fleet overview: call /vessels/fleet with your cruise line’s vessels to list at-sea vs. in-port, with quick links to the single-ship page.
- Feed terminal ops: for each homeport, show /port/expected-arrivals with links into the track view, so dock teams can validate real-time ETAs.
- ESG panel: surface /vessels/green for the same mmsi across 30d and 1y, capturing cii.rating and co2_per_nm as trend lines.
Port intelligence for cruise hubs
Beyond expected arrivals, you can snapshot congestion at specific ports, including vessels in anchorage vs. at berth and wait-time statistics, using GET /ports/congestion with a valid UNLOCODE (ARBUE for Buenos Aires, SGSIN for Singapore, NLRTM for Rotterdam). Keep this purely informational in your cruise terminal boards and avoid hard dependencies for operational decisioning if you don’t control berth allocation.
Complement that with GET /ports/data for a single port’s current vessel counts and timezone, which helps your UI localize schedules when paired with UTC timestamps from tracking endpoints.
Security and scalability notes
- Use a single API key across all endpoints via X-API-Key; do not embed in client-side code for public web apps—proxy requests via your backend.
- Batch wherever possible: /vessels/fleet reduces network roundtrips and aligns updates across ships to a single time slice.
- Be mindful of optional fields: weather, predicted_eta, and certain route metrics can be null. Your serializers should model these as optional to avoid crashes.
FAQ
How do I resolve a cruise ship when users type a partial or slightly misspelled name?
Use GET /vessels/search with the query parameter. The endpoint supports fuzzy matching on vessel names. Add ship_type=Passenger and paginate with per_page up to 100 for better UX.
What’s the recommended polling interval for live maps?
It depends on your UI and traffic, but a 30–60 second poll for /vessels/track is common. Use current_position.timestamp_utc to skip redraws if nothing changed since the last poll.
Can I get a fleet snapshot with one call?
Yes. POST /vessels/fleet with a list of {imo, mmsi} and include_positions true. The response also includes fleet totals (vessels_at_sea, vessels_in_port) for KPI cards.
How do I build a port arrivals board for cruise terminals?
Call GET /port/expected-arrivals with the port identifier (e.g., ARBUE). Render mmsi, name, vessel_type, eta, and departure_port, and link each row to /vessels/track for real-time verification.
Where do I get emissions and ratings for ESG dashboards?
Use GET /vessels/green with mmsi or imo and a period (24h|7d|30d|1y). The response includes distance_nm, estimated_emissions, and CII score/rating per IMO MEPC.339(76).
Ready to ship a cruise tracking experience your users can trust? Start with a single API key, consistent JSON across 18 endpoints, and global AIS coverage. Explore the Documentation, try calls in your environment, and connect operational teams, terminals, and ESG stakeholders to the same live data. When you’re set, Register and wire your app to the MCP for a clean, scalable integration path.


