New features are available only in the new API.
All the new features — Seasons, Crew Details (images & descriptions), Menu Details, More videos & more photos (over 20), new Amenities & Watersports, and every other new feature — are available only in the new API, not in the legacy API.

Central Yacht Agent — Public API Guide

Integrate yacht data (listings, details, seasons, availability) into your website or app.

↗ Interactive Docs


Base URL https://api.centralyachtagent.com/public/api/v1
Format JSON over HTTPS
Interactive docs /public/documentation
1. Authentication

Every request must include your API key in the X-API-Key header. Requests without a valid key return 403.

X-API-Key: your_api_key_here
🔑
Generate your key on the API Connections page in your Central Yacht Agent account (requires a plan with API access).
Keep your API key server-side. Never expose it in browser/frontend code — proxy the calls through your own backend (see examples below).

A key may also be restricted to a specific server IP (allowed_ip) or domain (allowed_domain). If your key is IP-restricted, calls must originate from that server's egress IP — another reason to call the API from your backend, not the browser.

2. Rate Limits
ScopeLimit
Per API key60 requests / minute
  • The limiter is keyed by your API key (fallback to client IP if no key).
  • Window strategy: fixed window (resets each minute).
  • When you exceed the limit you get HTTP 429 with a Retry-After header.

429 response body

{
  "error": "rate_limit_exceeded",
  "message": "Rate limit exceeded: 60 per 1 minute",
  "detail": "Too many requests. Please slow down and try again later.",
  "retry_after": 60
}

Response headers on 429

Retry-After: 60
X-RateLimit-Limit: 60 per 1 minute

Best practices

  • Cache responses where you can. /yachts/{user_id} is cached server-side for 60s and /bookings for 30s, so you don't need to poll them aggressively.
  • On a 429, back off and retry after the number of seconds in Retry-After.
  • Batch/queue your sync jobs instead of firing 60+ calls in one burst.
3. Endpoints

All paths are relative to the base URL https://api.centralyachtagent.com/public/api/v1.

Reference data (Labels)

Use these to map filter IDs to human labels (areas, amenities, watersports).

MethodPathDescription
GET/operating_areasAll cruising/operating areas with IDs + labels
GET/amenitiesAll yacht amenities with IDs + labels
GET/watersportsAll watersports equipment with IDs + labels
Yachts
MethodPathDescription
GET/yachts/{user_id}List yachts owned by a user (cached 60s)
GET/yachts_get/{yacht_id}Get one yacht by ID. Optional ?include= to limit fields
POST/yachts_searchSearch yachts with filters + pagination
GET/{yacht_id}/yacht/seasonsSeasons + pricing for a yacht

/yachts_get/{yacht_id} include parameter

By default the full yacht document is returned. Use include (comma-separated) to return only specific sub-collections and keep responses small:

GET /yachts_get/670f8ec038d3078885154e8d?include=images,seasons,reviews

Available values: images, amenities, watersports, videos, reviews, seasons.

Bookings / Availability
MethodPathDescription
GET/bookings?yacht_id={yacht_id}Bookings for a yacht for the current year (cached 30s)
POST /yachts_search — request body
{
  "page": 1,
  "limit": 10,
  "searchTerm": "",
  "filters": {
    "yachtTypes": ["motor", "sail"],
    "cabins": [3, 8],
    "guests": [6, 16],
    "length": [20, 60],
    "length_unit": "meter",
    "yearBuild": [2018, 2026],
    "season_price": [20000, 100000],
    "user_currency": "USD",
    "location_areas": [25, 20],
    "watersports": [3, 13, 17],
    "start_date": "2026-07-01",
    "end_date": "2026-07-14",
    "available_days": 7
  }
}
FieldTypeDefaultNotes
pageint1Pagination page
limitint10Items per page
searchTermstring""Matches yacht name
filters.yachtTypesstring[]e.g. ["sail","motor"]
filters.cabinsint[2][min, max]
filters.guestsint[2][min, max]
filters.lengthfloat[2][min, max]
filters.length_unitstring"meter""meter" or "feet"
filters.yearBuildint[2][min, max]
filters.season_pricefloat[2][min, max]
filters.user_currencystring"USD"USD, Euro, Pounds, CAD
filters.location_areasint[]Area IDs from /operating_areas
filters.watersportsint[]IDs from /watersports
filters.start_datestringYYYY-MM-DD
filters.end_datestringYYYY-MM-DD
filters.available_daysintMin available days in date range

Response shape (search & list)

{
  "yachts": [
    {
      "id": "670f8ec038d3078885154e8d",
      "yachtName": "Ocean Spirit",
      "yachtType": "sail",
      "length": 24.5,
      "units": "meter",
      "year_build": 2020,
      "cabins": 4,
      "pax": 8,
      "seasonAreas": ["Greece", "Croatia"],
      "image": "https://cya.sfo3.cdn.digitaloceanspaces.com/yachts/ocean_spirit_main.jpg",
      "min_price": 35000,
      "currency": "Euro"
    }
  ],
  "totalYachts": 1
}
5. Examples
Replace YOUR_API_KEY with your key. Server-side calls only.
# List yachts for a user
curl -X GET "https://api.centralyachtagent.com/public/api/v1/yachts/12345" \
  -H "X-API-Key: YOUR_API_KEY"

# Get one yacht, only images + seasons
curl -X GET "https://api.centralyachtagent.com/public/api/v1/yachts_get/670f8ec038d3078885154e8d?include=images,seasons" \
  -H "X-API-Key: YOUR_API_KEY"

# Search yachts
curl -X POST "https://api.centralyachtagent.com/public/api/v1/yachts_search" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "page": 1,
    "limit": 10,
    "filters": { "yachtTypes": ["motor"], "guests": [6, 16] }
  }'

# Bookings for a yacht
curl -X GET "https://api.centralyachtagent.com/public/api/v1/bookings?yacht_id=670f8ec038d3078885154e8d" \
  -H "X-API-Key: YOUR_API_KEY"

Store the key in config/services.php and .env, never hard-coded.

# .env
CYA_API_BASE=https://api.centralyachtagent.com/public/api/v1
CYA_API_KEY=YOUR_API_KEY
// config/services.php
'cya' => [
    'base' => env('CYA_API_BASE'),
    'key'  => env('CYA_API_KEY'),
],
use Illuminate\Support\Facades\Http;

class CyaApi
{
    private function client()
    {
        return Http::baseUrl(config('services.cya.base'))
            ->withHeaders(['X-API-Key' => config('services.cya.key')])
            ->acceptJson()
            ->timeout(15);
    }

    // GET /yachts/{user_id}
    public function yachtsForUser(string $userId): array
    {
        return $this->client()->get("/yachts/{$userId}")->throw()->json();
    }

    // GET /yachts_get/{yacht_id}?include=...
    public function yacht(string $yachtId, array $include = []): array
    {
        $query = $include ? ['include' => implode(',', $include)] : [];
        return $this->client()
            ->get("/yachts_get/{$yachtId}", $query)
            ->throw()->json();
    }

    // POST /yachts_search
    public function search(array $filters = [], int $page = 1, int $limit = 10): array
    {
        $res = $this->client()->post('/yachts_search', [
            'page'    => $page,
            'limit'   => $limit,
            'filters' => $filters,
        ]);

        // Handle rate limiting gracefully
        if ($res->status() === 429) {
            $retryAfter = (int) $res->header('Retry-After', 60);
            throw new \RuntimeException("Rate limited. Retry after {$retryAfter}s.");
        }

        return $res->throw()->json();
    }

    // GET /bookings?yacht_id=...
    public function bookings(string $yachtId): array
    {
        return $this->client()
            ->get('/bookings', ['yacht_id' => $yachtId])
            ->throw()->json();
    }
}

Usage in a controller

$api = new CyaApi();

$results = $api->search(
    filters: ['yachtTypes' => ['motor'], 'guests' => [6, 16]],
    page: 1,
    limit: 10
);

$yacht = $api->yacht('670f8ec038d3078885154e8d', ['images', 'seasons']);
<?php
$base = 'https://api.centralyachtagent.com/public/api/v1';
$key  = 'YOUR_API_KEY';

// --- GET example ---
$ch = curl_init("$base/yachts/12345");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ["X-API-Key: $key"],
]);
$body   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status === 429) {
    // back off and retry later
}
$data = json_decode($body, true);

// --- POST example ---
$payload = json_encode([
    'page'    => 1,
    'limit'   => 10,
    'filters' => ['yachtTypes' => ['motor'], 'guests' => [6, 16]],
]);

$ch = curl_init("$base/yachts_search");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_HTTPHEADER     => [
        "X-API-Key: $key",
        'Content-Type: application/json',
    ],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
const BASE = "https://api.centralyachtagent.com/public/api/v1";
const API_KEY = process.env.CYA_API_KEY; // keep server-side

async function searchYachts() {
  const res = await fetch(`${BASE}/yachts_search`, {
    method: "POST",
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      page: 1,
      limit: 10,
      filters: { yachtTypes: ["motor"], guests: [6, 16] },
    }),
  });

  if (res.status === 429) {
    const retryAfter = Number(res.headers.get("Retry-After") || 60);
    throw new Error(`Rate limited. Retry after ${retryAfter}s.`);
  }
  if (!res.ok) throw new Error(`API error ${res.status}`);

  return res.json();
}

async function getYacht(id, include = []) {
  const qs = include.length ? `?include=${include.join(",")}` : "";
  const res = await fetch(`${BASE}/yachts_get/${id}${qs}`, {
    headers: { "X-API-Key": API_KEY },
  });
  return res.json();
}
import axios from "axios";

const cya = axios.create({
  baseURL: "https://api.centralyachtagent.com/public/api/v1",
  headers: { "X-API-Key": process.env.CYA_API_KEY },
  timeout: 15000,
});

// GET
const { data } = await cya.get("/yachts/12345");

// POST
const search = await cya.post("/yachts_search", {
  page: 1,
  limit: 10,
  filters: { yachtTypes: ["motor"], guests: [6, 16] },
});
6. Error reference
StatusMeaningWhat to do
400Bad request (invalid ID or filter value)Check the yacht/user ID format and filter types
403Missing / invalid API key, or IP/domain not allowedVerify the X-API-Key header and key restrictions
404Resource not found (or hidden no_api yacht)The yacht/user doesn't exist or isn't public
429Rate limit exceededBack off and retry after Retry-After seconds
500Internal server errorRetry with backoff; contact support if it persists
7. Quick checklist for integrators
  • ☑ Call the API from your own backend, not the browser.
  • ☑ Send X-API-Key on every request.
  • ☑ Cache responses; respect the 60 req/min limit.
  • ☑ Handle 429 with Retry-After backoff.
  • ☑ Use /operating_areas, /amenities, /watersports to resolve filter IDs.
  • ☑ Use ?include= on /yachts_get to keep payloads small.