Cruise Data Feed: Lines, Itineraries, Ports & Prices avatar

Cruise Data Feed: Lines, Itineraries, Ports & Prices

Pricing

$2.00 / 1,000 cruise results

Go to Apify Store
Cruise Data Feed: Lines, Itineraries, Ports & Prices

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.

Pricing

$2.00 / 1,000 cruise results

Rating

0.0

(0)

Developer

VulnV

VulnV

Maintained by Community

Actor stats

1

Bookmarked

15

Total users

8

Monthly active users

21 hours

Issues response

7 hours ago

Last modified

Categories

Share

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: portCount and itineraryPortsText. portCount was 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 reported 0 for sailings with no published itinerary. The record already carries port_count from the feed itself: the distinct ports the ship actually calls at, null when unknown. The two agreed on only 4,122 of the 55,643 records where port_count is populated, so anyone reading the wrong one has been getting wrong numbers with nothing to flag it. Switch to port_count. itineraryPortsText was 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

  1. Click Try for free / Start.
  2. Pick your filters - cruise line, region, departure port, dates, duration, price (or leave them all empty to download the latest sailings across every line).
  3. 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_count and sea_days for quick scanning.
  • 💲 Pricing - lead-in price, currency and price-per-night, plus the full per-cabin-class fares breakdown (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.

FieldTypeDescription
cruiseLinesarrayFilter 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.
regionstringMatch cruises whose region contains this text, e.g. Caribbean, Alaska, Mediterranean.
embarkPortstringMatch cruises whose departure port contains this text, e.g. Barcelona, Miami.
shipNamestringExact ship name, e.g. MSC World Europa.
departureDateFrom / departureDateTostring (YYYY-MM-DD)Departure date range.
minNights / maxNightsintegerCruise duration range, in nights.
minPrice / maxPriceintegerPrice range, in the cruise's own currency.
roundTripstringany, true (round trip only) or false (one way only).
cabinCategorystringKeep only sailings offering a fare in interior, oceanview, balcony, suite or other.
onlyWithBookingUrlbooleanKeep only sailings that carry a direct booking_url. Default false.
scrapedSincestring (YYYY-MM-DD)Keep only sailings last confirmed against their source on or after this date. Default: no limit.
includePastbooleanInclude sailings that have already departed. Default false.
sortstringdeparture_date (default), -departure_date, price, -price.
maxResultsintegerMaximum 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 sortBooking linkLast read before 2026-06-28
540.0%0.0%
2540.0%0.0%
10025.0%30.0%
50027.4%25.4%
whole catalogue26.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_url59.2%
Last confirmed against source on/after 2026-06-2873.2%
price_amount90.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

📬 Need help? Open the Issues tab on this Actor's page.