VesselAPI Documentation
Welcome to VesselAPI. This API provides access to real-time and short-term historical AIS vessel tracking, port events, NAVTEX safety messages, global port data, and navigation infrastructure.
Base URL: https://api.vesselapi.com/v1
Quick Start
- Sign up for an API key at vesselapi.com
- Include your API key in the Authorization header
- Make requests to the endpoints
- Process the JSON responses
Developer Tools
Official SDKs and AI integration tools for working with the VesselAPI.
SDKs & MCP Server
Official client libraries for TypeScript, Python, and Go. MCP server for AI assistant integration with Claude, Cursor, and Windsurf.
API Categories
Documentation is organized by API category:
Vessel Tracking
Real-time AIS positions, vessel details, crew-reported ETA, and location-based queries. Track ships by MMSI, IMO, or geographic area.
Vessel Enrichment
Classification records, ownership, and technical specifications. Look up vessel details by IMO or MMSI.
Safety & Compliance
Marine casualty records and port state control inspections. Access safety event data and deficiency reports.
Vessel Emissions
Vessel emissions data including CO2 output, fuel consumption, and energy efficiency metrics by vessel or period.
Port Events
Track vessel arrivals and departures at ports worldwide. Query by port, vessel, or time range. Collected every 1 minute.
NAVTEX Messages
Access maritime safety information including weather warnings, navigation hazards, SAR operations, ice reports, and piracy alerts.
Ports Database
Global port database updated daily at 06:00 UTC. Search by name, UN/LOCODE, or geographic coordinates.
Navigation Infrastructure
Access data on maritime navigation aids including lighthouses, radio beacons, DGPS stations, and mobile offshore drilling units (MODUs). Updated weekly.
Notifications
Real-time webhook and WebSocket notifications for port arrivals, departures, ETA changes, speed changes, and geofence events.
Authentication
All API requests require authentication using an API key. Include your API key in the Authorization header of every request.
Header Format
Authorization: Bearer YOUR_API_KEY
Example Request
curl -X GET "https://api.vesselapi.com/v1/vessel/123456789?filter.idType=mmsi" \
-H "Authorization: Bearer YOUR_API_KEY"
Rate Limits & Quotas
VesselAPI enforces several limits to keep response times reliable across all customers. Exceeding any short-window rate limit returns HTTP 429 with a Retry-After header, and the block lifts automatically as your request rate drops back under the threshold. Clients that keep pushing through repeated 429s instead of backing off will trip a separate, longer-duration block — see Sustained-abuse blocks below.
Short-window rate limits
The following rolling 5-minute windows are evaluated independently. A single request can trigger any of them.
| Limit | Aggregation | Scope |
|---|---|---|
| 500 / 5 min | Source IP address | All endpoints |
| 3,000 / 5 min | API key (Authorization header) |
All endpoints |
| 300 / 5 min | API key (Authorization header) |
/v1/location/vessels/bounding-box and /v1/location/vessels/radius |
Example 429 response
Response headers include Retry-After: 60. The body:
{
"error": {
"type": "rate_limit_error",
"message": "You have exceeded the allowed request rate for this API key or IP. Reduce request volume and retry after the Retry-After period. See https://docs.vesselapi.com/rate-limits",
"retry_after_seconds": 60
}
}
Handling 429 in client code
Respect the Retry-After header value (in seconds) and retry after that delay. Short-window blocks lift automatically as your rolling 5-minute count drops below the limit — there is no separate "unblock" action for these. If your client keeps retrying inside the Retry-After window instead of waiting it out, you'll trip the sustained-abuse block instead, which is much harder to undo.
// Example: minimal backoff respecting Retry-After
async function withRetry(fn) {
for (let attempt = 0; attempt < 3; attempt++) {
const res = await fn();
if (res.status !== 429) return res;
const wait = Number(res.headers.get("Retry-After") || 60);
await new Promise(r => setTimeout(r, wait * 1000));
}
throw new Error("rate limited after retries");
}
Sustained-abuse blocks
A client that ignores Retry-After and keeps grinding requests through repeated 429s — roughly 50 consecutive rate-limit responses on the same API key in a short window — will trip a sustained-abuse block. When this fires, the offending source IP is blocked at the network edge and the API key is suspended for up to 6 hours. Any further requests from that IP to api.vesselapi.com are rejected with HTTP 403 before they reach the application; the dashboard and marketing site remain reachable so you can still manage your account.
There are two ways to clear a sustained-abuse block:
- Wait for the block to expire (up to 6 hours from when it fired).
- Upgrade to (or purchase) a paid plan. Activating a paid subscription automatically clears any active block on your account within ~30 seconds, so the upgrade is immediately useful.
Avoid the block entirely: implement Retry-After-aware backoff (see the example above) and cap your request concurrency. Tight retry loops that ignore Retry-After are the single most common cause of these blocks.
Time window cap on location searches
Separately from the rate limits above, /v1/location/vessels/bounding-box and /v1/location/vessels/radius reject any single query whose time window (time.to minus time.from) exceeds 4 hours. The default time window when neither bound is supplied is 2 hours, which is well within the cap.
If you need to retrieve positions across a longer range, split the request into sequential 4-hour slices and stitch the results client-side. The time predicate is inclusive on both ends, so to avoid duplicates at the boundaries use half-open intervals: set one slice's time.to to T and the next slice's time.from to T + 1ms. The API accepts millisecond-precision RFC3339.
# Single 12-hour request — rejected with HTTP 400:
GET /v1/location/vessels/bounding-box
?filter.lonLeft=101.76&filter.lonRight=103.71
&filter.latBottom=0.77&filter.latTop=2.72
&time.from=2026-04-21T06:00:00.000Z
&time.to=2026-04-21T18:00:00.000Z
# Three 4-hour slices stitched client-side — accepted:
GET .../bounding-box?...&time.from=2026-04-21T06:00:00.000Z&time.to=2026-04-21T10:00:00.000Z
GET .../bounding-box?...&time.from=2026-04-21T10:00:00.001Z&time.to=2026-04-21T14:00:00.000Z
GET .../bounding-box?...&time.from=2026-04-21T14:00:00.001Z&time.to=2026-04-21T18:00:00.000Z
Pagination across slices. A nextToken is only valid when reused with the same time.from / time.to as the call that issued it. The API does not reject mismatched bounds; it silently returns rows from the wrong slice. Paginate within each 4-hour slice independently.
If the 4-hour cap doesn't fit your use case, contact support and we can advise on alternatives.
Monthly quotas
Each subscription tier has a monthly request quota. When your key exceeds its monthly quota the API returns HTTP 429. Track remaining usage via the X-RateLimit-Remaining response header.
Best practice: monitor X-RateLimit-Remaining to avoid hitting your monthly quota, and implement Retry-After-aware backoff to handle short-window limits gracefully.
Error Handling
The API uses standard HTTP status codes and returns detailed error information in JSON format.
Error Types
invalid_request_error
The request had invalid parameters or was malformed.
HTTP Status: 400 Bad Request
authentication_error
Invalid API key or authentication failure.
HTTP Status: 401 Unauthorized
not_found_error
The requested resource was not found.
HTTP Status: 404 Not Found
rate_limit_error
Monthly request quota exceeded.
HTTP Status: 429 Too Many Requests
api_error
An internal server error occurred. These errors include a tracking ID for support.
HTTP Status: 500 Internal Server Error
service_unavailable_error
The service is temporarily unavailable. Retry after a brief delay.
HTTP Status: 503 Service Unavailable
Error Codes
| Code | Description |
|---|---|
| resource_missing | The requested resource (vessel, port, etc.) could not be found |
| invalid_parameter | A parameter value is invalid or malformed |
| invalid_mmsi | The provided MMSI (Maritime Mobile Service Identity) is not valid. MMSI must be a 9-digit number |
| invalid_imo | The provided IMO number is not valid. IMO must be a 7-digit number |
| invalid_coordinates | The provided latitude/longitude coordinates are invalid |
| invalid_time_range | The time range is invalid (e.g., end time before start time, or range too large) |
| missing_parameter | A required parameter was not provided |
| invalid_api_key | The provided API key is invalid or has been revoked |
| rate_limit_exceeded | Your API key has exceeded its monthly request quota |
| internal_error | An unexpected error occurred on the server. Contact support with the error_id if it persists |
| database_error | A database operation failed. This is a transient error; retry the request after a brief delay |
| service_unavailable | The service is temporarily unavailable. Retry after a brief delay |
Error Response Format
All errors return a JSON object with an error key. The fields included vary by error type.
400 Bad Request
Includes param and doc_url fields to help identify the issue.
{
"error": {
"type": "invalid_request_error",
"message": "Invalid MMSI: abc123",
"code": "invalid_mmsi",
"param": "mmsi",
"doc_url": "https://vesselapi.com/docs#invalid_mmsi"
}
}
Location searches with a time window over 4 hours are rejected with a 400 so callers know to split into shorter slices:
{
"error": {
"type": "invalid_request_error",
"message": "time window (time.to - time.from) must not exceed 4h0m0s. Split larger ranges into sequential calls and stitch results client-side; use half-open intervals (e.g. one slice's time.to = T, next slice's time.from = T + 1ms) to avoid boundary duplicates.",
"code": "invalid_parameter"
}
}
This applies to /location/vessels/bounding-box and /location/vessels/radius. See the Time window cap section above for the splitting recipe.
401 Unauthorized
{
"error": {
"type": "authentication_error",
"message": "api key is invalid or not found",
"code": "invalid_api_key"
}
}
404 Not Found
{
"error": {
"type": "not_found_error",
"message": "Vessel with MMSI 999999999 not found.",
"code": "resource_missing"
}
}
429 Too Many Requests
{
"error": {
"type": "rate_limit_error",
"message": "API monthly quota exceeded",
"code": "rate_limit_exceeded"
}
}
500 Internal Server Error
Includes a unique tracking ID and timestamp for support.
{
"error": {
"type": "api_error",
"message": "An internal error occurred. Please reference error ID 550e8400-e29b-41d4-a716-446655440000 when contacting support if this issue persists.",
"code": "internal_error",
"error_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2025-10-31T15:30:45Z"
}
}
Reference the error_id when contacting support for faster resolution.
503 Service Unavailable
{
"error": {
"type": "service_unavailable_error",
"message": "The service is temporarily unavailable. Retry after a brief delay.",
"code": "service_unavailable"
}
}
Pagination
List endpoints support pagination using cursor-based navigation.
Parameters
pagination.limit
Number of results to return per page (max 50, default 20)
pagination.nextToken
Cursor for the next page of results (provided in response)
Response Format
{
"vessels": [...],
"nextToken": "eyJsYXN0X2lkIjoxMjM0NTY3ODl9"
}
The array key varies by endpoint (e.g., vessels, vesselPositions, ports, portEvents, navtexMessages, emissions, casualties, inspections, dgpsStations, lightAids, radioBeacons, modus). The nextToken field is at the top level of the response.
Best Practices
Cache Responses
Static vessel data and port data are updated daily. Cache these responses to reduce API usage. AIS position data changes every 2-30 seconds for moving vessels, so cache TTLs should match your freshness requirements.
Handle Rate Limits
Implement exponential backoff when receiving 429 errors. Start with a 1-second delay and double it for each subsequent retry.
Use Pagination Efficiently
Request only the data you need. Use reasonable page sizes (20-50 items) and store pagination keys for subsequent requests.
Validate Input
Validate MMSI (9 digits) and IMO (7 digits) numbers on the client side before making API requests.
Monitor Error IDs
Log error IDs from 500 errors for troubleshooting. Include them when contacting support.
Ready to Get Started?
Try the endpoints interactively in the API Explorer or sign up for an API key.