You’re building a dashboard that needs to answer one urgent question: which offshore supply vessels (OSVs) are near a port right now, and what are they doing next? By the end of this guide, you’ll be able to search for an OSV, track it live with AIS, detect vessels nearby a port’s coordinates, and layer port congestion context—using a single REST API with one API key.
What you can build for offshore supply vessels near a port
OSVs cycle between offshore assets and nearby ports for bunkering, cargo, and crew changes. Your applications often need to:
- Detect all OSVs within a given radius of a port anchorage.
- Track a specific OSV’s live position and recent port calls.
- Estimate ETAs and current routes to plan quay resources.
- Understand port congestion signals to anticipate delays.
- Roll it up for a fleet view and, when needed, surface CII emissions context to ESG teams.
vessels-api.com provides 18 REST endpoints covering those use cases with consistent JSON envelopes and near real-time AIS updates. Authentication is a single header: X-API-Key.
API building blocks for OSVs near a port
Base URL for all endpoints: https://vessels-api.com/api/V1. Every response follows the same envelope: {status, success, message, data}. Authentication is always the same: X-API-Key: YOUR_API_KEY. All endpoints are GET, except /vessels/fleet which is POST.
For the “offshore supply vessel near a port” workflow, we’ll focus on:
- /vessels/search — find an OSV by name/IMO/MMSI, filter by type or flag.
- /vessels/track — live position, short history, route, and last port visits.
- /vessels/nearby — list of vessels around a port’s lat/lon in nautical miles.
- /ports/congestion — snapshot wait times and berth load at a named port.
- /vessels/analytics or /vessels/green — optional layers for KPIs or ESG.
Step 1: Identify the right OSV with vessels search
Start by resolving the OSV to an IMO or MMSI. OSV names can be similar, so a fuzzy search plus filters helps cut down ambiguity.
Search parameters that matter
- query: vessel name fragment, exact IMO, or MMSI.
- ship_type: filter to offshore support/supply segments as modeled in your app.
- flag, year_built_from/year_built_to: narrow down for compliance or operations.
- Pagination: page, per_page (max 100) for larger result sets.
Official cURL example (search by name and flag):
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
The response’s data.vessels[] contains core particulars like imo, mmsi, name, vessel_type, and dimensions. Persist imo and mmsi in your system; you’ll use either to call tracking and analytics endpoints.
Step 2: Live-track the OSV and read its port context
Tracking tells you if the OSV is really “near a port,” whether it’s underway or at anchor/berth, and what its latest port visits were. For offshore supply operations, this is your ground truth.
Official sample: Live track for documented MMSI 258785000
Copy and run this request as-is. It’s the authoritative sample for how tracking payloads look and where to find the fields you’ll likely map into your UI.
{
"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 to map in your UI:
- data.current_position.latitude/longitude — plot the marker on your map. Units: decimal degrees (WGS84). Timestamp is UTC in ISO 8601.
- data.current_position.speed_knots and course_degrees — color-code underway vs anchored (speed_knots ~0) for dispatchers.
- data.current_position.navigational_status — AIS integer (e.g., 5 = moored per common AIS coding) useful for “in port vs at sea” heuristics.
- data.route — departure/destination ports and average speed; show route badges on OSV cards.
- data.last_port_visits[] — port history for quick crew/logistics context. arrival_time/departure_time are UTC ISO 8601.
Tip: hours parameter can go up to 168 to fetch up to seven days of position history. Include include_route=true, include_predicted_eta=true, or include_weather=true to enrich the payload when needed.
Python: call the same tracking endpoint and extract near‑port context
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, # up to 168
# "include_route": "true",
# "include_predicted_eta": "true",
# "include_weather": "true",
}
headers = {"X-API-Key": API_KEY}
r = requests.get(url, params=params, headers=headers, timeout=30)
r.raise_for_status()
payload = r.json()
if not payload.get("success"):
raise RuntimeError(payload.get("message"))
data = payload["data"]
v = data["vessel"]
pos = data.get("current_position") or {}
route = data.get("route") or {}
visits = data.get("last_port_visits") or []
print(f"Tracked MMSI {v.get('mmsi')} (IMO {v.get('imo')})")
if pos:
ts = pos.get("timestamp_utc")
print(f"Lat/Lon: {pos.get('latitude')}, {pos.get('longitude')} at {ts} UTC")
print(f"Speed: {pos.get('speed_knots')} kn, Course: {pos.get('course_degrees')}°")
print(f"Nav status: {pos.get('navigational_status')}")
if route:
print(f"Voyage: {route.get('departure_port')} -> {route.get('destination_port')}")
print(f"Avg speed: {route.get('avg_speed_knots')} kn")
if visits:
last = visits[0]
print(f"Last port: {last.get('port_name')} (arrived {last.get('arrival_time')})")
This script reads standard fields and can be slotted into a backend job that updates your OSV tiles every minute. Cache per MMSI for a short TTL if you need to fan out to multiple displays.
Step 3: Detect every OSV near a port anchorage
To answer “Which offshore supply vessels are within X nautical miles of this port?”, use the nearby search centered on the port’s coordinates. For ports, you can fetch latitude/longitude from the port catalog and then call /vessels/nearby.
Get port coordinates
Call the ports catalog once and cache it locally:
The response provides data.ports[] with port_id, name, latitude, longitude, and timezone. Store the lat/lon for the port you care about; they’re your center point.
Find vessels around the port
Use the center lat/lon and select a radius in nautical miles (default 50, max 200). Optionally filter by ship_type and limit.
Key fields to consume:
- data.vessels[].position.latitude/longitude/timestamp_utc — place each AIS marker; timestamps are UTC ISO 8601.
- data.vessels[].distance_nm — precomputed distance from the center point in NM; sort ascending for “closest to port”.
- data.vessels[].ship_type and navigational_status — filter to OSV profiles and highlight “at anchor” vs “underway”.
In an OSV board, combine /vessels/nearby with targeted /vessels/track calls for the top N nearest vessels to enrich with route and last_port_visits.
Step 4: Add port congestion context
Even if an OSV is physically near the port, quay availability and anchorage load determine real-world turnaround. The congestion endpoint provides a snapshot and time-windowed stats.
Read these fields from data:
- snapshot.vessels_in_anchorage and snapshot.vessels_at_berth — current load indicators.
- statistics.avg_wait_time_hours_last_7d and max_wait_time_hours_last_7d — planning numbers for estimated port-side durations.
- statistics.port_calls_count — volume context for the selected period.
For dispatch planning, pair /vessels/nearby with this congestion snapshot to decide whether to route the OSV to a different port or hold offshore.
Optional: Roll up a fleet or surface CII
Batch fleet updates
If you manage multiple OSVs, use the fleet endpoint to fetch positions and routes in one round trip. It’s POST with a JSON body.
Use data.fleet for quick counts (vessels_at_sea, vessels_in_port), and data.vessels[] for each unit’s position and route snapshot.
ESG: CII scoring for compliance desks
When the OSV is selected, let compliance teams pull a quick CII status over a period (default 30d). Ratings follow IMO MEPC.339(76) with A to E scale.
Map data.cii.rating and data.estimated_emissions.co2_tons in your ESG tab. Keep the operational UI focused on AIS fields; surface CII on-demand.
Implementation notes that save time
- Authentication: always pass X-API-Key: YOUR_API_KEY. There’s no OAuth or per-endpoint auth difference.
- Units: distances and speeds are nautical miles and knots. Coordinates are decimal degrees. Timestamps are UTC ISO 8601.
- Response shape: every endpoint returns {status, success, message, data}. Check success before dereferencing data.
- Pagination: search endpoints expose data.pagination with current_page, per_page, total, last_page. Keep per_page ≤ 100.
- Nearby radius: radius is in NM (default 50, max 200). The endpoint returns distance_nm for each vessel relative to your center.
- Throttling: handle 429 gracefully; exponential backoff with jitter prevents thundering herds from multiple dashboards.
- Caching: cache /ports for hours or days. For live tracking, short TTLs (15–60 seconds) strike a good balance for OSV ops rooms.
- Error handling: 400 indicates invalid/missing parameter; 401 invalid/missing API key; 404 not found; 422 parameter out of range; 500 server error.
Putting it together for “OSV near a port”
Workflow outline
- Resolve your OSV(s): /vessels/search → store imo/mmsi and metadata.
- Get the port center: /ports (or /ports/data for a single port) → cache lat/lon.
- List nearby units: /vessels/nearby?latitude=...&longitude=...&radius=... → filter to OSV ship types and sort by distance_nm.
- Enrich top N units: /vessels/track?mmsi=...&hours=48 → read speed_knots, navigational_status, last_port_visits, route.
- Contextualize delays: /ports/congestion?port_id=...&period=7d → show anchorage/berth load and recent wait times.
- Optional: batch updates for many OSVs via /vessels/fleet; ESG panels via /vessels/green.
JavaScript quickstart for a map tile
This example fetches vessels near a port center and then hydrates the top result with a tracking call. Use it in a server environment or behind your own API proxy to keep the key off the client.
const fetch = (...args) => import('node-fetch').then(({default: f}) => f(...args));
const API_KEY = "YOUR_API_KEY";
const PORT_CENTER = { lat: -34.60, lon: -58.38 }; // example coords
async function getNearby(center, radiusNm = 30, limit = 10) {
const url = new URL("https://vessels-api.com/api/V1/vessels/nearby");
url.searchParams.set("latitude", center.lat);
url.searchParams.set("longitude", center.lon);
url.searchParams.set("radius", radiusNm);
url.searchParams.set("limit", limit);
const r = await fetch(url.toString(), { headers: { "X-API-Key": API_KEY } });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const json = await r.json();
if (!json.success) throw new Error(json.message);
return json.data.vessels || [];
}
async function getTrackByMmsi(mmsi) {
const url = new URL("https://vessels-api.com/api/V1/vessels/track");
url.searchParams.set("mmsi", mmsi);
url.searchParams.set("hours", 48);
const r = await fetch(url.toString(), { headers: { "X-API-Key": API_KEY } });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const json = await r.json();
if (!json.success) throw new Error(json.message);
return json.data;
}
(async () => {
const vessels = await getNearby(PORT_CENTER, 30, 10);
if (vessels.length === 0) {
console.log("No vessels nearby.");
return;
}
// Pick the closest candidate
const nearest = vessels.sort((a, b) => a.distance_nm - b.distance_nm)[0];
console.log(`Nearest MMSI: ${nearest.mmsi} at ${nearest.distance_nm} NM`);
const track = await getTrackByMmsi(nearest.mmsi);
const pos = track.current_position || {};
console.log(`Lat/Lon: ${pos.latitude},${pos.longitude}; Speed: ${pos.speed_knots} kn; Status: ${pos.navigational_status}`);
})();
Operational tips specific to OSVs
- Use navigational_status and speed_knots to distinguish “standby near field” from “at berth.” OSVs often hold position near port; velocity thresholds help.
- Plot last_port_visits with durations to diagnose turnaround anomalies after port calls.
- Leverage /vessels/analytics with type=vessel and a 7d or 30d period to track avg_speed_knots and port_calls_count for performance baselining.
- When dispatching tugs and quayside services, pair the OSV’s predicted ETA (when requested) with /ports/congestion to time assets efficiently.
- Batch critical OSVs via /vessels/fleet every refresh interval rather than calling /vessels/track N times; it reduces round trips.
Reference links
FAQ
How often does the AIS data refresh?
The API provides near real-time AIS updates. Use short cache TTLs (e.g., 15–60 seconds) for live dashboards and check the timestamp_utc and age_minutes fields in current_position to judge freshness per vessel.
What’s the best identifier to store for OSVs?
Store both imo and mmsi. MMSI is convenient for AIS tracking; IMO is persistent across radio equipment changes and works across endpoints that accept either.
Can I get all vessels for a port without lat/lon?
Yes. Use the legacy GET /vessel/port?port=PORT_ID for a quick list, or combine /ports/data with /vessels/nearby using the port’s coordinates for custom filters and distances in NM.
How do I interpret navigational_status?
It’s an AIS-coded integer. Combine it with speed_knots to create business rules: for example, status indicating moored or anchored with speed near zero usually means “in port” or “awaiting berth.”
What errors should I handle in production?
Expect 401 for missing/invalid X-API-Key, 404 when a vessel or port is not found, 422 for parameter bounds (e.g., radius > 200), and 429 when rate limits are exceeded. Always check success and message in the response envelope.
Build your “OSV near a port” feature now
Start with /vessels/nearby to find offshore supply vessels around your target port, then enrich the closest units with /vessels/track and layer in /ports/congestion for operational reality. The consistent response envelope and single API key simplify integration for web and backend services. Spin up your prototype today: read the Documentation, explore tools in the MCP, and Register to get your API key.