Boats
The primary data surface. Both endpoints require the boats.read scope.
GET /v1/boats
Returns a page of boat summaries.
curl -s "https://api.theboatdb.com/v1/boats?manufacturer=albin-marine&sort=year&order=desc" \
-H "Authorization: Bearer $THEBOATDB_API_KEY"Query parameters
| Parameter | Type | Notes |
|---|---|---|
manufacturer | slug | e.g. albin-marine |
type | slug | Boat type, e.g. sail, power |
category | slug | e.g. cruiser-racer-boats |
activity | slug | e.g. day_cruising |
yearFrom, yearTo | 1800–2100 | Production-window overlap — see below |
lengthMin, lengthMax | number | Expressed in lengthUnit |
lengthUnit | m, ft | Unit of your length bounds. Default m |
search | 2–100 chars | Term-prefix match on model name |
sort | model, year, length, manufacturer | Default model |
order | asc, desc | Default asc |
limit, offset | integer | limit max 100, default 20 |
Any other parameter is a 400. See Conventions.
Filtering by year
Year filtering is overlap-based, not a filter on the first year of production. A boat built from
1971 to 1982 matches ?yearFrom=1975, because it was in production then.
- A boat with no recorded last year is treated as still in production, so it matches any
yearTo. - A boat with no recorded first year is excluded as soon as you set either bound — there is nothing to compare it against, and including it would mean returning boats we cannot confirm match.
Filtering by length
Our catalogue stores each boat's measurements in the system its manufacturer published, so lengths are a mix of metres and feet. You do not have to care:
# Boats between 8 and 12 metres
curl -s "https://api.theboatdb.com/v1/boats?lengthMin=8&lengthMax=12" -H "Authorization: Bearer $KEY"
# The same range, expressed in feet
curl -s "https://api.theboatdb.com/v1/boats?lengthMin=26&lengthMax=40&lengthUnit=ft" -H "Authorization: Bearer $KEY"Your bound is converted to both systems and each boat is compared against the one matching its own declared unit.
lengthUnit describes your bounds. It is not a filter on the boat's own unit, and it does not
change the units in the response — measurements always come back as stored, with unit telling
you which system they are in.
sort=length orders on a normalised metre value, so a mixed-unit catalogue sorts as one sequence
rather than interleaving 30-foot and 30-metre boats.
Response
data is an array of boat summaries:
{
"data": [
{
"id": "boat-3f9c1a",
"slug": "albin-vega-27",
"model": "Vega 27",
"version": null,
"manufacturer": {
"id": "mfr-71b0",
"name": "Albin Marine",
"slug": "albin-marine",
"website": "https://example.com"
},
"type": { "id": "type-sail", "name": "Sail", "slug": "sail" },
"production": { "firstYear": 1966, "lastYear": 1979 },
"unit": "metric",
"lengthOverall": 8.25,
"lengthOverallMetres": 8.25,
"referencePrice": { "amount": 12000, "currency": { "code": "EUR", "symbol": "€" } },
"image": {
"url": "https://cdn.sanity.io/images/...",
"alt": null,
"width": 1600,
"height": 1067,
"format": "jpg"
}
}
],
"pagination": { "total": 1482, "limit": 20, "offset": 0 }
}| Field | Type | Notes |
|---|---|---|
id | string | Stable across renames. Store this, not the slug. |
slug | string | Readable identifier, also accepted by the detail endpoint. |
model | string | |
version | string | null | Variant name, where the model has one. |
manufacturer | object | null | Thin reference: id, name, slug, website. |
type | object | null | id, name, slug. |
production | object | firstYear and lastYear, either of which may be null. |
unit | metric | imperial | Which system the measurements are in. |
lengthOverall | number | null | In the system unit declares. |
lengthOverallMetres | number | null | Always metres. The comparable length. |
referencePrice | object | null | amount plus currency.code and currency.symbol. |
image | object | null | url, alt, width, height, format. |
GET /v1/boats/:idOrSlug
Returns one boat in full, addressable by slug or id.
curl -s "https://api.theboatdb.com/v1/boats/albin-vega-27" \
-H "Authorization: Bearer $THEBOATDB_API_KEY"Takes no query parameters — ?fields=model is a 400, not a partial response. An unknown or
unpublished boat is a 404 NOT_FOUND.
Response
Everything in the summary above, plus the full specification grouped by subject:
| Group | Contains |
|---|---|
description | Plain text, or null when no published description exists |
categories | Array of id/name/slug references |
activities | Array of id/name/slug references |
hull | type, material, keelType, unsinkable, trailerable, appendages |
dimensions | Lengths, beams, drafts, headroom, displacement, ballast, tonnage |
accommodation | cabins, berths, heads |
steering | helm, rudder, cockpitType, cockpitLocation |
performance | cruisingSpeed, topSpeed, displacementHullSpeed |
rig | Mast height and configuration, rigging, spars, spreaders |
sails | upwindArea, downwindArea, and name/value entries for mainsail, headsail and spinnaker |
tanks | Capacities for fuel, water, holding and waterHeaters |
engines | Array of maker, model, type, drive, fuel, power, powerType |
designers | naval, interior and exterior, each an array of id/name/website |
ceClassification | Levels for classes a–d, or null if unclassified |
gallery | Array of images, same shape as image |
Engine power and tank capacities are strings, not numbers. Real values include "2 x 40" for a
twin-engine installation, and coercing that to a number would silently misreport the boat. Parse
them as text.
Any specification we do not hold for a given boat is null rather than omitted, so the shape of the
object does not change between boats. description is only populated once an editor has published
it — a work-in-progress description reads as null here rather than shipping early.