Import Boat
Import a boat listing with images
/import/boatImport a new boat listing in a single API call. Handles creating the boat, storing image URLs, and auto-matching to a marina location. The boat is created unpublished — see Publishing.
Authentication
Requires API key in the X-API-Key header.
- Your API key: the boat is created under a company you belong to — one you own, when you own one — or, if you belong to none, under a personal "…'s Fleet" company created for you.
owner_id/owner_emailare ignored. To target a specific company, passcompany_id— you must be a member of it, or the request fails with403. - Platform key (internal): requires
owner_idorowner_emailin the request body to identify who the boat belongs to.
Request Body
Required Fields
| Field | Type | Description |
|---|---|---|
title | string | Boat name/title |
boat_type | string | One of: yacht, catamaran, sailboat, motorboat, rib, other |
capacity | integer | Maximum guests (must be at least 1) |
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
description | string | Full description (supports markdown) | |
price_per_day | number | Daily rate. Ignored when import_source is boataround — those boats are stored with no daily rate, because a rate here would not be backed by any bookable season. | |
length_ft | number | Length overall (LOA) in feet. Values outside the plausible hull range (~6.5–350 ft) are stored as no length rather than as sent — only boats priced from a connected platform take their length from the model catalogue. | |
year | integer | Year built | |
cabins | integer | Number of cabins | |
berths | integer | Number of berths | |
toilets | integer | Number of toilets | |
model | string | Boat model name, stored as sent | |
manufacturer_name | string | The make. Matched on its slug (the name lowercased, non-alphanumerics turned into -), so send the name exactly as GET /api/manufacturers lists it — Bali, not Bali Catamarans. A name that matches no existing make creates a new one, and boats under a stray make don't answer the site's make filter for the real one. | |
rental_type | string | bareboat | bareboat, skippered, or no_licence_needed |
fuel_included | boolean | false | Whether fuel is included |
security_deposit | number | Security deposit amount | |
instant_book | boolean | false | Allow instant booking |
is_published | boolean | false | Has no lasting effect — imported boats end up unpublished (see below) |
Publishing
An imported boat is created unpublished, and the import cannot publish it. A listing
goes live only when it is complete — a title, a location, a capacity, at
least one image, and check-in days — and check-in days cannot be sent through this
endpoint. is_published: true runs that completeness check, which therefore always ends
with the boat unpublished; the response's boat.is_published reads false.
Open the boat in the dashboard, set its check-in days (and fill anything else it reports missing), and it publishes as soon as it is complete.
Location Fields
The boat is placed on a marina in this order:
- Coordinates. With
latitudeandlongitude, the first known location inside a box of roughly ±1 km around the point is used. It is the first one found, not the nearest, so send the marina's own coordinates. - Name. If that finds nothing (or no coordinates were sent),
location_addressis matched as a substring of a location's name (send the marina name, e.g.D-Marin Lefkas, not a street address). - New marina. If neither matches and coordinates were sent, a new marina is created at them, named after
location_address. Without one it has no name and is shown by its type ("Marina") — a name is never made up from the city.
location_city and location_state are only used to fill in a newly created marina — they never find an existing one. A boat sent with neither coordinates nor a matching name gets no location.
| Field | Type | Description |
|---|---|---|
location_city | string | City; stored on a newly created marina only |
location_state | string | Country; stored as the country of a newly created marina only |
location_address | string | Marina name — matched as a substring of location names, and used to name a new marina |
latitude | number | GPS latitude |
longitude | number | GPS longitude |
Deduplication Fields
When both fields are provided, the endpoint checks for existing imports to prevent duplicates.
| Field | Type | Description |
|---|---|---|
import_source | string | Platform name (e.g. boataround) |
import_source_id | string | Unique ID on that platform |
Owner Identification
| Field | Type | Description |
|---|---|---|
company_id | string | Assign to a specific company (overrides auto-detection). With your API key you must be a member of it (403 otherwise) |
owner_id | string | Platform key only — ignored with your own API key |
owner_email | string | Platform key only — ignored with your own API key |
owner_name | string | Platform key only — ignored with your own API key |
Images
Array of image URLs or image objects. The first image is set as primary unless specified otherwise.
| Field | Type | Default | Description |
|---|---|---|---|
images | array | Image URLs (strings) or objects with url and optional is_primary | |
download_images | boolean | false | Download images to server storage instead of keeping source URLs |
Equipment
A boat's equipment (what the features search filter and the listing's equipment list read) is set in the dashboard after import, not through this endpoint.
Request
Response
The boat object is the full boat record (every column on the boats row) plus
company, manufacturer, images, and a legacy amenities list (not used by listings — see Equipment). Only the most relevant
fields are shown below. Store boat.id — the slug in view_url can change when the listing is renamed.
Response Fields
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the import succeeded |
duplicate | boolean | true if an existing boat was returned (same import_source + import_source_id under the same company) |
boat | object | Created (or existing) boat with company, manufacturer and images |
images_uploaded | integer | Number of image records created (absent on a duplicate) |
images_downloaded | integer | Number of images downloaded to storage when download_images: true (absent on a duplicate) |
amenities_added | integer | Legacy counter, 0 unless a legacy amenities array was sent (absent on a duplicate) |
view_url | string | Public URL of the listing — it serves once the boat is published |
Deduplication Behavior
When import_source and import_source_id are both provided:
- Same company: Returns the existing boat with
duplicate: trueand nothing is changed — the response is{ success, duplicate: true, boat, view_url }only (no image or amenity counters) - Different company: Returns
409 Conflict
The check runs before the boat, its images or a new marina are created, so a duplicate adds none of them.
Errors
| Status | Message |
|---|---|
400 | title is required |
400 | boat_type is required |
400 | boat_type must be one of: yacht, catamaran, sailboat, motorboat, rib, other |
400 | capacity is required and must be >= 1 |
400 | rental_type must be one of: bareboat, skippered, no_licence_needed |
400 | Owner identification required: provide owner_id or owner_email |
401 | API key required. Include X-API-Key header. |
401 | Invalid API key |
403 | API key owner is not a member of the specified company |
409 | This boat has already been imported by another company |
500 | Failed to create company: … |
500 | Failed to create boat: … |
A request with no key at all is stopped by bot protection before it reaches this endpoint (an HTML 403 page).