OFFICIAL SDK
VesselAPI Go SDK
vesselapi-go is the official Go client for the VesselAPI REST API: vessel particulars and positions, port events, ports, emissions and casualty records. Every call takes a context, errors are typed, and every list endpoint has an iterator.
Installation and authentication
go get github.com/vessel-api/vesselapi-go/v4
v4 requires Go 1.24. Versions v1 to v3 are deprecated: their pkg.go.dev pages still list methods for endpoints the API retired in August 2026 and now answers with HTTP 410.
The constructor takes the API key. Keys are created in the dashboard and sent as a bearer token on every request. Options set the base URL, the HTTP client, the user agent and the retry count.
import (
"context"
"net/http"
"os"
"time"
vesselapi "github.com/vessel-api/vesselapi-go/v4"
)
client, err := vesselapi.NewVesselClient(os.Getenv("VESSELAPI_API_KEY"),
vesselapi.WithVesselHTTPClient(&http.Client{Timeout: 60 * time.Second}),
vesselapi.WithVesselUserAgent("my-app/1.0"),
vesselapi.WithVesselRetry(3), // the default
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
3 examples
1. Look up a vessel and its latest position
Vessel endpoints take an IMO number by default; pass the MMSI id type to look up by MMSI. A vessel that exists may have no stored position, and the API answers that with 404.
// Particulars by IMO number (nil params means the default id type, IMO)
vessel, err := client.Vessels.Get(ctx, "9811000", nil)
if err != nil {
log.Fatal(err)
}
// Every response field is a pointer; Deref returns the zero value for nil.
if vessel.Vessel != nil {
fmt.Println(vesselapi.Deref(vessel.Vessel.Name), vesselapi.Deref(vessel.Vessel.VesselType))
}
// Latest stored position by MMSI
pos, err := client.Vessels.Position(ctx, "232003239", &vesselapi.GetVesselIdPositionParams{
FilterIdType: vesselapi.Ptr("mmsi"),
})
var apiErr *vesselapi.APIError
switch {
case errors.As(err, &apiErr) && apiErr.IsNotFound():
fmt.Println("no stored position for this vessel")
case err != nil:
log.Fatal(err)
default:
fmt.Println(vesselapi.Deref(pos.VesselPosition.Latitude), vesselapi.Deref(pos.VesselPosition.Longitude))
}
2. Vessels within 10 km of a point
Area queries return vessels with a position in the last 2 hours by default, and at most 4 hours. The radius is in metres, up to 100 km.
nearby, err := client.Location.VesselsRadius(ctx, &vesselapi.GetLocationVesselsRadiusParams{
FilterLatitude: 51.9225,
FilterLongitude: 4.47917,
FilterRadius: 10000,
})
if err != nil {
log.Fatal(err)
}
for _, v := range vesselapi.Deref(nearby.Vessels) {
fmt.Println(vesselapi.Deref(v.VesselName), vesselapi.Deref(v.Latitude), vesselapi.Deref(v.Longitude))
}
3. Page through port events
Every list method has an iterator variant that follows nextToken for you. Each page is one API call against your quota, so the page size sets how many calls a full walk costs.
// Every port event in the last 24 hours, 50 per page
since := time.Now().UTC().Add(-24 * time.Hour).Format(time.RFC3339)
it := client.PortEvents.ListAll(ctx, &vesselapi.GetPorteventsParams{
FilterTimeFrom: vesselapi.Ptr(since),
PaginationLimit: vesselapi.Ptr(50),
})
for it.Next() {
e := it.Value()
fmt.Println(vesselapi.Deref(e.Event), vesselapi.Deref(e.PortName), vesselapi.Deref(e.VesselName))
}
if err := it.Err(); err != nil {
log.Fatal(err)
}
// Collect gathers every page; check the error before treating the slice as complete.
ports, err := client.Search.AllPorts(ctx, &vesselapi.GetSearchPortsParams{
FilterCountry: &[]string{"NL"},
}).Collect()
Errors and retries
A call rejected before anything is sent returns a *ParamError (a missing required filter, an out-of-range coordinate). A non-2xx response returns an *APIError with the status code, the API's error code and the message. 429 and 5xx responses are retried with exponential backoff, and the Retry-After header is honoured.
var apiErr *vesselapi.APIError
if errors.As(err, &apiErr) {
switch {
case apiErr.IsNotFound():
fmt.Println("no such resource")
case apiErr.IsRateLimited():
fmt.Println("quota or rate limit reached")
case apiErr.IsAuthError():
fmt.Println("check the API key")
case apiErr.Code == vesselapi.ErrorCodeFeatureNotAvailable:
fmt.Println("this endpoint is not part of the plan")
}
fmt.Println(apiErr.StatusCode, apiErr.Message)
}
if errors.Is(err, vesselapi.ErrInvalidParams) {
fmt.Println("nothing was sent:", err)
}
Services
| Service | Covers | Guide |
|---|---|---|
| Vessels | Vessel particulars, latest and batch positions, ETA, emissions and casualty records | Vessel tracking |
| Ports | Port lookup by UN/LOCODE and inbound vessels | Ports and navigation |
| PortEvents | Arrivals and departures by port, by vessel or across all covered ports | Port events |
| Emissions | EU MRV emissions reports by reporting year | Emissions and casualty records |
| Search | Search across vessels, ports, lights, radio beacons, DGPS stations and MODUs | Vessel tracking |
| Location | Bounding-box and radius queries for every entity type | Ports and navigation |
The Notifications API (webhooks and WebSocket) is not part of the SDK; the notifications guide shows the plain HTTP calls. Request and response schemas for every endpoint are in the API reference, and the package is on pkg.go.dev with its source on GitHub. For an AI assistant, the MCP server exposes the same endpoints as tools.
Start with the free tier
150 calls a month, every endpoint, no card. Paid tiers raise the quota.