Traveloka Hotel Scraper - SEA Hotel Prices & Ratings avatar

Traveloka Hotel Scraper - SEA Hotel Prices & Ratings

Pricing

from $1.80 / 1,000 hotel results

Go to Apify Store
Traveloka Hotel Scraper - SEA Hotel Prices & Ratings

Traveloka Hotel Scraper - SEA Hotel Prices & Ratings

Scrape Traveloka hotels across Singapore, Malaysia, Indonesia, Thailand, Vietnam and the Philippines: nightly prices, star ratings, guest scores, coordinates and photos by city. Paste search or hotel links, and monitor price changes with recurring updates.

Pricing

from $1.80 / 1,000 hotel results

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

1

Bookmarked

4

Total users

2

Monthly active users

11 days ago

Last modified

Categories

Security

Limited permissions

Share

Traveloka Scraper: Hotel Prices, Ratings & Availability Across Southeast Asia

Traveloka Scraper turns Traveloka, a major Southeast Asian travel platform, into structured hotel data. Search any city or area across its Singapore, Malaysia, Indonesia, Thailand, Vietnam, Philippines and Australia storefronts, or paste Traveloka search and hotel links, and get nightly prices in the local currency, star ratings, guest review scores, coordinates, photos and facilities for every hotel. Turn on recurring monitoring to track price and rating changes over time, then export to JSON, CSV or Excel, or pull results straight into your app through the API.

Why This Scraper?

  • 30+ fields per hotel: nightly price, star rating, guest score out of 10, review count, region, coordinates, photos and facilities.
  • Seven storefronts (Singapore, Malaysia, Indonesia, Thailand, Vietnam, Philippines, Australia) with per-market currency handling.
  • Two ways to find hotels: keyword search by city or area, or paste Traveloka search links (with spec=) and hotel detail links.
  • Optional enrichment, priced per hotel: facility codes and labels, feature highlights, extra photos and a backfilled guest rating merged straight from the site's own data feed, no extra page loads for search results. A separate guest-review pull is also available.
  • Recurring updates: incremental mode classifies every hotel NEW, UPDATED, UNCHANGED or EXPIRED between scheduled runs, so you only pay for what changed.
  • Client-side filters and sorting: price range, minimum star rating and minimum guest score, plus popularity or price-based sort order.
  • Fail-loud runs: when the site refuses to serve, the run says so instead of returning an empty result set.

Use Cases

  • Travel meta-search and price-comparison sites: pull nightly Traveloka rates across cities to power search widgets or price-alert tools.
  • Hotel revenue teams and market researchers: track competitor pricing, star ratings and guest scores across Traveloka's storefronts.
  • OTA and travel-tech platforms: feed structured hotel inventory (facilities, photos, coordinates) into an internal booking or aggregation product.
  • Travel content and city-guide sites: build listing pages from ratings, facilities and photos without manual data entry.
  • Recurring rate monitoring: schedule the actor against a fixed set of cities or hotels to catch price and rating changes over time.

Data You Get

Sample shape: values are illustrative placeholders, not from a live listing.

FieldExample
hotelId"9034521"
name"Sample City Hotel"
url"https://www.traveloka.com/en-sg/hotel/singapore/sample-city-hotel-9034521?checkIn=2026-11-01&checkOut=2026-11-02"
locationName"Singapore" (the search location this row was found under)
region"Sample District, Singapore"
address"1 Sample Street, Singapore" (empty unless backfilled, see below)
price185.0
priceMinor18500 (the site's raw integer rate, before the currency's decimal places)
currency"SGD"
starRating4.0
guestRating / guestRatingMax8.2 / 10.0
guestRatingLabel"Very Good"
numReviews1240
latitude / longitude1.301 / 103.855
facilities / facilityCodes["Wi-Fi in public areas", "Swimming pool"] / ["WIFI_PUBLIC_AREA", "POOL"]
imageUrl / imageUrls"https://ik.imagekit.io/tvlk/sample/asset/sample-hotel-0000000.jpeg" / up to 8-12 photo URLs
market"en-sg"
checkInDate / checkOutDate"2026-11-01" / "2026-11-02"
changeType / changedFields"NEW" / [] on every row; with incremental mode also "UPDATED" / ["price"], "UNCHANGED" or "EXPIRED"
firstSeenAt / lastSeenAt"2026-11-01T00:00:00Z" / "2026-11-01T00:00:00Z" (carried over between runs in incremental mode)
features["Free cancellation"] (detail toggle, merged from the site's own data feed)
descriptionfull text hotel description (detail toggle, pasted hotel detail links only)
amenities["Free Wi-Fi", "Swimming pool"] (detail toggle, pasted hotel detail links only)
detailFetchedtrue when fetchDetails merged new data from the site's feed (search rows) or read the hotel page (pasted detail links); not set by an address backfill or fetchReviews on their own
reviews[{"author": "Guest Name", "rating": 9.0, "ratingMax": 10.0, "date": "2026-09-01", "text": "...", "provider": "GOOGLE"}] (review toggle)

Records from a pasted hotel detail link (URL mode) carry hotelId, url, currency, market, checkInDate, checkOutDate, plus (with fetchDetails on) the name, description, address and amenities the page itself carried, and the page's own guestRating / numReviews when it publishes them; locationName, region and price are not available for that path since no search feed was read. A hotel link whose page could not be read (with fetchDetails on), or whose reviews could not be read (with fetchReviews on and fetchDetails off, including when the device token is unavailable), is skipped with a warning: it is not saved, not billed and not added to the incremental baseline. A hotel with no reviews at all is still returned. With fetchReviews on and fetchDetails off, a hotel link carries its reviews but no name, description, address or amenities. With both fetchDetails and fetchReviews off, a hotel link returns only its id, link, dates and market.

How to Use

  1. Pick a mode: search (by city or area) or url (paste Traveloka links).
  2. For search mode, set the market (storefront), one or more locations, dates and any filters; for URL mode, paste your links (search filters are ignored). If you switch to URL mode and leave the links field untouched, it scrapes two built-in examples (one Singapore search link and one hotel detail link). Search links with a past check-in date are rolled forward to tomorrow, keeping the stay length. Hotel landing pages and activity/experience links are not supported and are skipped with a warning in the log.
  3. Turn on detail enrichment or guest reviews if you need them (both add a per-hotel surcharge), and set Max results or Max pages to control run size and cost.
  4. Click Start. Download the dataset as JSON, CSV or Excel, or read it through the API.

Search hotels in one city:

{
"mode": "search",
"market": "en-sg",
"locations": ["Singapore"],
"maxItems": 20
}

Search with filters, sorting and multiple cities:

{
"mode": "search",
"market": "en-th",
"locations": ["Bangkok", "Phuket"],
"checkInDate": "2026-11-01",
"checkOutDate": "2026-11-03",
"adults": 2,
"sortBy": "price-asc",
"minStarRating": 4,
"maxPrice": 5000,
"maxItems": 40
}

Walk a pasted Traveloka search link and enrich the results:

{
"mode": "url",
"urls": ["https://www.traveloka.com/en-sg/hotel/search?spec=01-12-2026.02-12-2026.1.1.HOTEL_GEO.107493.Singapore.2"],
"fetchDetails": true,
"maxItems": 30
}

Monitor a scope for changes (recurring schedule):

{
"mode": "search",
"market": "en-my",
"locations": ["Kuala Lumpur"],
"checkInDate": "2026-12-20",
"checkOutDate": "2026-12-22",
"maxItems": 50,
"incrementalMode": true,
"stateKey": "kl-december-stay",
"emitUnchanged": false
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/traveloka-com-scraper").call(run_input={
"mode": "search", "market": "en-sg", "locations": ["Singapore"], "maxItems": 20,
})
for hotel in client.dataset(run["defaultDatasetId"]).iterate_items():
print(hotel["name"], hotel["price"], hotel["currency"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/traveloka-com-scraper').call({
mode: 'search', market: 'en-sg', locations: ['Singapore'], maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

LinkResult
Search link with spec= (/hotel/search?spec=...)Walked page by page, one row per hotel
Hotel detail link (/hotel/<city>/<name>-<id>)One row for that hotel, read from its page (fetchDetails reads its page; fetchReviews adds its reviews, with or without fetchDetails)
Hotel landing page without spec=Skipped with a warning: run a search on the site and paste that link
Activity or experience linkSkipped with a warning: hotel links only

When the links field is left untouched in URL mode, the urls default is used: one Singapore search link and one hotel detail link, so a first run needs no setup. Search links with a past check-in date are rolled forward to tomorrow, keeping the stay length. Supported markets: en-sg, en-my, en-id, en-th, en-vn, en-ph, en-au.

Caps across multiple locations

Max results (maxItems) is the one real cap for the whole run (up to 100000 hotels, 0 meaning no limit); with several locations or links it is split evenly across each of them, and a location that has fewer hotels than its share does not pass the leftover on to the others. Max pages (maxPages) separately caps how many result pages are walked per location, regardless of maxItems.

Tip: the actor deduplicates hotels once per run by hotel id, so the same hotel is never pushed twice even if two locations or links overlap. In a run with tight per-location budgets, a hotel that is read right as one location's share of the budget runs out is marked as already seen for the rest of the run, even though it was not actually saved to the dataset. If that same hotel also appears under a later, still-under-budget location, it will not be re-tried there either, which can leave a run under its maxItems target when locations overlap. Raising maxItems, raising maxPages, or running overlapping locations as separate runs avoids it.

Resume and recurring updates

Incremental mode (incrementalMode) is for scheduled runs over the same scope. Each hotel is classified NEW, UPDATED (with changedFields), UNCHANGED (suppressed and not billed unless emitUnchanged is on) or EXPIRED (only after a run that scanned the entire scope without hitting a cap, a page limit or a refusal, and only with emitExpired). stateKey names or shares the stored baseline. ignoreFieldsForChanges lists fields whose changes are not counted; changing the list never re-bills the saved baseline. With incremental mode off, every row is NEW.

Keep in mind for scheduled monitoring:

  • The automatic baseline is tied to your exact input, including the dates. If you leave the dates empty, they roll forward every day, so each day starts a fresh baseline and every hotel comes back NEW. Set fixed checkInDate / checkOutDate for the stay you are watching, or set a stateKey.
  • With a shared stateKey but changing dates, every hotel comes back UPDATED with url in changedFields, because the hotel link carries the dates.
  • An EXPIRED hotel is emitted (and billed) once, then removed from the baseline, so later complete runs do not emit it again. If it comes back, it is classified NEW.

Resume (resumeFromRunId) continues one interrupted run by skipping hotels it already saved, so they are not fetched or billed again. Do not combine it with Incremental mode.

Send results into your apps (MCP connectors)

You can optionally send results into the apps you already use through Apify MCP connectors: authorize a connector under Apify, Settings, API and Integrations, then pick it in the mcpConnectors input. Notion receives one page per hotel under the page you set in notionParentPageUrl; other connectors receive a condensed summary per item (title plus the main fields flattened as text). The connector write is a side channel: it never changes the dataset, and the complete record always stays in the Apify dataset. Cap the export per run with maxNotifyListings.

Known issue in the current build: the connector export step fails before any item is sent, and the run only logs "MCP connector export skipped" without failing. Your dataset is complete and unaffected, but connectors currently receive nothing from this actor. Until this is fixed, connect the dataset through the Integrations tab instead.

Input Parameters

ParameterTypeDefaultDescription
modeselectsearchSearch by city or area, or paste Traveloka links.
marketselecten-sgStorefront locale: sets the language and the currency prices are returned in.
locationsstringList["Singapore"]Cities or areas to walk (search mode). Each is resolved to Traveloka's internal region id; multiple locations split the result budget evenly.
checkInDatedatetomorrowCheck-in date (YYYY-MM-DD). Leave empty to default to tomorrow (site time).
checkOutDatedateday after check-inCheck-out date (YYYY-MM-DD). Leave empty to default to the day after check-in.
adultsinteger2Number of adult guests per search.
roomsinteger1Number of rooms per search.
sortByselectpopularitySearch-mode sort order: popularity (site default), price low to high, or price high to low.
minPriceintegeremptyDrop hotels whose nightly price is below this. Applied to returned results (client side), in the market's currency.
maxPriceintegeremptyDrop hotels whose nightly price is above this. Applied to returned results (client side), in the market's currency.
minStarRatinginteger0Drop hotels below this star rating (0 keeps all). Applied to returned results (client side).
minGuestRatingnumberemptyDrop hotels whose guest review score is below this, out of 10. Applied to returned results (client side); hotels without a score are kept.
urlsarraysampleTraveloka hotel-search links (with spec=) and/or hotel detail links (URL mode). Search filters are ignored in URL mode.
fetchDetailsbooleanfalseMerges facility codes and labels, feature highlights, extra photos and a backfilled guest rating into each row. Bills the detail-enrichment surcharge once per enriched row; unenriched rows bill nothing extra. Pasted hotel detail links are read as full pages.
fetchReviewsbooleanfalseAdds a reviews array (author, rating, date, text) per hotel, up to maxReviews. Bills the detail-enrichment surcharge like other enrichment. Works with fetchDetails on or off.
reviewSortBystringDEFAULTOrder of the reviews array: site default, newest, oldest, most helpful, highest or lowest rating.
reviewCutoffDatestringemptyKeep only reviews dated after this day.
aggregateRatingsbooleantrueWith fetchReviews on, adds an aggregatedReview object (count, overallRating) to each hotel row. Search results take both from the listing; a hotel detail link takes them from the page when it publishes a rating, which requires fetchDetails (without it the link carries reviews but no aggregatedReview). The data this actor reads carries no per-score breakdown, so there is no distributions.
maxReviewsinteger10Cap on review rows per hotel when fetchReviews is on. For search results, each enriched row bills detail-enrichment units equal to the number of started blocks of 10 reviews, minimum 1 (10 reviews = 1, 25 = 3, 1000 = 100). A hotel detail link bills 1 unit per row.
maxItemsinteger20Maximum hotel records to return per run, across all locations. 0 means unlimited.
maxPagesinteger0Safety cap on result pages walked per location. 0 means no page cap.
resumeFromRunIdstringemptyID of an interrupted run to continue, skipping hotels already saved by that run. Conflicts with Incremental mode (pick one).
incrementalModebooleanfalseRecurring monitoring: only new or changed hotels are emitted on repeated runs of the same scope. Conflicts with Resume from run ID.
stateKeystringemptyName for the storage the incremental baseline is kept under. Leave empty to derive it from your scope automatically.
emitUnchangedbooleanfalseWith Incremental mode: also emit (and bill) rows whose fingerprint did not change.
emitExpiredbooleanfalseWith Incremental mode: also emit (and bill) rows that were tracked before but did not appear in a complete walk.
ignoreFieldsForChangesarray[]With Incremental mode: field names that never make a hotel count as updated (use dots for nested fields). They stay in the output. The cover image link and the order of facilities are always ignored.
proxyproxyRESIDENTIAL (Apify Proxy)With the default RESIDENTIAL selection the run starts on Apify residential (a multi-country pool) and fails over to backup connections when an exit is refused. Only if you remove the RESIDENTIAL group does it start on a datacenter lane and escalate to residential; see Plan Requirement.
mcpConnectorsarrayemptyOptionally send results into the apps you already use through Model Context Protocol connectors, authorized under Apify Settings, API and Integrations.
notionParentPageUrlstringemptyNotion connector only: the page under which item pages are created.
maxNotifyListingsinteger50Cap on items written to each connector per run. Does not affect the dataset.

Output Example

Sample shape: values are illustrative placeholders, not from a live listing.

{
"hotelId": "9034521",
"name": "Sample City Hotel",
"url": "https://www.traveloka.com/en-sg/hotel/singapore/sample-city-hotel-9034521?checkIn=2026-11-01&checkOut=2026-11-02",
"locationName": "Singapore",
"region": "Sample District, Singapore",
"address": null,
"price": 185.0,
"priceMinor": 18500,
"currency": "SGD",
"starRating": 4.0,
"guestRating": 8.2,
"guestRatingMax": 10.0,
"guestRatingLabel": "Very Good",
"numReviews": 1240,
"latitude": 1.301,
"longitude": 103.855,
"facilities": ["Wi-Fi in public areas"],
"facilityCodes": ["WIFI_PUBLIC_AREA"],
"imageUrl": "https://ik.imagekit.io/tvlk/sample/asset/sample-hotel-0000000.jpeg",
"imageUrls": ["https://ik.imagekit.io/tvlk/sample/asset/sample-hotel-0000000.jpeg"],
"market": "en-sg",
"checkInDate": "2026-11-01",
"checkOutDate": "2026-11-02",
"changeType": "NEW",
"changedFields": [],
"firstSeenAt": "2026-11-01T00:00:00Z",
"lastSeenAt": "2026-11-01T00:00:00Z"
}

With fetchDetails on, search-mode rows also carry features, extra facilityCodes/facilities/imageUrls and a backfilled guestRating merged from the site's own data feed, plus detailFetched; a backfilled address is added when coordinates are available. Pasted hotel detail links additionally carry description and amenities read from the page itself. With fetchReviews on, rows also carry a reviews array (each review adds reviewer name, timestamp, score, original text, reaction count, provider and any host reply fields) and an aggregatedReview summary. The dataset has four views: Overview, Reviews (one row per review), Hotel details and Changes (change type and changed fields for incremental runs). Each run also stores a summary in the key-value store under OUTPUT.

Reviews: fetchReviews adds up to maxReviews reviews per hotel (default 10), ordered by reviewSortBy and limited to dates after reviewCutoffDate; aggregateRatings adds the aggregatedReview summary (count and overallRating). The order and the date filter are applied to the reviews the actor reads in the site's own order, so "newest first" orders those reviews and does not search the hotel's whole review list. Each hotel's review read has a time limit; if it runs out, the reviews read so far are kept and a warning is logged.

Plan Requirement

Traveloka sits behind a bot-verification wall that plain datacenter or direct requests do not clear, so a paid Apify plan with proxy access is required. With the default RESIDENTIAL proxy selection the actor starts on Apify residential and then tries the backup connections, and it keeps using the one that was served. If you remove the RESIDENTIAL group it starts on a datacenter lane instead (which Traveloka usually refuses), escalates to residential, then to the backup connections. On a free plan, which has no residential proxy, the run logs: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." Pages served that way log "Served over the backup connection (the default connection was unavailable)." You can also supply your own proxy URLs.

FAQ

How much does it cost?

You pay per hotel record returned (the dataset-item event), plus a small per-run start event. Detail enrichment and guest reviews use the detail-enrichment event, charged once per enriched row. For search results the units per row equal the number of started blocks of 10 reviews, minimum 1; a hotel detail link bills 1 unit per row whatever its review count. Unchanged, duplicate and failed rows are not charged: a hotel link whose page or reviews could not be read is skipped, not saved and not billed. The Pricing tab shows the current rates. Use Max results to cap the cost of any run.

This actor collects only publicly available hotel listing data. You are responsible for how you use it: follow Traveloka's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices and availability change constantly and are not guaranteed to be bookable at the time you read them.

Can I get only new or changed hotels on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated records, and unchanged ones are not billed unless you turn on Emit unchanged rows. Use fixed check-in and check-out dates (or a State key) for the scope you are watching: with empty dates the scope changes every day and every hotel comes back as new.

Why did my price or rating filters return fewer hotels than Max results?

minPrice, maxPrice, minStarRating and minGuestRating are applied after each page is fetched (client side), not sent to the site as a request filter. A page can be mostly filtered out and still count toward the page budget, so a heavily filtered search may need a higher Max results or Max pages to fill your target count.

Why did my run fail instead of returning an empty dataset?

A run that could not read any page stops with one of these messages, so "no hotels for these dates" is never confused with "nothing could be read":

  • "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required." Upgrade, or check your own proxy credentials.
  • "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Traveloka pages." Upgrade or supply your own proxy URLs.
  • "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search." Try again later.

If only part of the walk was refused, you still get what was found, and the run reports the shortfall in its status message.

Does Resume from run ID actually skip hotels from the earlier run?

Yes. Pass the interrupted run's ID in resumeFromRunId and hotels that run already saved are skipped, so they are not fetched or billed again. It cannot be combined with Incremental mode.

Can I use it with AI agents or MCP?

Yes. Any MCP client or AI agent that can call Apify actors can run it and read the dataset. The built-in connector field (mcpConnectors) is meant to push results into Notion, Linear or Airtable, but in the current build that export sends nothing (see the MCP connectors section), so read the dataset directly for now.

๐Ÿ”— Want more travel data?

Pair this actor with these related scrapers from the same team:

๐Ÿจ Wego Scraper
Scrape Wego hotels by city or search URL. Get rates, providers, booking links...
๐Ÿจ Rakuten Travel Scraper
Scrape Rakuten Travel, Japan's largest domestic hotel-booking site, by prefecture...
๐Ÿจ Expedia Hotels Scraper
Scrape Expedia.com hotel listings and reviews for any destination. Extract prices...
๐Ÿจ Trip.com Hotels, Rooms & Reviews Scraper
Pull structured hotel listings from trip.com with prices, room types, amenities...
๐Ÿจ Hostelworld Scraper
Scrape Hostelworld hostels, hotels, guesthouses, B&Bs, apartments and campsites by city...
๐Ÿจ Airbnb Scraper
Scrape Airbnb stays by destination or link: nightly and total price, rating breakdown...

๐Ÿ‘‰ Browse all abotapi scrapers

๐Ÿ’ฌ Support

  • ๐Ÿž Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • ๐Ÿ› ๏ธ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • โญ Enjoying it? A quick review on the actor page helps other users find it.