Overview
The whole index is published as static JSON files over HTTPS. There is no key, no sign-up and no rate limit, and CORS is open, so you can call it straight from a browser.
Files are regenerated every time the index is updated. All endpoints are GET and live under:
https://liveaboardindex.com/api/v1
Quick start
Find a boat by IMO, then fetch its full record:
curl https://liveaboardindex.com/api/v1/lookup/imo/8978930.json
# {"id":"lvb_7k2m9q"}
curl https://liveaboardindex.com/api/v1/boats/lvb_7k2m9q.json
The same in JavaScript. A lookup that doesn't exist returns HTTP 404:
const base = 'https://liveaboardindex.com/api/v1'
const res = await fetch(`${base}/lookup/imo/8978930.json`)
if (res.status === 404) throw new Error('Not in the index')
const { id } = await res.json()
const boat = await fetch(`${base}/boats/${id}.json`).then((r) => r.json())
For search or filtering, download /boats.json once and filter it locally. It holds every active boat:
const boats = await fetch('https://liveaboardindex.com/api/v1/boats.json').then((r) => r.json())
const big = boats.filter((b) => b.guests >= 16 && b.region === 'galapagos')
Endpoints
| Path | Returns |
|---|---|
| /boats.json | Every active boat, compact |
| /boats/{id}.json | One boat, full record |
| /lookup/imo/{imo}.json | Boat ID by IMO |
| /lookup/mmsi/{mmsi}.json | Boat ID by MMSI |
| /regions.json | Every region |
| /regions/{id}.json | Boats in a region |
| /operators.json | Every active operator |
| /operators/{id}.json | One operator, full record |
List boats
/api/v1/boats.json
Every active boat in a compact form, sorted by name. Merged duplicates are left out.
Example: /api/v1/boats.json
[
{
"id": "lvb_7y50yc",
"name": "Aqua",
"url": "/boats/lvb_7y50yc/aqua",
"imo": null,
"mmsi": null,
"flag": null,
"region": "galapagos",
"operator": "op_kmy8",
"hull": null,
"loa_m": 26,
"guests": 16,
"cabins": 9,
"status": "active"
},
{
"id": "lvb_7k2m9q",
"name": "Calipso",
"url": "/boats/lvb_7k2m9q/calipso",
"imo": 8978930,
"mmsi": 735023481,
"flag": "EC",
"region": "galapagos",
"operator": "op_snng",
"hull": "steel_motor",
"loa_m": 36,
"guests": 16,
"cabins": 8,
"status": "active"
},
… 7 more
]
Get a boat
/api/v1/boats/{id}.json
The full record. Exists for every ID ever issued, including merged ones; see merges.
Example: /api/v1/boats/lvb_7k2m9q.json
{
"id": "lvb_7k2m9q",
"names": [
{
"name": "Calipso",
"from": null,
"to": null
}
],
"hull": "steel_motor",
"built": {
"year": 1991,
"yard": null
},
"imo": 8978930,
"mmsi": 735023481,
"call_sign": "HC-2081",
"flag": "EC",
"loa_m": 36,
"beam_m": 8,
"gt": 249,
"refit_year": 2019,
"guests": 16,
"cabins": 8,
"bathrooms": 9,
"max_speed_kn": 11,
"cruising_speed_kn": 9,
"engines": {
"count": 2,
"make": "cummins",
"model": "NTA855-M",
"power_hp": 400
},
"tenders": 2,
"fuel_l": 27255,
"water_l": 18927,
"watermaker_lpd": null,
"cabin_types": [
{
"name": "Upper Deck Cabins",
"count": null,
"max_guests": 2,
"beds": {
"single": 0,
"double": 1,
"bunk": 0,
"convertible": true
},
"size_m2": {
"min": 11,
"max": 13
},
"deck": "upper",
"window": "panoramic",
"bathroom": "ensuite",
"aircon": true,
"external_ids": {
"liveaboard_com": 9461
}
},
{
"name": "Main Deck Cabins",
"count": null,
"max_guests": 2,
"beds": {
"single": 0,
"double": 1,
"bunk": 0,
"convertible": true
},
"size_m2": {
"min": 11,
"max": 13
},
"deck": "main",
"window": "panoramic",
"bathroom": "ensuite",
"aircon": true,
"external_ids": {
"liveaboard_com": 9462
}
}
],
"amenities": [
"ensuite_bathrooms",
"aircon_cabins",
"seaview_cabins",
"non_smoking_cabins",
"tv_in_cabins",
"hot_showers",
"outside_showers",
"indoor_saloon",
"aircon_saloon",
"outdoor_dining",
"bar",
"bbq",
"sun_deck",
"leisure_deck",
"jacuzzi",
"library",
"entertainment",
"internet",
"charging_stations",
"camera_station",
"camera_rinse",
"housekeeping",
"purpose_built_dive",
"private_charter"
],
"dive_facilities": [
"dive_deck",
"shaded_dive_area",
"dive_tenders",
"rinse_hoses",
"nitrox",
"din_adaptors",
"sidemount",
"gear_rental"
],
"safety": [
"radar",
"gps",
"vhf_dsc_ssb",
"satellite_phone",
"depth_sounder",
"searchlight",
"epirb",
"flares",
"life_rafts",
"life_vests",
"fire_suppression",
"bilge_alarm",
"engine_room_cctv",
"oxygen",
"first_aid_kit",
"crew_first_aid"
],
"dining": [
"local_food",
"western_food",
"vegetarian",
"vegan",
"buffet",
"snacks",
"beer",
"wine"
],
"languages": [
"en",
"es"
],
"operator": "op_snng",
"region": "galapagos",
"status": "active",
"merged_into": null,
"external_ids": {
"liveaboard_com": 5901
},
"sources": [
"https://magicport.ai/vessels/passenger/calipso-mmsi-735023481",
"https://www.liveaboard.com/diving/galapagos/calipso"
],
"notes": "",
"name": "Calipso",
"url": "/boats/lvb_7k2m9q/calipso"
}
Look up by IMO or MMSI
/api/v1/lookup/imo/{imo}.json
/api/v1/lookup/mmsi/{mmsi}.json
Returns the ID of the active boat with that number, or HTTP 404 if there is none.
Examples: /api/v1/lookup/imo/8978930.json, /api/v1/lookup/mmsi/735023481.json
{
"id": "lvb_7k2m9q"
}
List regions
/api/v1/regions.json
Every region: { id, name, countries }, with ISO country codes.
Example: /api/v1/regions.json
[
{
"id": "galapagos",
"name": "Galápagos",
"countries": [
"EC"
]
}
]
Boats in a region
/api/v1/regions/{id}.json
Active boats in the region, in the same compact form as /boats.json. Only regions with at least one boat have a file.
Example: /api/v1/regions/galapagos.json
[
{
"id": "lvb_7y50yc",
"name": "Aqua",
"url": "/boats/lvb_7y50yc/aqua",
"imo": null,
"mmsi": null,
"flag": null,
"region": "galapagos",
"operator": "op_kmy8",
"hull": null,
"loa_m": 26,
"guests": 16,
"cabins": 9,
"status": "active"
},
{
"id": "lvb_7k2m9q",
"name": "Calipso",
"url": "/boats/lvb_7k2m9q/calipso",
"imo": 8978930,
"mmsi": 735023481,
"flag": "EC",
"region": "galapagos",
"operator": "op_snng",
"hull": "steel_motor",
"loa_m": 36,
"guests": 16,
"cabins": 8,
"status": "active"
},
… 7 more
]
List operators
/api/v1/operators.json
Every active operator, with the number of active boats it runs.
Example: /api/v1/operators.json
[
{
"id": "op_366c",
"name": "Aggressor Fleet & Dancer Fleet",
"url": "/operators/op_366c/aggressor-fleet-dancer-fleet",
"country": null,
"status": "active",
"boats": 1
},
{
"id": "op_kmy8",
"name": "Andean Travel Company",
"url": "/operators/op_kmy8/andean-travel-company",
"country": null,
"status": "active",
"boats": 1
},
… 6 more
]
Get an operator
/api/v1/operators/{id}.json
The full operator record.
Example: /api/v1/operators/op_snng.json
{
"id": "op_snng",
"names": [
{
"name": "Calipso Galapagos",
"from": null,
"to": null
}
],
"country": null,
"website": null,
"status": "active",
"merged_into": null,
"external_ids": {
"liveaboard_com": 1290
},
"sources": [],
"notes": "",
"name": "Calipso Galapagos",
"url": "/operators/op_snng/calipso-galapagos"
}
IDs
Boats are lvb_ + 6 characters, operators op_ + 4, in lowercase Crockford base32 (no i, l, o or u). IDs are random, never change and are never reused, even if a boat is renamed, reflagged or sold.
Store the ID, not the name or IMO. IMO and MMSI are attributes: they can be missing, and an MMSI can change when a boat is reflagged.
API paths are exact and lowercase. Website URLs are forgiving: /boats/LVB_7K2M9Q and any slug redirect to the canonical page.
Merges
When two records turn out to be the same boat, one is kept and the other gets status: "merged" plus merged_into with the surviving ID. Merged records drop out of lists and lookups, but their /boats/{id}.json file stays forever, so stored IDs keep resolving. Follow merged_into to get the current record.
Boat fields
| Field | Type | Description |
|---|---|---|
| id | string | Permanent ID: lvb_ + 6 Crockford base32 characters. Never changes or gets reused. |
| name | string | Current name. |
| url | string | Canonical page on liveaboardindex.com. For merged boats, the page of the boat it was merged into. |
| names | array | Name history, oldest first: { name, from, to }. to: null marks the current name. Dates are a year, YYYY-MM or YYYY-MM-DD. |
| status | string | One of active, retired, lost, merged. |
| merged_into | string | null | When status is merged, the ID of the record this duplicate was merged into. |
| region | string | null | Region ID, see /regions.json. |
| operator | string | null | Operator ID (op_xxxx). |
| hull | string | null | <material>_<propulsion>. Materials: steel, wood, aluminium, fiberglass, composite. Propulsion: motor, sail, motor_sail. |
| built | object | { year, yard }, both nullable. |
| refit_year | number | null | Year of the last major refit. |
| guests | number | null | Maximum guests. |
| cabins | number | null | Guest cabins. |
| bathrooms | number | null | Number of bathrooms. |
| cabin_types | array | Cabin types: { name, count, max_guests, beds, size_m2, deck, window, bathroom, aircon, external_ids }. beds is { single, double, bunk, convertible }; size_m2 is { min, max }. Deck: lower, main, upper, sun, across. Window: none, porthole, standard, picture, panoramic. Bathroom: ensuite, shared. count is the number of cabins of that type, when known. |
| amenities | string[] | On-board amenities: ensuite_bathrooms, aircon_cabins, seaview_cabins, family_cabins, non_smoking_cabins, tv_in_cabins, hot_showers, outside_showers, indoor_saloon, aircon_saloon, outdoor_dining, bar, bbq, sun_deck, observation_deck, leisure_deck, jacuzzi, library, entertainment, internet, charging_stations, camera_station, camera_rinse, laundry, housekeeping, kayaks, snorkeling_gear, naturalist_guide, non_diver_friendly, purpose_built_dive, private_charter. |
| dive_facilities | string[] | Diving setup: dive_deck, shaded_dive_area, dive_tenders, rinse_hoses, nitrox, din_adaptors, sidemount, tech_diving, gear_rental. |
| safety | string[] | Navigation and safety equipment: radar, gps, vhf_dsc_ssb, satellite_phone, depth_sounder, fishfinder, searchlight, epirb, flares, life_rafts, life_vests, fire_suppression, bilge_alarm, engine_room_cctv, oxygen, first_aid_kit, crew_first_aid. |
| dining | string[] | Food and drink: local_food, western_food, vegetarian, vegan, buffet, set_menu, snacks, beer, wine. |
| languages | string[] | Languages the crew speaks, ISO 639-1 (e.g. en, es). |
| max_speed_kn | number | null | Top speed, knots. |
| cruising_speed_kn | number | null | Cruising speed, knots. |
| engines | object | null | { count, make, model, power_hp }, power_hp per engine. Makes: caterpillar, cummins, detroit_diesel, deutz, john_deere, man, mitsubishi, mtu, mwm, perkins, scania, volvo_penta, yanmar. |
| tenders | number | null | Number of tenders (dinghies / skiffs). |
| fuel_l | number | null | Fuel capacity, liters. |
| water_l | number | null | Fresh water capacity, liters. |
| watermaker_lpd | number | null | Watermaker output, liters per day. |
| loa_m | number | null | Length overall, meters. |
| beam_m | number | null | Beam, meters. |
| gt | number | null | Gross tonnage. |
| imo | number | null | IMO number (7 digits, check digit validated). Unique across active boats. |
| mmsi | number | null | MMSI (9 digits). Unique across active boats. |
| call_sign | string | null | Radio call sign. |
| flag | string | null | Flag state, ISO 3166-1 alpha-2 (e.g. EC). |
| external_ids | object | IDs in other systems: { liveaboard_com }. |
| sources | string[] | URLs the data was checked against. |
| notes | string | Free-form editorial notes; empty string if none. |
Operator fields
| Field | Type | Description |
|---|---|---|
| id | string | Permanent ID: op_ + 4 Crockford base32 characters. |
| name | string | Current name. |
| url | string | Canonical page on liveaboardindex.com. |
| names | array | Name history, same shape as for boats. |
| status | string | One of active, defunct, merged. |
| merged_into | string | null | When merged, the ID of the operator it was merged into. |
| country | string | null | Home country, ISO 3166-1 alpha-2. |
| website | string | null | Official website. |
| external_ids | object | IDs in other systems: { liveaboard_com }. |
| sources | string[] | URLs the data was checked against. |
| notes | string | Free-form editorial notes; empty string if none. |
List endpoints return a subset of these fields. /operators.json adds boats, the number of active boats.
Versioning
Within v1, fields may be added but are never renamed, removed or changed in type, so ignore fields you don't know. A breaking change would ship as /api/v2 alongside v1.
Missing values are always null, never omitted.