Webhooks
Business plans and above can have the change feed pushed to an HTTPS endpoint. Add one on the webhooks page, with optional ship, line and port filters. After each weekly load we send one delivery per endpoint, paged at 1,000 events.
Delivery
POST https://your.app/hooks/cruise
Content-Type: application/json
X-CI-Signature: t=1790900000,v1=9f2c...e1
{
"id": 4812,
"type": "changes.batch",
"detected_on": "2026-09-28",
"previous_on": "2026-09-21",
"page": 1,
"pages": 3,
"events": [
{
"id": 90112,
"kind": "price_drop",
"...": "..."
}
]
}
The signature is an HMAC-SHA256, hex encoded, of "<t>.<raw body>" using your endpoint's secret. Verify it against the raw bytes before parsing, and reject timestamps more than five minutes old.
Verification, Node
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const mac = createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest('hex');
const a = Buffer.from(mac), b = Buffer.from(parts.v1 ?? '');
return a.length === b.length && timingSafeEqual(a, b);
}
Reply with any 2xx within 10 seconds. Anything else is retried after 5 minutes, 30 minutes, 2, 6, 12 and 24 hours, then 24 hours again, for 8 attempts in all, and then marked failed. Twenty failed deliveries in a row disable the endpoint. We only deliver to public HTTPS addresses.
Reference data
GET/v1/linesNo key
List cruise lines
Every cruise line, alphabetical, with how many ships and upcoming sailings each has.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
Example
curl 'https://cruise-itinerary.com/v1/lines'
Response 200
{
"data": [
{
"id": "amawaterways",
"name": "AmaWaterways",
"ship_count": 38,
"sailing_count": 3199
},
{
"id": "american-cruise-lines",
"name": "American Cruise Lines",
"ship_count": 23,
"sailing_count": 696
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 45,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/lines/{id}No key
Get a cruise line
One line by slug.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Line slug. |
Example
curl 'https://cruise-itinerary.com/v1/lines/holland-america-line'
Response 200
{
"data": {
"id": "holland-america-line",
"name": "Holland America Line",
"ship_count": 13,
"sailing_count": 1234
},
"meta": {
"as_of": "2026-09-28"
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/shipsNo key
List ships
Ships, alphabetical. q matches part of the name or an exact IMO number.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
line | query | string | Only ships of this line (slug). |
q | query | string | Name contains, or exact IMO. |
Example
curl 'https://cruise-itinerary.com/v1/ships'
Response 200
{
"data": [
{
"id": "eurodam",
"name": "Eurodam",
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"imo": "9378448",
"mmsi": "245206000",
"passengers": 2104,
"crew": 929,
"gross_tonnage": 86273,
"decks": 11,
"length_ft": 936,
"width_ft": 106,
"maiden_voyage": "2008-07-05",
"launched": "2008-01-01",
"refurbished": null,
"adults_only": false
},
{
"id": "koningsdam",
"name": "Koningsdam",
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"imo": "9692557",
"mmsi": "244830547",
"passengers": 2650,
"crew": 1025,
"gross_tonnage": 99500,
"decks": 12,
"length_ft": 935,
"width_ft": 115,
"maiden_voyage": "2016-04-04",
"launched": null,
"refurbished": null,
"adults_only": false
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 13,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/ships/{id}No key
Get a ship
One ship by slug.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Ship slug. |
Example
curl 'https://cruise-itinerary.com/v1/ships/eurodam'
Response 200
{
"data": {
"id": "eurodam",
"name": "Eurodam",
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"imo": "9378448",
"mmsi": "245206000",
"passengers": 2104,
"crew": 929,
"gross_tonnage": 86273,
"decks": 11,
"length_ft": 936,
"width_ft": 106,
"maiden_voyage": "2008-07-05",
"launched": "2008-01-01",
"refurbished": null,
"adults_only": false
},
"meta": {
"as_of": "2026-09-28"
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/portsNo key
List ports
Ports and scenic stops. Use has_calls=true for ports a ship is scheduled to visit. For matching free text to a port, use /v1/resolve.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
q | query | string | Name or alias contains. |
destination | query | string | Destination slug. |
region | query | string | Region slug. |
has_calls | query | string | true: only ports with scheduled calls; false: only those without. One of: true, false. |
kind | query | string | port or scenic. One of: port, scenic. |
Example
curl 'https://cruise-itinerary.com/v1/ports'
Response 200
{
"data": [
{
"id": "miami",
"name": "Miami",
"kind": "port",
"latitude": 25.7890972,
"longitude": -80.2040435,
"destination": {
"id": "miami",
"name": "Miami"
},
"region": {
"id": "united-states",
"name": "United States"
},
"aliases": [],
"upcoming_calls": 2424
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 1,
"has_more": false
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/ports/{id}No key
Get a port
One port by slug. The numeric id of a port that was merged into another also works and returns the surviving port, with meta.resolved_from set to the id you sent.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Port slug, or the numeric id of a merged legacy port. |
Example
curl 'https://cruise-itinerary.com/v1/ports/miami'
Response 200
{
"data": {
"id": "miami",
"name": "Miami",
"kind": "port",
"latitude": 25.7890972,
"longitude": -80.2040435,
"destination": {
"id": "miami",
"name": "Miami"
},
"region": {
"id": "united-states",
"name": "United States"
},
"aliases": [],
"upcoming_calls": 2424
},
"meta": {
"as_of": "2026-09-28"
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/destinationsNo key
List destinations
Destinations with their region and a representative coordinate.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
Example
curl 'https://cruise-itinerary.com/v1/destinations'
Response 200
{
"data": [
{
"id": "abu-dhabi",
"name": "Abu Dhabi",
"region": {
"id": "middle-east",
"name": "Middle East"
},
"latitude": 24.453884,
"longitude": 54.3773438
},
{
"id": "acapulco",
"name": "Acapulco",
"region": {
"id": "mexico-region",
"name": "Mexico region"
},
"latitude": 16.863611,
"longitude": -99.8825
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 379,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/regionsNo key
List regions
The 18 top-level regions.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
Example
curl 'https://cruise-itinerary.com/v1/regions'
Response 200
{
"data": [
{
"id": "africa",
"name": "Africa"
},
{
"id": "alaska-region",
"name": "Alaska region"
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 18,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
Resolver
GET/v1/resolveNo key
Resolve a name
Match free text such as "Port Canaveral, Florida" or "Royal Caribbean Symphony of the Seas" to ports, ships and lines. Returns up to five candidates, best first. Confidence is 1.0 for an exact name, about 0.9 to 0.97 after normalising accents, punctuation, country suffixes and aliases, and 0.85 or less for approximate matches. For many names at once, use the POST form.
Parameters
| Name | In | Type | Notes |
|---|
qrequired | query | string | The text to match, up to 200 characters. |
type | query | string | Restrict to one kind of entity. One of: port, ship, line. |
Example
curl 'https://cruise-itinerary.com/v1/resolve?q=Port%20Canaveral%2C%20Florida'
Response 200
{
"data": [
{
"type": "port",
"id": "port-canaveral",
"name": "Port Canaveral",
"confidence": 0.9,
"match": "normalized"
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 1,
"total": 1,
"has_more": false
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
POST/v1/resolveFree
Resolve names in bulk
Up to 100 items per request. The response holds one entry per item, in order, each with its own candidate list.
Needs a key on the Free plan or above. Lower plans receive 403 plan_required.
Request body
{
"items": [
{
"q": "Port Canaveral",
"type": "port"
},
{
"q": "Allure of the seas"
}
]
}
Example
curl -X POST 'https://cruise-itinerary.com/v1/resolve' \
-H "Authorization: Bearer $CI_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"items":[{"q":"Port Canaveral","type":"port"},{"q":"Allure of the seas"}]}'
Response 200
{
"data": [
{
"q": "Port Canaveral",
"type": "port",
"candidates": [
{
"type": "port",
"id": "port-canaveral",
"name": "Port Canaveral",
"confidence": 1,
"match": "exact"
}
]
},
{
"q": "Allure of the seas",
"type": null,
"candidates": [
{
"type": "ship",
"id": "allure-of-the-seas",
"name": "Allure of the Seas",
"confidence": 1,
"match": "exact"
}
]
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 2,
"has_more": false
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
Sailings
GET/v1/sailingsDeveloper
Search sailings
Upcoming sailings with their latest fares. Fares are weekly snapshots from the inventory feed, not live bookable prices. port matches sailings that call at a port (itinerary days), embark_port the departure port. from and to bound the departure date.
Needs a key on the Developer plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
ship | query | string | Ship slug. |
line | query | string | Line slug. |
port | query | string | Port slug; sailings that call there. |
embark_port | query | string | Departure port slug. |
region | query | string | Region slug. |
from | query | string (date) | Earliest departure date. |
to | query | string (date) | Latest departure date. |
min_nights | query | integer | Minimum length. |
max_nights | query | integer | Maximum length. |
cabin | query | string | Restrict to sailings with a fare in this cabin; also the cabin max_fare and sort=fare use. One of: inside, outside, balcony, suite. |
max_fare | query | integer | Highest fare in USD (in cabin, or in any cabin when cabin is absent). |
sort | query | string | departure (default) or fare, lowest first. One of: departure, fare. |
Example
curl 'https://cruise-itinerary.com/v1/sailings' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": [
{
"id": 14026605,
"cruise": {
"id": "1-night-pacific-northwest-eurodam-da8a2f",
"title": "1 Night Pacific Northwest"
},
"ship": {
"id": "eurodam",
"name": "Eurodam"
},
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"departure_date": "2026-10-03",
"return_date": "2026-10-04",
"nights": 1,
"embark_port": {
"id": "seattle",
"name": "Seattle"
},
"disembark_port": {
"id": "vancouver",
"name": "Vancouver"
},
"fares": {
"inside": null,
"outside": 129,
"balcony": 129,
"suite": 239
},
"currency": "USD",
"fares_changed_on": "2026-09-14",
"observed_on": "2026-09-28"
},
{
"id": 14324436,
"cruise": {
"id": "21-night-panama-canal-eurodam-19a3f5",
"title": "21 Night Panama Canal"
},
"ship": {
"id": "eurodam",
"name": "Eurodam"
},
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"departure_date": "2026-10-03",
"return_date": "2026-10-24",
"nights": 21,
"embark_port": {
"id": "seattle",
"name": "Seattle"
},
"disembark_port": {
"id": "ft-lauderdale",
"name": "Ft. Lauderdale"
},
"fares": {
"inside": null,
"outside": null,
"balcony": null,
"suite": null
},
"currency": "USD",
"fares_changed_on": "2026-09-07",
"observed_on": "2026-09-28"
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 113,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/sailings/{id}Developer
Get a sailing
One sailing with its day-by-day itinerary. Embark and disembark days are exact; days in between are estimated and labelled so.
Needs a key on the Developer plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Sailing id. |
Example
curl 'https://cruise-itinerary.com/v1/sailings/14026605' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": {
"id": 14026605,
"cruise": {
"id": "1-night-pacific-northwest-eurodam-da8a2f",
"title": "1 Night Pacific Northwest"
},
"ship": {
"id": "eurodam",
"name": "Eurodam"
},
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"departure_date": "2026-10-03",
"return_date": "2026-10-04",
"nights": 1,
"embark_port": {
"id": "seattle",
"name": "Seattle"
},
"disembark_port": {
"id": "vancouver",
"name": "Vancouver"
},
"fares": {
"inside": null,
"outside": 129,
"balcony": 129,
"suite": 239
},
"currency": "USD",
"fares_changed_on": "2026-09-14",
"observed_on": "2026-09-28",
"itinerary": [
{
"day_offset": 0,
"date": "2026-10-03",
"port": {
"id": "seattle",
"name": "Seattle",
"latitude": 47.6062095,
"longitude": -122.3320708
},
"kind": "embark",
"day_confidence": "exact"
},
{
"day_offset": 1,
"date": "2026-10-04",
"port": {
"id": "vancouver",
"name": "Vancouver",
"latitude": 49.261226,
"longitude": -123.1139268
},
"kind": "disembark",
"day_confidence": "exact"
}
]
},
"meta": {
"as_of": "2026-09-28"
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/sailings/{id}/pricesBusiness
Fare history of a sailing
Every weekly snapshot of the sailing's four fares, oldest first. History starts on 2026-09-06 and continues after the sailing departs.
Needs a key on the Business plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Sailing id. |
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
Example
curl 'https://cruise-itinerary.com/v1/sailings/14026605/prices' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": [
{
"observed_on": "2026-09-06",
"fares": {
"inside": 129,
"outside": 129,
"balcony": 129,
"suite": 239
},
"currency": "USD"
},
{
"observed_on": "2026-09-07",
"fares": {
"inside": 129,
"outside": 129,
"balcony": 129,
"suite": 239
},
"currency": "USD"
},
{
"observed_on": "2026-09-14",
"fares": {
"inside": null,
"outside": 129,
"balcony": 129,
"suite": 239
},
"currency": "USD"
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 3,
"total": 5,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/cruises/{id}Developer
Get a cruise
A cruise is the product (title, ship, ordered stops); a sailing is one departure of it. Cruise slugs are permanent.
Needs a key on the Developer plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Cruise slug. |
Example
curl 'https://cruise-itinerary.com/v1/cruises/1-night-pacific-northwest-eurodam-da8a2f' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": {
"id": "1-night-pacific-northwest-eurodam-da8a2f",
"title": "1 Night Pacific Northwest",
"ship": {
"id": "eurodam",
"name": "Eurodam"
},
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"nights": 1,
"embark_port": {
"id": "seattle",
"name": "Seattle"
},
"disembark_port": {
"id": "vancouver",
"name": "Vancouver"
},
"stops": [
{
"seq": 1,
"port": {
"id": "seattle",
"name": "Seattle"
}
},
{
"seq": 2,
"port": {
"id": "vancouver",
"name": "Vancouver"
}
}
],
"next_departure": "2026-10-03",
"sailing_count": 2
},
"meta": {
"as_of": "2026-09-28"
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
Ships and ports
GET/v1/ships/{id}/scheduleDeveloper
Ship schedule
Where a ship is on each day: one row per stop, with sea days. When sailings overlap, the shortest one that covers a day owns it. Window limit: 400 days; default 90 days from today.
Needs a key on the Developer plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Ship slug. |
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
from | query | string (date) | First day (default today). |
to | query | string (date) | Last day. |
Example
curl 'https://cruise-itinerary.com/v1/ships/eurodam/schedule' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": [
{
"date": "2026-10-03",
"seq": 0,
"kind": "embark",
"port": {
"id": "seattle",
"name": "Seattle",
"latitude": 47.6062095,
"longitude": -122.3320708
},
"day_confidence": "exact",
"sailing_id": 14026605
},
{
"date": "2026-10-04",
"seq": 0,
"kind": "turnaround",
"port": {
"id": "vancouver",
"name": "Vancouver",
"latitude": 49.261226,
"longitude": -123.1139268
},
"day_confidence": "exact",
"sailing_id": 14317445
},
{
"date": "2026-10-05",
"seq": 0,
"kind": "sea",
"port": null,
"day_confidence": "estimated",
"sailing_id": 14317445
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 3,
"total": 29,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/ports/{id}/calendarBusiness
Port traffic calendar
Ships, lower berths and turnarounds per day. Only days with at least one ship appear; a missing day has none. Window limit: 730 days; default 90 days from today. Berths count lower-berth capacity; real headcount runs 10 to 20 percent higher.
Needs a key on the Business plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Port slug (or merged legacy id). |
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
from | query | string (date) | First day (default today). |
to | query | string (date) | Last day. |
Example
curl 'https://cruise-itinerary.com/v1/ports/miami/calendar' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": [
{
"date": "2026-11-01",
"ships": 7,
"berths": 26885,
"ships_no_capacity": 0,
"embark_ships": 6,
"disembark_ships": 7,
"turnaround_ships": 6,
"estimated_ships": 0,
"busy_pct": 90
},
{
"date": "2026-11-02",
"ships": 3,
"berths": 12560,
"ships_no_capacity": 0,
"embark_ships": 3,
"disembark_ships": 3,
"turnaround_ships": 3,
"estimated_ships": 0,
"busy_pct": 39
},
{
"date": "2026-11-05",
"ships": 4,
"berths": 11456,
"ships_no_capacity": 0,
"embark_ships": 4,
"disembark_ships": 4,
"turnaround_ships": 4,
"estimated_ships": 0,
"busy_pct": 30
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 3,
"total": 27,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/ports/{id}/days/{date}Business
Port day detail
The counts for one day plus the ships behind them. A day with no ships returns zero counts and an empty calls list.
Needs a key on the Business plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
idrequired | path | string | Port slug (or merged legacy id). |
daterequired | path | string | YYYY-MM-DD. |
Example
curl 'https://cruise-itinerary.com/v1/ports/miami/days/2026-11-14' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": {
"date": "2026-11-14",
"ships": 7,
"berths": 23928,
"ships_no_capacity": 1,
"embark_ships": 7,
"disembark_ships": 7,
"turnaround_ships": 7,
"estimated_ships": 0,
"busy_pct": 85,
"calls": [
{
"ship": {
"id": "carnival-magic",
"name": "Carnival Magic",
"passengers": 3690
},
"line": {
"id": "carnival-cruise-line",
"name": "Carnival Cruise Line"
},
"kind": "turnaround",
"day_confidence": "exact",
"sailing_id": 14252373
},
{
"ship": {
"id": "carnival-sunrise",
"name": "Carnival Sunrise",
"passengers": 2984
},
"line": {
"id": "carnival-cruise-line",
"name": "Carnival Cruise Line"
},
"kind": "turnaround",
"day_confidence": "exact",
"sailing_id": 14252960
},
{
"ship": {
"id": "msc-world-america",
"name": "MSC World America",
"passengers": 5240
},
"line": {
"id": "msc-cruises",
"name": "MSC Cruises"
},
"kind": "turnaround",
"day_confidence": "exact",
"sailing_id": 14290891
},
{
"ship": {
"id": "norwegian-luna",
"name": "Norwegian Luna",
"passengers": null
},
"line": {
"id": "norwegian-cruise-line",
"name": "Norwegian Cruise Line"
},
"kind": "turnaround",
"day_confidence": "exact",
"sailing_id": 14221900
},
{
"ship": {
"id": "freedom-of-the-seas",
"name": "Freedom of the Seas",
"passengers": 3634
},
"line": {
"id": "royal-caribbean",
"name": "Royal Caribbean"
},
"kind": "turnaround",
"day_confidence": "exact",
"sailing_id": 14240653
},
{
"ship": {
"id": "icon-of-the-seas",
"name": "Icon of the Seas",
"passengers": 5610
},
"line": {
"id": "royal-caribbean",
"name": "Royal Caribbean"
},
"kind": "turnaround",
"day_confidence": "exact",
"sailing_id": 14089008
},
{
"ship": {
"id": "scarlet-lady",
"name": "Scarlet Lady",
"passengers": 2770
},
"line": {
"id": "virgin-voyages",
"name": "Virgin Voyages"
},
"kind": "turnaround",
"day_confidence": "exact",
"sailing_id": 14199956
}
]
},
"meta": {
"as_of": "2026-09-28"
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
Changes
GET/v1/changesBusiness
Change feed
What changed between consecutive weekly loads: fare moves per cabin, cabins appearing or vanishing, sailings added or removed, itineraries swapped. Newest load first. kind takes a comma-separated list.
Needs a key on the Business plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
since | query | string (date) | Earliest detected_on. |
until | query | string (date) | Latest detected_on. |
kind | query | string | Comma-separated: price_drop, price_rise, cabin_unavailable, cabin_available, sailing_added, sailing_removed, itinerary_changed. |
ship | query | string | Ship slug. |
line | query | string | Line slug. |
port | query | string | Port slug; sailings that call there. |
Example
curl 'https://cruise-itinerary.com/v1/changes' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": [
{
"id": 254254,
"detected_on": "2026-09-28",
"previous_on": "2026-09-21",
"kind": "price_drop",
"sailing_id": 14038925,
"cruise_id": "7-night-eastern-caribbean-holiday-amber-cove-and-bahamas-nieuw-amsterdam-ecb222",
"previous_cruise_id": null,
"ship": {
"id": "nieuw-amsterdam",
"name": "Nieuw Amsterdam"
},
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"departure_date": "2026-12-20",
"cabin": "inside",
"old_value": 749,
"new_value": 699,
"currency": "USD"
},
{
"id": 254255,
"detected_on": "2026-09-28",
"previous_on": "2026-09-21",
"kind": "price_drop",
"sailing_id": 14038929,
"cruise_id": "9-night-eastern-caribbean-st-maarten-antigua-and-bahamas-nieuw-amsterdam-44b3c2",
"previous_cruise_id": null,
"ship": {
"id": "nieuw-amsterdam",
"name": "Nieuw Amsterdam"
},
"line": {
"id": "holland-america-line",
"name": "Holland America Line"
},
"departure_date": "2026-12-11",
"cabin": "inside",
"old_value": 899,
"new_value": 849,
"currency": "USD"
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 2,
"total": 869,
"has_more": true
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
Price index
GET/v1/price-index/latestNo key
Latest price index
The all-lines Cruise Price Index plus the eight largest lines, for cabin any and segment all, over the last 12 loads. Ordered all lines first, then by line name, oldest load first. The series starts on 2026-09-06 at 100.
Example
curl 'https://cruise-itinerary.com/v1/price-index/latest'
Response 200
{
"data": [
{
"observed_on": "2026-09-06",
"previous_on": null,
"line": null,
"cabin": "any",
"segment": "all",
"matched": 54299,
"index_value": 100,
"mean_log_change": null,
"pct_cut": null,
"pct_raised": null,
"pct_unavailable": null,
"avg_per_diem": null
},
{
"observed_on": "2026-09-07",
"previous_on": "2026-09-06",
"line": null,
"cabin": "any",
"segment": "all",
"matched": 54299,
"index_value": 99.9036,
"mean_log_change": -0.000964,
"pct_cut": 0.62,
"pct_raised": 0.52,
"pct_unavailable": 0.18,
"avg_per_diem": 464.59
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 45,
"total": 45,
"has_more": false
}
}
Also returns: 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/price-indexBusiness
Price index series
The full series for one line, cabin and segment, oldest first. line is a slug, or omitted (or all) for all lines. Rows exist only where at least 30 sailings matched.
Needs a key on the Business plan or above. Lower plans receive 403 plan_required.
Parameters
| Name | In | Type | Notes |
|---|
page | query | integer | Page number, from 1. Pages deeper than 100,000 rows are refused. Default: 1. Range: at least 1. |
per_page | query | integer | Rows per page, 1 to 200. Default: 50. Range: 1 to 200. |
line | query | string | Line slug or all. |
cabin | query | string | Cabin class (default any). One of: inside, outside, balcony, suite, any. |
segment | query | string | all (default), w000_090, w091_180, w181_365, w366_plus, or a sail quarter like q2027_1. |
from | query | string (date) | Earliest observed_on. |
to | query | string (date) | Latest observed_on. |
Example
curl 'https://cruise-itinerary.com/v1/price-index' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": [
{
"observed_on": "2026-09-06",
"previous_on": null,
"line": {
"id": "carnival-cruise-line",
"name": "Carnival Cruise Line"
},
"cabin": "balcony",
"segment": "w000_090",
"matched": 300,
"index_value": 100,
"mean_log_change": null,
"pct_cut": null,
"pct_raised": null,
"pct_unavailable": null,
"avg_per_diem": null
},
{
"observed_on": "2026-09-07",
"previous_on": "2026-09-06",
"line": {
"id": "carnival-cruise-line",
"name": "Carnival Cruise Line"
},
"cabin": "balcony",
"segment": "w000_090",
"matched": 300,
"index_value": 100.3153,
"mean_log_change": 0.003148,
"pct_cut": 3,
"pct_raised": 7,
"pct_unavailable": 2.28,
"avg_per_diem": 150.02
},
{
"observed_on": "2026-09-14",
"previous_on": "2026-09-07",
"line": {
"id": "carnival-cruise-line",
"name": "Carnival Cruise Line"
},
"cabin": "balcony",
"segment": "w000_090",
"matched": 255,
"index_value": 100.3892,
"mean_log_change": 0.000737,
"pct_cut": 51.76,
"pct_raised": 42.35,
"pct_unavailable": 13.56,
"avg_per_diem": 148.1
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 50,
"total": 5,
"has_more": false
}
}
Also returns: 400 Bad request: an unknown or malformed parameter. The message names it.; 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
Bulk exports
GET/v1/exportsPlatform
List weekly exports
The last eight weekly bulk files, as gzipped CSV, with row counts and SHA-256 checksums. Column names match the API and ids are slugs, so files join to the identity-* files. access: anon files download without a key.
Needs a key on the Platform plan or above. Lower plans receive 403 plan_required.
Example
curl 'https://cruise-itinerary.com/v1/exports' \
-H "Authorization: Bearer $CI_API_KEY"
Response 200
{
"data": [
{
"date": "2026-09-28",
"files": [
{
"name": "identity-lines.csv.gz",
"bytes": 901,
"rows": 45,
"sha256": "88aff6f5f821b9a1aa20d69906c003c10a61971621965745846e9ca727974916",
"access": "anon",
"url": "https://cruise-itinerary.com/v1/exports/2026-09-28/identity-lines.csv.gz"
},
{
"name": "identity-ships.csv.gz",
"bytes": 20361,
"rows": 674,
"sha256": "057b373853a8b2aaee3e54dfdddd02f92e435d307aece09ba6f80aad06011ce6",
"access": "anon",
"url": "https://cruise-itinerary.com/v1/exports/2026-09-28/identity-ships.csv.gz"
}
]
}
],
"meta": {
"as_of": "2026-09-28",
"page": 1,
"per_page": 1,
"total": 1,
"has_more": false
}
}
Also returns: 401 The key is malformed, unknown or revoked. A bad key is never treated as anonymous.; 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.
GET/v1/exports/{date}/{file}No key
Download an export file
Streams one gzipped CSV. date may be latest. Files named identity-* (lines, ships, ports, destinations, regions) need no key; the rest need Platform.
Parameters
| Name | In | Type | Notes |
|---|
daterequired | path | string | Export date from the listing, or latest. |
filerequired | path | string | File name from the listing. |
Example
curl 'https://cruise-itinerary.com/v1/exports/latest/identity-lines.csv.gz'
Also returns: 403 The plan does not include this endpoint (plan_required, with required_plan), or the account is suspended (account_suspended).; 404 No such entity or endpoint.; 429 Per-minute rate limit (rate_limited, with Retry-After) or monthly quota (quota_exceeded) reached.; 500 Internal error. Safe to retry.. See errors.