Your team needs to know which container ships are near a port, where they’re headed, and when they’ll berth—so you can allocate cranes, trucks, and yard slots without guesswork. By the end of this guide, you’ll be able to query live AIS for container ships around any port, pull a specific vessel’s recent track and route, surface expected arrivals with ETAs, and monitor port congestion—all via a single REST API and a single API key.
What you’ll build with Vessels API
Vessels API exposes 18 REST endpoints under one base URL and one API key. For a “container ship near a port” workflow, we’ll combine:
- GET /vessels/nearby — find ships within a radius of your port’s coordinates
- GET /vessels/track — retrieve the live position, historical track, route, and predicted ETA for a target vessel
- GET /port/expected-arrivals — list vessels en route to a port with ETA and origin
- GET /ports/congestion — snapshot and wait-time stats you can show in ops dashboards
- GET /vessels/analytics — voyage aggregates to support service-level and dwell-time KPIs
Every response uses the same JSON envelope: {status, success, message, data}. All examples use the X-API-Key header and the base URL https://vessels-api.com/api/V1.
Locate container ships near your port with /vessels/nearby
When ops pings you with “Which boxships are 20–30 NM out?”, start with latitude/longitude and a radius in nautical miles. You can also filter by ship_type for container vessels.
cURL
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
Example response (truncated)
{
"status": 200,
"success": true,
"message": "OK",
"data": {
"vessel": {
"imo": "9122556",
"mmsi": "258785000",
"name": null
},
"current_position": {
"latitude": 53.33708,
"longitude": 7.17993,
"position_precision": "precise",
"position_accuracy_nm": null,
"speed_knots": 0,
"course_degrees": 212,
"heading_degrees": null,
"navigational_status": 5,
"timestamp_utc": "2017-01-24T04:07:12+00:00",
"age_minutes": 5158040,
"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
}
}
Fields to use immediately:
- position.timestamp_utc is UTC ISO8601. Use as-is in dashboards and logs.
- distance_nm is already computed from your center point—no need to geodesic-calc in code.
- navigational_status is AIS nav status (0 underway, 5 moored/anchored, etc.).
Tip: limit defaults to 50, and radius caps at 200 NM. Combine with /port/expected-arrivals to confirm ETAs for those within 30 NM.
Drill into a specific container ship with /vessels/track
Once you’ve identified a target MMSI/IMO, pull its current position, up to 168 hours of history, active route, and predicted ETA. Below is the official track fixture you can copy-paste right now.
Official cURL (documented MMSI 258785000)
Official JSON (copy as reference)
What to wire into your UI:
- current_position: speed_knots, course_degrees, navigational_status, and timestamp_utc drive your live map pins and state badges.
- route: departure_port, destination_port, and avg_speed_knots are adequate for a simple voyage ribbon.
- last_port_visits: list of recent calls—handy for compliance checks and cycle-time analytics.
Note: hours defaults to 24 and maxes at 168. If you need just the present fix, omit hours to keep payloads lean.
JavaScript fetch example
async function getVesselTrack(mmsi) {
const url = `https://vessels-api.com/api/V1/vessels/track?mmsi=${encodeURIComponent(mmsi)}&hours=48`;
const res = await fetch(url, {
headers: { "X-API-Key": "YOUR_API_KEY" }
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
const { data } = json;
const fix = data.current_position;
return {
mmsi: data.vessel.mmsi,
lat: fix.latitude,
lon: fix.longitude,
speed: fix.speed_knots,
course: fix.course_degrees,
status: fix.navigational_status,
timestampUtc: fix.timestamp_utc,
route: data.route
};
}
// Example call (uses the documented MMSI)
getVesselTrack("258785000")
.then(console.log)
.catch(console.error);
See who’s inbound with /port/expected-arrivals
Before a container ship shows up in your 30 NM ring, it may already be on the port’s official expected list. This endpoint gives you a list of inbound vessels with ETA and origin port.
cURL
Example response (truncated)
Practical notes:
- All timestamps are UTC ISO8601.
- Use this list to pre-allocate berth windows and to alert drayage ahead of time.
- Join with /vessels/track by mmsi to refine last-mile ETA once vessels are within 50 NM.
Quantify port load with /ports/congestion
To contextualize “nearby container ships,” show current congestion and wait-time metrics. You’ll get a snapshot of vessels in anchorage vs. at berth, plus recent wait-time statistics.
cURL
Returned fields include:
- snapshot.vessels_in_anchorage and snapshot.vessels_at_berth
- statistics.avg_wait_time_hours_last_7d and max_wait_time_hours_last_7d
- statistics.avg_berth_time_hours_last_7d and port_calls_count
UI tip: color-code inbound ships by the congestion snapshot to signal realistic landside ETAs to TMS and yard systems.
Roll up voyage performance with /vessels/analytics
Ops leads often want aggregates: “What’s the average speed and port call count for this container ship over the last 30 days?” Use type=vessel with either mmsi or imo.
cURL
Example response (truncated)
These aggregates are ideal for SLA dashboards or monthly service reviews. Pair them with live route data to understand whether delays are sea leg vs. port dwell.
End-to-end flow: from “nearby” to ETA
- Call /vessels/nearby with your port’s coordinates and a 30 NM radius, filtering ship_type=Container.
- For each candidate, display distance_nm, speed_knots, and navigational_status to quickly triage who’s actually inbound.
- Call /vessels/track for high-priority MMSIs to fetch route.destination_port and current_position. If include_predicted_eta is supported in your workflow, enable it to show predicted_eta when available.
- Overlay /port/expected-arrivals to validate schedule ETAs. If a ship is listed but remains at anchorage, surface /ports/congestion statistics to explain the holdup.
- Use /vessels/analytics for weekly rollups to gauge how often your “nearby” alerts translate to on-time berths.
Python example: “Container ships within 30 NM, with live fix and ETA”
import requests
from datetime import datetime
API_KEY = "YOUR_API_KEY"
BASE = "https://vessels-api.com/api/V1"
HEADERS = {"X-API-Key": API_KEY}
def get_nearby_containers(lat, lon, radius_nm=30, limit=25):
url = f"{BASE}/vessels/nearby"
params = {
"latitude": lat,
"longitude": lon,
"radius": radius_nm,
"ship_type": "Container",
"limit": limit
}
r = requests.get(url, headers=HEADERS, params=params, timeout=20)
r.raise_for_status()
return r.json()["data"]["vessels"]
def get_track(mmsi):
url = f"{BASE}/vessels/track"
params = {"mmsi": mmsi, "hours": 24}
r = requests.get(url, headers=HEADERS, params=params, timeout=20)
r.raise_for_status()
return r.json()["data"]
def iso_to_dt(zulu):
return datetime.fromisoformat(zulu.replace("Z", "+00:00"))
def main():
# Example: coordinates near a major container port
lat, lon = 1.265, 103.82
ships = get_nearby_containers(lat, lon)
enriched = []
for v in ships:
track = get_track(v["mmsi"])
fix = track["current_position"]
route = track.get("route") or {}
enriched.append({
"name": v.get("name"),
"mmsi": v["mmsi"],
"distance_nm": v["distance_nm"],
"speed_knots": v["speed_knots"],
"status": v["navigational_status"],
"timestamp_utc": fix["timestamp_utc"],
"dest": route.get("destination_port"),
"eta": fix.get("eta") or (track.get("predicted_eta") if track.get("predicted_eta") else None)
})
# Sort by distance then speed
enriched.sort(key=lambda x: (x["distance_nm"], -(x["speed_knots"] or 0)))
for row in enriched[:10]:
print(row)
if __name__ == "__main__":
main()
Notes:
- All time values are UTC ISO8601; parse accordingly.
- The API returns consistent envelopes; always index into ["data"].
- When predicted_eta is null, fall back to current_position.eta if present, or compute based on distance/speed.
Operational caveats that will save you time
- Authentication: always send X-API-Key. There’s no OAuth or per-endpoint auth differences.
- Units: distances are nautical miles; speeds are knots; coordinates are decimal degrees (WGS84).
- Refresh: coverage is global AIS with near real-time refresh rates. Cache route/analytics data longer than raw positions.
- Pagination: /vessels/search supports page and per_page (max 100). /vessels/nearby returns total and can be trimmed with limit.
- Status codes: handle 400/401/404/422/429/500. For 429, back off and retry with jitter.
- Consistency: every endpoint returns {status, success, message, data}, which simplifies error handling in shared middleware.
Why developers ship faster with Vessels API
- One base URL and one API key across 18 endpoints (vessel search, tracking, fleet ops, port intelligence, ESG/CII, and a premium real-time AIS feed).
- Consistent JSON envelope across all responses, easy to type once and reuse in clients and SDKs.
- 7-day free trial across plans; scales from indie tools to enterprise fleet platforms.
- Targets the teams that need it: developers, logistics startups, fleet managers, port operators, and ESG/compliance teams.
Explore the interactive reference in the Documentation, and try the model control plane via the MCP for testing and orchestration.
Extended examples you might need next
Search by vessel name or IMO, then track
# Find a container ship by fuzzy name
curl -H "X-API-Key: YOUR_API_KEY" \
"https://vessels-api.com/api/V1/vessels/search?query=ever&ship_type=Container&per_page=25"
Response includes data.vessels with {imo, mmsi, name, flag, vessel_type, dimensions}. Pipe mmsi into /vessels/track as shown above.
Batch monitor a lane with /vessels/fleet
Use this to render a single “lane board” for 10–200 ships with fewer round trips and consistent payloads.
FAQ
Q: How do I authenticate calls?
A: Send your key in the X-API-Key header on every request. There’s no OAuth or per-endpoint auth variance.
Q: Which timestamps and units should I expect?
A: All timestamps are ISO8601 UTC. Distances are nautical miles, speeds are knots, and coordinates are decimal degrees.
Q: How do I throttle usage safely?
A: Implement exponential backoff on 429 responses and cache route/analytics data longer than raw position fixes to reduce load.
Q: Can I filter “nearby” to only container ships?
A: Yes—pass ship_type=Container. You can also set radius (default 50 NM, max 200) and limit the number of returned vessels.
Q: What response structure should my client expect?
A: A consistent envelope on every response: {status, success, message, data}. This makes generic error handling straightforward.
Get started
Build your “container ship near a port” workflow with the endpoints above. Use the official track fixture to wire your client quickly, then expand into expected arrivals and congestion snapshots. Create an account to start a 7-day trial: Register. Explore endpoints and payloads in the Documentation, and validate calls in the MCP.