You need to reliably track a fishing vessel: confirm live position, see where it has been during the last day or week, understand its route and ETA back to port, and surface all of that into a dashboard or ops workflow. By the end of this guide, you’ll call a production-grade AIS API to: find a fishing vessel, fetch its live and historical positions, locate nearby targets in a fishing ground, batch-load fleet status, and compute voyage statistics you can ship straight into your app.
What you’ll build with Vessels API
Vessels API is a single REST API for maritime intelligence with 18 endpoints covering vessel search, live tracking, fleet operations, port intelligence, IMO CII emissions, and a premium real-time AIS feed. You use one API key, one base URL, and receive a consistent JSON envelope on every response: {status, success, message, data}. Coverage is global and refresh rates are near real time. A 7‑day free trial is available across plans and the API scales from indie tools to enterprise fleets.
- Base URL: https://vessels-api.com/api/V1
- Authentication: X-API-Key header on every request
- Response envelope: {"status", "success", "message", "data"}
In this post we’ll go deep on five endpoints that matter for fishing-vessel tracking:
- /vessels/search — find the target by name, IMO, or MMSI
- /vessels/track — live position + up to 168h history, route, predicted ETA, weather
- /vessels/nearby — detect vessels operating in the same fishing ground
- /vessels/fleet — batch-fetch fleet positions and routes
- /vessels/analytics — roll-up voyage KPIs over 24h to 90d
If you want to explore interactively, see the MCP. For full parameter lists and fields, check the Documentation. When you’re ready, Register for an API key and start calling the endpoints below.
Auth, formats, and practical details
All endpoints are under https://vessels-api.com/api/V1 and expect a header X-API-Key: YOUR_API_KEY. Timestamps are UTC ISO-8601. Distances are in nautical miles (nm). Speeds are in knots. When you fetch collections, pagination is available via page and per_page (up to 100). Cache live-track calls for 15–60 seconds in UI dashboards to avoid jitter and keep your rate usage efficient; cache search and analytics for minutes if your UX allows.
1) Find a fishing vessel with /vessels/search
Use fuzzy name search or direct identifiers (IMO or MMSI) to locate the vessel you need to track. You can filter by ship_type to focus on “Fishing”.
cURL: fuzzy name + flag filter
curl -H "X-API-Key: YOUR_API_KEY" "https://vessels-api.com/api/V1/vessels/track?mmsi=258785000&hours=48"
Useful filters: ship_type=Fishing, year_built_from/year_built_to, and pagination controls. The response includes a vessels array and pagination metadata.
JavaScript example (Node.js, fetch)
import fetch from "node-fetch";
const API_KEY = "YOUR_API_KEY";
const base = "https://vessels-api.com/api/V1";
async function findFishingVesselsByName(query) {
const url = new URL(`${base}/vessels/search`);
url.searchParams.set("query", query);
url.searchParams.set("ship_type", "Fishing");
url.searchParams.set("per_page", "25");
const res = await fetch(url.toString(), {
headers: { "X-API-Key": API_KEY }
});
if (!res.ok) {
throw new Error(`Search failed with ${res.status}`);
}
const payload = await res.json(); // { status, success, message, data }
const { vessels, pagination } = payload.data;
return { vessels, pagination };
}
findFishingVesselsByName("atlantic")
.then(({ vessels }) => {
for (const v of vessels) {
console.log(`Candidate: ${v.name} | IMO ${v.imo} | MMSI ${v.mmsi} | Type ${v.vessel_type}`);
}
})
.catch(console.error);
Key fields to take to the next step: mmsi or imo from each matched vessel. Those identifiers feed into /vessels/track.
2) Live tracking and history with /vessels/track
This endpoint returns the current position, up to 168 hours of history, the active route (departure, destination, ETA), and optionally weather. You can query by imo or mmsi and control the time window with hours (default 24, max 168). For fishing operations, 24–72 hours of history helps you visualize gear-setting patterns and revisit tracks near productive grounds.
OFFICIAL SAMPLE — copy and run as-is
The following cURL and JSON are the official sample fixture for a documented MMSI. Use it to validate your integration shape and field names.
{
"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:24+00:00",
"age_minutes": 5116160,
"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
}
}
Field highlights you’ll actually use in a fishing-ops workflow:
- data.vessel.{imo,mmsi,name}: stable identifiers for storage and display
- data.current_position.{latitude,longitude,speed_knots,course_degrees,navigational_status,timestamp_utc}: plot on the map, show speed and last-seen timestamp
- data.position_history: time series track (empty in the sample; in live calls you’ll receive points up to hours=168)
- data.route.{departure_port,destination_port,eta}: support landing-plans and cold-chain scheduling
- data.last_port_visits: create a quick audit of recent landings
Python example: tracking a fishing vessel
import os
import requests
from datetime import datetime, timezone
API_KEY = "YOUR_API_KEY"
BASE = "https://vessels-api.com/api/V1"
def track_by_mmsi(mmsi: str, hours: int = 48):
url = f"{BASE}/vessels/track"
params = {"mmsi": mmsi, "hours": str(hours)}
headers = {"X-API-Key": API_KEY}
r = requests.get(url, headers=headers, params=params, timeout=20)
r.raise_for_status()
payload = r.json() # {status, success, message, data}
d = payload["data"]
cur = d["current_position"]
lat, lon = cur["latitude"], cur["longitude"]
speed = cur["speed_knots"]
ts = cur["timestamp_utc"]
route = d.get("route") or {}
print(f"MMSI {mmsi} @ {ts} UTC | lat={lat:.5f} lon={lon:.5f} speed={speed} kn")
print(f"Route: {route.get('departure_port')} -> {route.get('destination_port')} ETA={route.get('eta')}")
# Render last 12 hours of history if available
hist = d.get("position_history", [])
recent = hist[-60:] if len(hist) > 60 else hist # throttle to 60 points for plotting
for p in recent:
print(p.get("timestamp_utc"), p.get("latitude"), p.get("longitude"))
if __name__ == "__main__":
track_by_mmsi("258785000", hours=48)
Implementation notes:
- Plot current_position immediately; stream position_history incrementally to a polyline on your map.
- Use timestamp_utc for recency warnings (e.g., stale if older than 60 minutes).
- If predicted_eta is present, prefer it for logistics ETAs; otherwise use route.eta.
3) See who’s fishing nearby with /vessels/nearby
When your skippers or analysts focus on specific grounds, a nearby scan helps quantify local activity and assess congestion and bycatch risks. This endpoint returns vessels within a radius (default 50 NM, max 200 NM) of a lat/lon. Filter by ship_type to only include Fishing.
cURL: scan 30 NM around a ground
Example response snippet
Use distance_nm to sort by proximity and timestamp_utc to discard stale hits. If you need to cap rendering load, use limit to trim the vessel list for front-end map layers.
4) Monitor a fishing fleet with /vessels/fleet (batch)
Fishing companies often manage mixed fleets: trawlers, longliners, tenders. The /vessels/fleet endpoint returns positions and routes for multiple targets in one POST. That’s ideal for a single dashboard refresh or a scheduled ETL that feeds your backend.
cURL: batch positions for two vessels
Response shape (snippet)
Use data.fleet to populate high-level counters. For lists, render each vessel’s position, speed, and ETA columns directly from the per-vessel fields.
5) Voyage KPIs for operations with /vessels/analytics
Operations teams need roll-ups: distance steamed over the last week, average speed, port calls, and time in port. /vessels/analytics provides these by mmsi or imo across periods: 24h, 7d, 30d, 90d.
cURL: weekly stats for a fishing vessel
Response snippet
These fields drop cleanly into summaries and compliance reports. For longline or trawl operations, avg_speed_knots can be an indicator of fishing versus steaming segments when paired with /vessels/track history.
Optional: ESG/Compliance for fishing fleets
If your buyers or corporate policies require emissions reporting, /vessels/green returns IMO CII-style estimates and a rating (A–E). It accepts imo or mmsi and a period like 30d or 1y.
Use co2_tons and co2_per_nm to normalize performance, and the cii.rating for compliance dashboards. Ratings follow IMO MEPC.339(76).
Build the fishing-vessel tracker UI
Map layer and state
- Fetch /vessels/track on a 30–60s cadence per tracked vessel.
- Render data.current_position as the icon point; color/opacity by recency.
- Draw a polyline from data.position_history; thin to 1 point per 2–5 minutes to reduce draw cost.
- If data.route is present, show an ETA badge; if predicted_eta exists, prefer it over route.eta.
Nearby context
- When the map center changes, call /vessels/nearby with a radius that fits your zoom level (e.g., 20–50 NM for shelf grounds).
- Filter by ship_type=Fishing to avoid clutter from merchant traffic.
Fleet dashboard
- Call /vessels/fleet hourly (or on demand) to fill tables and counters (at sea, in port, total).
- De-duplicate by mmsi to avoid accidental dual entries when you have both IMO and MMSI.
KPIs and reports
- Use /vessels/analytics for “last 7 days” and “month to date” widgets.
- Add /vessels/green as a separate ESG tab if you report emissions.
Error handling, limits, and safety
- HTTP 400/422 indicate missing/invalid or out-of-range parameters (e.g., radius > 200). Validate before sending.
- 401 indicates your X-API-Key is missing or invalid.
- 404 means the vessel or port is not found. For fishing calls, retry with the other identifier if you have both IMO and MMSI.
- 429 rate limit exceeded. Back off with exponential wait and cache responses where possible.
- 500 server error. Retry with jitter; log the request ID if included in headers (for your own observability).
End-to-end example: from search to live track (Node.js)
import fetch from "node-fetch";
const API_KEY = "YOUR_API_KEY";
const BASE = "https://vessels-api.com/api/V1";
async function searchFishingVessel(query) {
const url = new URL(`${BASE}/vessels/search`);
url.searchParams.set("query", query);
url.searchParams.set("ship_type", "Fishing");
url.searchParams.set("per_page", "5");
const res = await fetch(url, { headers: { "X-API-Key": API_KEY } });
if (!res.ok) throw new Error(`Search failed: ${res.status}`);
const payload = await res.json();
const candidates = payload.data.vessels || [];
return candidates.find(v => v.mmsi) || candidates[0];
}
async function trackVesselByMMSI(mmsi) {
const url = new URL(`${BASE}/vessels/track`);
url.searchParams.set("mmsi", mmsi);
url.searchParams.set("hours", "48");
const res = await fetch(url, { headers: { "X-API-Key": API_KEY } });
if (!res.ok) throw new Error(`Track failed: ${res.status}`);
const payload = await res.json();
const { vessel, current_position, route } = payload.data;
return { vessel, current_position, route };
}
(async () => {
const found = await searchFishingVessel("Atlantic");
if (!found) {
console.log("No fishing vessel found");
return;
}
console.log(`Tracking ${found.name} MMSI ${found.mmsi}`);
const { current_position, route } = await trackVesselByMMSI(found.mmsi);
console.log("Position:", current_position);
console.log("Route:", route);
})().catch(console.error);
Data modeling tips that save time
- Store both IMO and MMSI. Fishing vessels can change MMSI with equipment swaps; IMO (if assigned) is the stable identifier across the hull’s life.
- Normalize positions to a track table keyed by {mmsi, timestamp_utc}. Idempotent upserts make retries safe.
- Use navigational_status and speed_knots to infer “fishing” vs “transit” heuristics (e.g., trawling at 2–5 kn for extended periods). This is domain logic on your side; the raw fields are in /vessels/track.
- For front-end performance, clip position_history to the last 500–1000 points and decimate older points.
- If you display nearby vessels, hide entries with timestamp_utc older than your staleness threshold (e.g., > 2 hours) to keep the picture situationally accurate.
Port intelligence hooks (optional for fishing ops)
For landing planning, pair vessel tracking with port endpoints:
- /ports returns the catalog (identifiers, coordinates, timezone) for mapping and selection lists.
- /ports/data returns a single port’s live counts (vessels_in_port, vessels_expected).
- /port/activity gives arrivals/departures event feeds for alerting on landings.
Keep timestamps in UTC for storage; display in the port’s timezone when presenting to humans (the /ports response includes timezone).
Quality-of-life features you get “for free”
- One API key for everything: no OAuth flows or per-endpoint auth differences.
- Consistent JSON envelope: predictable parsing for retries and error surfaces.
- Global coverage and near real-time updates suitable for live dashboards.
FAQ
How often should I poll /vessels/track for a single fishing vessel?
For a live dashboard, 30–60 seconds is a good balance. For back-office systems, 2–5 minutes often suffices. Cache results in your app layer to mitigate bursts.
What’s the difference between route.eta and predicted_eta?
route.eta is part of the declared voyage data; predicted_eta (when present) is a model-based estimate. Prefer predicted_eta if you need a computed ETA; otherwise use route.eta.
How do I filter only fishing vessels?
Use ship_type=Fishing in /vessels/search and /vessels/nearby. The returned vessel_type/ship_type fields help you validate and display the classification.
Which identifier should I store for long-term tracking?
Store both. Use IMO as the primary key when available (it’s hull-stable) and MMSI for short-term AIS correlation and quick lookups.
What are the units and timezones?
Speeds are knots, distances are nautical miles, and all timestamps are UTC ISO-8601. Convert to local timezones for UI using the timezone field returned by the ports catalog where relevant.
Ship your fishing-vessel tracker
With /vessels/search, /vessels/track, /vessels/nearby, /vessels/fleet, and /vessels/analytics you have what you need to power a real fishing-ops dashboard: live positions, historical tracks, fleet snapshots, and weekly KPIs. Explore interactively in the MCP, read the full Documentation, and Register to start integrating today.




