Your harbor ops team needs real-time tug positions, fast proximity alerts around terminals, and reliable ETAs for tow jobs. By the end of this guide, you’ll be able to integrate tugboat tracking and analytics into dispatch dashboards using the vessels-api.com REST API—covering live AIS positions, multi-vessel fleet snapshots, nearby queries around piers, and aggregated voyage stats that inform utilization and ESG reporting.
What makes tug tracking different (and how the API helps)
Tugboats work close to shore, pivot quickly between jobs, and often dwell near breakwaters, anchorages, or terminals. That mix of short legs, dense traffic, and frequent stops requires:
- Near real-time AIS positions and short refresh intervals.
- Fast, radius-based discovery near ports and terminals.
- Batch fleet calls that return positions and routes in one payload.
- Operational analytics tuned to short time windows (24h–7d) for utilization.
vessels-api.com delivers 18 REST endpoints with one API key and a single base URL. Every response has the same JSON envelope: {status, success, message, data}. Coverage is global and designed for production logistics stacks—startups and enterprise fleets alike.
Quick start: Base URL, auth, and response envelope
Base URL for all endpoints:
https://vessels-api.com/api/V1
Authentication is a single header on every request:
X-API-Key: YOUR_API_KEY
Responses share a consistent envelope so you can standardize parsing and error handling:
{
"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:31+00:00",
"age_minutes": 5100260,
"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": "2026-04-30T09:00:00+00:00",
"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
}
}
Common status codes you should handle in client code: 200 (OK), 400 (invalid parameter), 401 (auth), 404 (not found), 422 (out of range), 429 (rate limit), 500 (server error). Timestamps are UTC (ISO-8601). Distances are in nautical miles (nm); speed in knots.
Live tug positions with 24–168h history: /vessels/track
For dispatch and safety, start with live position and recent history. You can request by MMSI or IMO, ask for up to 168 hours of history, and include route and predicted ETA when available.
Official cURL (copy/paste)
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
Official JSON response
What you’ll actually use in a tug dashboard:
- data.current_position.latitude/longitude, speed_knots, course_degrees, navigational_status (AIS nav status code), timestamp_utc
- data.route for active tow jobs with departure/destination context (when available)
- data.last_port_visits for recent port touchpoints to infer job cycles
Tip: For short-haul tug ops, set hours=24 to minimize payload size and poll more frequently. For incident reviews, expand to hours=168.
Python example (poll and normalize)
import requests
from datetime import datetime, timezone
API_KEY = "YOUR_API_KEY"
BASE = "https://vessels-api.com/api/V1"
def get_tug_position(mmsi: str, hours: int = 24):
r = requests.get(
f"{BASE}/vessels/track",
headers={"X-API-Key": API_KEY},
params={"mmsi": mmsi, "hours": hours}
)
r.raise_for_status()
body = r.json()
if not body.get("success"):
raise RuntimeError(body.get("message", "API error"))
cp = body["data"]["current_position"]
# Normalize to a minimal dict for your map widget
return {
"mmsi": mmsi,
"lat": cp["latitude"],
"lon": cp["longitude"],
"speed_knots": cp["speed_knots"],
"course": cp["course_degrees"],
"status": cp["navigational_status"],
"timestamp_utc": cp["timestamp_utc"]
}
if __name__ == "__main__":
pos = get_tug_position("258785000", hours=48)
print(pos)
Fleet view for dispatch: /vessels/fleet (batch)
Dispatchers need a one-screen picture: all tug positions, who’s at sea vs in port, and current routes. Use the batch endpoint to avoid per-vessel fanout.
Request
What to expect
Response includes:
- data.fleet: total_vessels, vessels_at_sea, vessels_in_port
- data.vessels[]: each with imo, mmsi, name, position, route
Use vessels_at_sea to color-code panels. Use each vessel.position.timestamp_utc to gray out stale AIS.
JavaScript example (Node/Browser fetch)
async function getFleetSnapshot() {
const res = await fetch("https://vessels-api.com/api/V1/vessels/fleet", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
vessels: [{ imo: "9122556" }, { mmsi: "309374000" }],
include_positions: true,
include_routes: true
})
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json();
const { fleet, vessels } = body.data;
return {
counts: fleet,
markers: vessels.map(v => ({
id: v.mmsi || v.imo,
name: v.name,
lat: v.position?.latitude,
lon: v.position?.longitude,
status: v.position?.navigational_status,
eta: v.route?.eta
}))
};
}
getFleetSnapshot().then(console.log).catch(console.error);
Terminal proximity and safety: /vessels/nearby
For tow assignment and safety lanes, proximity is crucial. Query all vessels within a radius in nautical miles around a terminal’s coordinates. Filter by ship_type if you only want tugs in the overlay, or omit to display situational awareness for all traffic.
Request
Response fields you’ll use
- data.vessels[].position.latitude/longitude/timestamp_utc
- data.vessels[].distance_nm (sort by distance for “nearest tug” lists)
- data.vessels[].speed_knots, course_degrees, navigational_status
Tip: Use a 5–10 NM radius for large port complexes. For berth-level views, reduce to 1–2 NM. Limit is your friend to bound payloads on dense waterways.
Utilization and tow-cycle KPIs: /vessels/analytics
Short-haul operations benefit from short-window analytics. The analytics endpoint aggregates voyage statistics per vessel or across a fleet for a time window you pick.
Common tug queries
- Per-tug 24h activity for shift handover (type=vessel, period=24h).
- Fleet weekly utilization for ops reviews (type=fleet, period=7d with mmsi_list).
- Port-side activity summary when coordinating berth windows (type=port).
Example (single tug, 7 days)
Key fields to chart:
- statistics.total_distance_nm
- statistics.avg_speed_knots and max_speed_knots (watch for outliers due to AIS noise)
- statistics.port_calls_count and total_time_in_port_hours
- statistics.ports_visited for routing patterns
Port context for tow planning
Tugboats depend on port readiness. Two port endpoints complement your dispatch stack:
- /ports/congestion provides a snapshot of vessels in anchorage and at berth with wait-time stats; use it to anticipate job queues. Request with port_id (UNLOCODE) such as ARBUE, SGSIN, NLRTM.
- /port/activity surfaces recent arrivals and departures for event feeds that can trigger automatic tow assignments.
Requests
Combine congestion snapshots with /vessels/nearby to pick the nearest available tug when inbound tonnage spikes.
Search and data hygiene
Need to seed your tug roster or resolve identifiers? Use /vessels/search to find a tug by name, MMSI, or IMO and cache key particulars locally.
Request
What matters for rosters:
- data.vessels[].imo, mmsi, name, flag
- Dimensions: length_m, width_m (useful for berth compatibility rules)
- pagination fields to iterate through results (current_page, per_page, total, last_page)
Implementation notes that save time
- Units: distances in nautical miles; speeds in knots; timestamps in UTC ISO-8601.
- Polling: for dispatch maps, 15–60s refresh is common. Use hours=24 on /vessels/track to keep payloads small.
- Staleness: check current_position.timestamp_utc and any age_minutes field to gray out stale targets.
- Filtering: ship_type=Tug on /vessels/nearby returns only tugboats, but you can also fetch all traffic for situational awareness layers.
- Batching: prefer /vessels/fleet for multi-tug screens; it reduces request overhead and keeps snapshots consistent across vessels.
- Errors: handle 404 when a tug temporarily drops from AIS; retry backoff for 429.
ESG and compliance: /vessels/green (when relevant)
Some tug operators report on emissions as part of terminal or corporate ESG dashboards. The /vessels/green endpoint returns estimated emissions and an IMO CII rating over a selected period (24h to 1y). While short-haul tugs may not always fall under the same scrutiny as deep-sea vessels, having distance_nm, estimated_emissions.co2_tons, and cii.rating available can streamline unified fleet reporting.
Request
Putting it together: a minimal tug-dispatch flow
- Roster build: use /vessels/search with ship_type=Tug to confirm identifiers.
- Map: poll /vessels/fleet with include_positions to place markers, or /vessels/track per tug when you need deeper route fields.
- Proximity: call /vessels/nearby around terminals to rank nearest available tug by distance_nm and speed_knots.
- Ops awareness: query /ports/congestion for wait-time pressure and /port/activity for inbound events.
- KPIs: pull /vessels/analytics weekly to compute tow-cycles, berth time, and distances.
FAQ
How often are positions refreshed?
Global AIS is near real-time, but effective refresh on your side depends on your polling cadence. For dispatch UIs, 15–60 seconds is typical.
What timestamps and timezones does the API use?
All timestamps are ISO-8601 in UTC (e.g., 2026-04-30T09:00:00+00:00). Normalize to local timezones in your UI as needed.
Can I filter only tugboats?
Yes. Use ship_type=Tug on /vessels/search and /vessels/nearby. For fleets, store a tug list and call /vessels/fleet.
How do I detect stale AIS data?
Check current_position.timestamp_utc or age_minutes when present. If older than your threshold, mark the target as stale and avoid dispatching based solely on it.
What’s the response format across endpoints?
Every response uses the same JSON envelope: {status, success, message, data}. Parse consistently and branch on status codes for errors.
Next steps
Build your tug dispatch map and analytics in a day. Start with the official endpoints and examples above, then expand into port intelligence and ESG as needed. Create your key and explore the schemas here:




