Cruise Data Feed: Lines, Itineraries, Ports & Prices
Pricing
$2.00 / 1,000 cruise results
Cruise Data Feed: Lines, Itineraries, Ports & Prices
Download structured cruise inventory from 60+ cruise lines (MSC, Disney, Royal Caribbean, Carnival, Viking and more) in one clean schema: ships, sailing dates, durations, ports of call, day-by-day itineraries and pricing. Unofficial; public data only.
Cruise Data Feed: All Lines, Itineraries, Ports & Prices
This cruise data scraper downloads clean, structured cruise inventory from 60+ cruise lines - MSC, Disney, Royal Caribbean, Carnival, Norwegian, Princess, Costa, Viking and more - in one normalized schema. Every record is a sailing with its ship, departure date, duration, embarkation/disembarkation ports, region, day-by-day itinerary and pricing.
Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by any cruise line or travel brand. Cruise line and ship names are trademarks of their respective owners and are used here only to describe the data the Actor returns. The data is public cruise inventory only - no personal data.
⚠️ Breaking output change, shipped in 1.4. Two fields the Actor added on top of the feed have been removed from every record:
portCountanditineraryPortsText.portCountwas not merely redundant, it was wrong - it counted itinerary rows, so days at sea and package flight/hotel days were counted as ports, and it reported0for sailings with no published itinerary. The record already carriesport_countfrom the feed itself: the distinct ports the ship actually calls at,nullwhen unknown. The two agreed on only 4,122 of the 55,643 records whereport_countis populated, so anyone reading the wrong one has been getting wrong numbers with nothing to flag it. Switch toport_count.itineraryPortsTextwas just" → ".join(d["port"] for d in itinerary if d.get("port"))- rebuild it in one line if you display it. Nothing else about the record changed.
🚀 How to use
- Click Try for free / Start.
- Pick your filters - cruise line, region, departure port, dates, duration, price (or leave them all empty to download the latest sailings across every line).
- Run the Actor and download the results as JSON, CSV, Excel, HTML or via API.
✨ Features
- 🛳️ 70 bookable cruise lines in one schema - ocean, river and expedition lines, all normalized to the same fields. No per-site scraping, no layout drift. The dropdown is regenerated daily from the live catalogue, so a newly added brand is selectable without waiting for anyone to notice. Ship owners (Scylla, Mystic, StarDream) are deliberately not offered as cruise lines - nobody books under those names; pick the retail brand that carries their sailings (Tauck / Riviera Travel, nicko cruises, Resorts World Cruises).
- 🔎 Rich filtering - cruise line, region/destination, departure port, ship, departure date range, number of nights, price range and round-trip vs. one-way.
- 🗺️ Day-by-day itineraries - every record includes the ordered list of ports of
call (with sequence, port name, day and embark/disembark flags), plus
port_countandsea_daysfor quick scanning. - 💲 Pricing - lead-in price, currency and price-per-night, plus the full
per-cabin-class
faresbreakdown (interior/oceanview/balcony/suite) with availability. - ⚡ Fast & reliable - talks to a single JSON API, so runs are lightweight (no headless browser) and the schema stays stable run-to-run.
🧭 Input
All fields are optional. Run with no input to download the latest sailings across all lines.
| Field | Type | Description |
|---|---|---|
cruiseLines | array | Filter by one or more cruise lines (e.g. MSC Cruises, Royal Caribbean). Empty = all lines. Pick several and the result budget is split evenly between them - see below. |
region | string | Match cruises whose region contains this text, e.g. Caribbean, Alaska, Mediterranean. |
embarkPort | string | Match cruises whose departure port contains this text, e.g. Barcelona, Miami. |
shipName | string | Exact ship name, e.g. MSC World Europa. |
departureDateFrom / departureDateTo | string (YYYY-MM-DD) | Departure date range. |
minNights / maxNights | integer | Cruise duration range, in nights. |
minPrice / maxPrice | integer | Price range, in the cruise's own currency. |
roundTrip | string | any, true (round trip only) or false (one way only). |
cabinCategory | string | Keep only sailings offering a fare in interior, oceanview, balcony, suite or other. |
onlyWithBookingUrl | boolean | Keep only sailings that carry a direct booking_url. Default false. |
scrapedSince | string (YYYY-MM-DD) | Keep only sailings last confirmed against their source on or after this date. Default: no limit. |
includePast | boolean | Include sailings that have already departed. Default false. |
sort | string | departure_date (default), -departure_date, price, -price. |
maxResults | integer | Maximum number of records to store (default 100). |
Example input
{"cruiseLines": ["MSC Cruises", "Royal Caribbean"],"region": "Caribbean","departureDateFrom": "2026-10-01","departureDateTo": "2026-12-31","minNights": 5,"sort": "price","maxResults": 100}
Asking for several cruise lines
The API filters one cruise line per query, so the Actor issues a request per line and
shares maxResults evenly between them. Three lines and maxResults: 40 returns
14 / 13 / 13, not 40 from whichever line you listed first. If a line runs out of
matching sailings its unused share goes back to the others rather than being lost, so
you still receive the full maxResults wherever the catalogue can supply it. Sort
order within each line is whatever you asked for; the split only decides how much of
each line is read.
Sort order, and what the first page actually contains
sort defaults to departure_date - soonest departure first, with ties broken by
freshness, so among the many sailings sharing a departure date you get the
most-recently-confirmed ones first.
That tiebreak matters more than it sounds. Departure dates are heavily tied, so a small run reads one date's worth of rows; when ties resolved by insertion order the first 25 rows were entirely one bulk-imported source with no booking links at all. Breaking ties by freshness instead puts the best rows at the front:
| First N rows of the default sort | Booking link | Last read before 2026-06-28 |
|---|---|---|
| 5 | 40.0% | 0.0% |
| 25 | 40.0% | 0.0% |
| 100 | 25.0% | 30.0% |
| 500 | 27.4% | 25.4% |
| whole catalogue | 26.0% | 26.8% |
Imminent departures are also not lower quality than the catalogue as a whole: rows departing in the next 7 days are 25.0% stale against 26.8% catalogue-wide, and 28.1% carry a booking link against 26.0%.
Still, a short run is a slice, not a random sample. If every row has to be bookable or
recently confirmed, say so with onlyWithBookingUrl or scrapedSince rather than
relying on the sort to deliver it.
📦 Output
Each dataset item is one sailing. Example:
{"id": "cru_7b3e1f9a2c4d6058","cruise_line": "MSC Cruises","ship_name": "MSC World Europa","title": "7 nights, round trip from Barcelona","departure_date": "2026-10-18","return_date": "2026-10-25","duration_days": 7,"nights": 7,"round_trip": true,"embark_port": "Barcelona, Spain","disembark_port": "Barcelona, Spain","region": "Western Mediterranean","sea_days": 2,"port_count": 4,"price_amount": 899,"price_currency": "EUR","price_per_night": 128.43,"taxes_and_fees": 189.5,"obc_amount": 50,"sold_out": false,"booking_url": "https://www.msccruises.com/en-gb/...","fares": [{ "cabin_class": "Interior", "price_amount": 899, "price_currency": "EUR", "price_per_night": 128.43, "available": true },{ "cabin_class": "Balcony", "price_amount": 1249, "price_currency": "EUR", "price_per_night": 178.43, "available": true }],"itinerary": [{ "seq": 1, "port": "Barcelona, Spain", "date_raw": "18 Oct", "is_embark": true, "is_disembark": false },{ "seq": 2, "port": "Marseille, France", "date_raw": "19 Oct", "is_embark": false, "is_disembark": false }],"scraped_at": "2026-06-27T21:14:08.493837"}
port_count is the number of distinct ports the ship actually calls at - days at
sea and package flight/land/hotel days are not counted, and it is null where the
sailing publishes no itinerary. It is not len(itinerary); the two differ on most
sailings.
Records also carry ship_code, destination_code, taxes_and_fees, taxes_currency,
obc_amount, sold_out and detail_url when the source provides them
(some fields are partial across lines).
The dataset has two views: Overview (flat table - line, ship, dates, ports, price,
taxes, onboard credit, sold-out) and Itineraries (the full day-by-day itinerary array).
📊 Coverage and freshness
Not every field is populated on every sailing, because not every source publishes it. Rather than let you find that out in your own data, here are the numbers across the 81,519 upcoming sailings served at the time of writing:
| Coverage | |
|---|---|
booking_url (a direct link you can send a customer to) | 26.0% |
detail_url | 59.2% |
| Last confirmed against source on/after 2026-06-28 | 73.2% |
price_amount | 90.2% |
port_count (itinerary published) | 68.2% |
Coverage varies enormously by cruise line - some lines publish a booking link on every sailing and others on none - so a filtered run looks nothing like these averages. Two inputs let you require what you need instead of hoping for it:
onlyWithBookingUrl- only sailings with a direct booking link.scrapedSince- only sailings last confirmed on or after a date you choose.
Both are off by default: a plain run returns the whole catalogue and hides nothing.
Turning one on does not shorten your results - the filter is applied by the feed
itself, so the request comes back already narrowed and maxResults still means the
number of records you receive, drawn from a smaller pool. Every run also logs how many
of the records it stored carry a booking link and how many were confirmed in the last
30 days.
💡 Use cases
- Build a cruise comparison, search or alerting tool across many lines at once.
- Monitor sailing availability, durations and lead-in prices for specific routes.
- Analyse itineraries, ports of call and seasonality across the whole industry.
- Feed normalized cruise data into a travel agency, affiliate or OTA site.
💳 Pricing
This Actor is monetized on a pay-per-result + usage basis: you pay a small fee per cruise record returned, plus the Apify platform usage your run consumes. Runs are lightweight - a single API, no headless browser - so usage stays low. New users can try it on the Apify free tier (capped sample). See the Pricing tab on the Actor's page for the current rate.
❓ FAQ
Where does the data come from? The feed aggregates publicly available cruise inventory from across the industry and normalizes it into one schema. Use the data responsibly and in line with applicable laws.
How current is it?
The feed is refreshed continuously and re-priced on a recurring schedule. Each record
includes a scraped_at timestamp.
How do I scrape only a few results to test?
Set maxResults to a small number (e.g. 5) and add a filter such as a single cruise
line or region.
Is it official? No. It is an independent, unofficial Actor and is not affiliated with any cruise line. Brand names are used only to describe the data.
🛠️ Our Other Scrapers
- MSC Cruises Scraper - Live MSC sailings with full itineraries and the cabin price matrix.
- Booking.com Cruises Scraper - Cruise deals, prices and itineraries from Booking.com.
- Disney Cruises Scraper - Disney Cruise Line itineraries and pricing.
- Amazon Product Scraper - Amazon product data, pricing and reviews.
📬 Need help? Open the Issues tab on this Actor's page.