You have a bulk carrier to track, an ETA to defend, and stakeholders who need reliable positions, routes, and port calls without refreshing three dashboards. By the end of this article you’ll be able to query a bulk carrier by name or IMO/MMSI, stream its latest AIS position with up to 168 hours of history, summarize voyage analytics, and roll multiple bulkers into one fleet call — all via vessels-api.com with a single API key and a consistent JSON envelope.
Why bulk carrier tracking needs a focused API workflow
Bulk carriers move commodities on long-haul voyages, anchor at congested ports, and often operate in convoys with tight laycan windows. For these workloads you typically need:
- Fast vessel lookup that tolerates fuzzy names and filters on ship type and DWT.
- Live AIS positions with course, speed, and navigational status, plus route and ETA context.
- Batch fleet retrieval to avoid N sequential calls when monitoring a panel of bulkers.
- Voyage analytics to benchmark speed, distance sailed, and port-call activity.
- Port-side signals when planning arrivals and mitigating berth/anchorage wait times.
vessels-api.com provides 18 REST endpoints over a single base URL, secured by an X-API-Key header, with the same JSON envelope on every response: {status, success, message, data}. No OAuth flows, no per-endpoint auth differences, and coverage sourced from global AIS feeds.
Quickstart: base URL, authentication, and response envelope
All endpoints are under the same base:
- Base URL: https://vessels-api.com/api/V1
- Authentication: HTTP header X-API-Key: YOUR_API_KEY
- Methods: all GET except POST /vessels/fleet
- Envelope: every response includes status, success, message, data
Time fields are returned as UTC ISO 8601 strings. Distances are in nautical miles (nm). Speeds are in knots. Pagination (where applicable) is provided via data.pagination with current_page, per_page, total, and last_page.
1) Find the bulk carrier by name, IMO, or MMSI
Start by resolving the vessel identifiers you will use across the rest of the endpoints. For bulk carriers, you’ll usually want to filter by ship type and deadweight tonnage (DWT) to reduce ambiguity when the name is common (e.g., “Atlantic” anything).
Endpoint
GET /vessels/search
Example cURL
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
What you’ll use
- data.vessels[].imo, data.vessels[].mmsi: identifiers to pass into tracking/analytics.
- data.vessels[].vessel_type: verify the ship is indeed a bulk carrier.
- data.pagination: iterate over additional pages for common names.
Response snippet (illustrative)
{
"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:21+00:00",
"age_minutes": 5112560,
"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
}
}
Note: The response above follows the documented schema. Use the actual vessel name returned by the API; names can be null or vary by registry.
2) Live tracking with route, 48-hour history, and ETA
Once you have the MMSI or IMO, pull the current position and optional historical positions, route, and ETA. For bulk carriers, the combination of navigational status, course, and last port visits is critical for detecting whether a ship is drifting, anchored, or steaming under way.
Endpoint
GET /vessels/track — requires imo or mmsi. Optional hours (up to 168), include_route, include_predicted_eta, include_weather.
Official sample (copy/paste)
The following cURL and JSON are the official documented sample for a tracked vessel by MMSI 258785000 (keep exactly as shown).
Fields to drive a transportation workflow
- data.current_position.latitude/longitude: plot the ship on the map (WGS84). Units in decimal degrees.
- data.current_position.speed_knots, course_degrees, navigational_status: determine underway/anchored/drifting conditions. Status values are integer-coded.
- data.current_position.timestamp_utc: position time in UTC.
- data.route.departure_port, destination_port, eta: voyage context for dashboards and ETA exception rules.
- data.last_port_visits[]: recent port-call events for operations timelines.
Tip: include_route, include_predicted_eta, and include_weather are available as optional parameters on this endpoint. Omit them when you only need the core AIS position for low-latency polling.
Python example: read position, route, and last port calls
import requests
from datetime import datetime
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://vessels-api.com/api/V1"
def track_bulk_carrier(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=15)
r.raise_for_status()
payload = r.json()
if not payload.get("success"):
raise RuntimeError(f"API error: {payload.get('message')}")
data = payload["data"]
vessel = data.get("vessel", {})
pos = data.get("current_position", {}) or {}
route = data.get("route", {}) or {}
visits = data.get("last_port_visits", []) or []
# Parse useful fields
result = {
"imo": vessel.get("imo"),
"mmsi": vessel.get("mmsi"),
"name": vessel.get("name"),
"lat": pos.get("latitude"),
"lon": pos.get("longitude"),
"speed_knots": pos.get("speed_knots"),
"course_deg": pos.get("course_degrees"),
"navigational_status": pos.get("navigational_status"),
"position_time_utc": pos.get("timestamp_utc"),
"route_departure_port": route.get("departure_port"),
"route_destination_port": route.get("destination_port"),
"route_eta": route.get("eta"),
"last_ports": [
{
"port_name": v.get("port_name"),
"arrival_time": v.get("arrival_time"),
"departure_time": v.get("departure_time"),
} for v in visits
],
}
return result
if __name__ == "__main__":
track = track_bulk_carrier("258785000", hours=48)
print("Vessel IDs:", track["imo"], track["mmsi"])
print("Position:", track["lat"], track["lon"], "speed", track["speed_knots"], "kn")
print("Status:", track["navigational_status"], "Course:", track["course_deg"])
print("Route:", track["route_departure_port"], "→", track["route_destination_port"], "ETA", track["route_eta"])
for p in track["last_ports"]:
print("Recent port:", p["port_name"], "Arr:", p["arrival_time"], "Dep:", p["departure_time"])
3) Monitor multiple bulkers with one POST: /vessels/fleet
Transportation control towers often monitor a panel of bulk carriers across the Atlantic, Pacific, and Indian Ocean basins. Rather than serially call /vessels/track for each unit, use /vessels/fleet to return positions and routes for many vessels in a single round trip.
Endpoint
POST /vessels/fleet — Body: {"vessels":[{"imo":"..."},{"mmsi":"..."}], "include_positions": true, "include_routes": true}
Example cURL
What you’ll use
- data.fleet.vessels_at_sea vs. vessels_in_port: high-level operational status snapshot.
- data.vessels[].position: same shape as current_position in /vessels/track for mapping tiles.
- data.vessels[].route: departure, destination, and ETA to populate Gantt-like voyage timelines.
Response snippet (illustrative)
Tip: If you only need positions, set include_routes to false to reduce payload size and latency.
4) Voyage analytics for bulk carriers
To prioritize exceptions and measure performance across your bulk fleet, query aggregated voyage analytics. For a single vessel, analytics can show distance sailed and time in port — useful for turn-time benchmarking and COAs (contracts of affreightment) cadence.
Endpoint
GET /vessels/analytics — Required: type=vessel|port|fleet. For a single bulker, use type=vessel with mmsi or imo. Optional period=24h|7d|30d|90d.
Example cURL
Response snippet (illustrative)
Use total_distance_nm and avg_speed_knots to detect slow-steaming legs. Combine port_calls_count and total_time_in_port_hours to highlight congested nodes in your network.
5) Port-side intelligence for arrival planning
When your bulk carrier approaches a congested terminal, you’ll want both the ship’s ETA and the port’s expected-arrivals feed to avoid surprises at anchorage.
Expected arrivals at a port
GET /port/expected-arrivals — Required: port (port identifier).
You’ll receive data.expected_arrivals[] containing mmsi/imo, name, vessel_type, eta, and departure_port. Filter by vessel_type = Bulk Carrier to build a commodity-specific arrival board.
Port congestion snapshot
GET /ports/congestion — Required: port_id (UNLOCODE in parentheses, e.g., ARBUE, SGSIN, NLRTM). Optional: period=24h|3d|7d.
Use snapshot.vessels_in_anchorage vs. vessels_at_berth and statistics.* wait-time fields to understand operational pressure. Do not assume values outside the API; use the fields as returned by the endpoint.
Putting it together: a bulk carrier dashboard pipeline
Data flow
- Identify vessels: /vessels/search with ship_type=Bulk Carrier and name/IMO/MMSI filters.
- Live positions: /vessels/track (single) or /vessels/fleet (batch), with hours=24–48 for context.
- ETAs and routes: same /vessels/track payload, optionally include_predicted_eta=true.
- Voyage KPIs: /vessels/analytics with period=7d or 30d.
- Port-side context: /port/expected-arrivals and /ports/congestion for the destination port.
Polling strategy
- Positions: poll every 2–10 minutes depending on your control-tower latency tolerance. Use age_minutes to guard against stale updates.
- Routes/ETAs: poll less frequently (e.g., 15–30 minutes) unless a schedule-critical leg is in execution.
- Analytics: poll on longer windows (hourly/daily) and cache in your own store.
Data hygiene tips
- Null-safety: vessel names and heading_degrees can be null; check before rendering.
- Units: speeds in knots, distances in nautical miles, timestamps in UTC ISO 8601.
- Pagination: per_page max is 100 on search; walk pages via data.pagination.last_page.
- Ship identity: prefer IMO as a persistent key; MMSI can change with flag re-registration.
Error handling that avoids on-call pages
All endpoints return the same envelope, with HTTP codes for fast triage:
- 200: OK
- 400: Missing/invalid parameter
- 401: Invalid or missing X-API-Key
- 404: Vessel/port not found
- 422: Parameter out of range (e.g., hours above 168, per_page above 100)
- 429: Rate limit exceeded
- 500: Server error
Pattern: check HTTP status first, then payload.success and payload.message. For batch workflows, partially succeed by skipping only the failing entries rather than aborting the entire process.
ESG and compliance: IMO CII for bulkers
Bulk carriers are squarely in scope for IMO’s Carbon Intensity Indicator (CII) regulation. Use /vessels/green to surface emissions estimates and the CII rating in your dashboards.
Endpoint
GET /vessels/green — Required: imo or mmsi. Optional period=24h|7d|30d|1y (defaults to 30d).
Example cURL
Fields to display
- data.estimated_emissions.co2_tons and co2_per_nm: normalize emissions intensity across voyages.
- data.cii.rating: A to E, based on IMO MEPC.339(76), for straightforward policy gating.
Ports directory and single-port data
To transform a port code from the track payload into a map pin and timezone-aware ETA, use the ports catalog and single-port data endpoints:
- GET /ports — returns 248 ports with port_id, name, country, latitude, longitude, timezone.
- GET /ports/data?port=PORT_ID — returns vessels_in_port and vessels_expected counts, in addition to static attributes.
These endpoints are useful for enriching route.departure_port and route.destination_port from /vessels/track with coordinates and timezone for UI displays.
JavaScript example: expected arrivals filtered for bulk carriers
async function getExpectedBulkArrivals(portId) {
const url = `https://vessels-api.com/api/V1/port/expected-arrivals?port=${encodeURIComponent(portId)}`;
const res = await fetch(url, {
headers: { "X-API-Key": "YOUR_API_KEY" }
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const payload = await res.json();
if (!payload.success) throw new Error(payload.message);
const arrivals = payload.data.expected_arrivals || [];
return arrivals.filter(a => a.vessel_type === "Bulk Carrier")
.map(a => ({
mmsi: a.mmsi,
imo: a.imo,
name: a.name,
eta: a.eta,
from: a.departure_port
}));
}
// Example usage:
getExpectedBulkArrivals("ARBUE")
.then(rows => console.table(rows))
.catch(err => console.error(err));
Developer ergonomics: one key, one URL, consistent JSON
For transportation teams integrating bulk carrier data into planning and control systems, consistency matters more than anything. With vessels-api.com:
- One API key via the X-API-Key header.
- Same JSON envelope across vessel, fleet, and port endpoints.
- Global AIS coverage and near real-time refresh rates across endpoints used above.
- 7-day free trial applies across all plans to validate your pipeline end to end.
See the Documentation for parameter options like include_route or limit, and explore the MCP interface when testing interactions quickly from your browser.
Operational checklists for productionizing bulk tracking
- Backoff and retry: implement exponential backoff on 429 and short jittered retries on 500.
- Cache TTLs: cache static port metadata (GET /ports) for days; cache routes/ETAs for minutes.
- Staleness alarms: if current_position.age_minutes exceeds your SLO, alert and fall back to last known with a UI warning.
- Fleet batch windows: prefer /vessels/fleet to reduce spinning up N parallel HTTP connections.
- Security: keep API keys in server-side stores or CI/CD secrets, not in client bundles.
FAQ
Q: Can I track a bulk carrier by either IMO or MMSI?
A: Yes. /vessels/track accepts either imo or mmsi. Use IMO as a long-lived identifier; MMSI can change with reflagging.
Q: How much history can I request for a track?
A: Use the hours parameter on /vessels/track. Default is 24; maximum is 168.
Q: How do I query multiple bulkers without N requests?
A: Use POST /vessels/fleet with a body listing vessels by IMO or MMSI. You can include positions and routes in one response.
Q: What timezone are the timestamps in?
A: All timestamps are UTC, formatted as ISO 8601 strings.
Q: How do I get CII ratings for a bulk carrier?
A: Call /vessels/green with imo or mmsi. The response includes cii.score and rating (A to E) along with emissions estimates.
Next step
Ship your bulk-carrier control tower by wiring the endpoints above into your planning and visibility stack. Start with the official track request, add fleet batching, and layer analytics and port feeds for decision support. Get an API key and try it with your MMSIs: Register. Explore parameters and payloads in the Documentation and experiment in the MCP.




