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

1

Total users

1

Monthly active users

3 days ago

Last modified

Categories

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; locationName, region, price and the rating fields are not available for that path since no search feed was read.

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).
  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-11-2026.02-11-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.

Caps across multiple locations

Max results (maxItems) is the one real cap for the whole run; 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. 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 stays in the baseline, so it is emitted (and billed) again on every later complete run until it returns. When it returns unchanged, it is suppressed like any other unchanged hotel rather than flagged as back.

Resume (resumeFromRunId) is meant to continue one interrupted run by skipping hotels it already saved. In the current build this does not work: loading the earlier run's saved hotel ids fails silently, so the run proceeds as if nothing had been seen before, and every hotel from the interrupted run is fetched (and billed) again. Until this is fixed, treat resumeFromRunId as a plain rerun; if you need to merge both datasets, dedupe by hotelId on your side afterward.

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.
maxReviewsinteger10Cap on review rows per hotel when fetchReviews is on. The surcharge scales with volume: 1 unit per row plus 1 unit per started block of 10 reviews.
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.
proxyproxyRESIDENTIAL (Apify Proxy)The actor rotates a multi-country residential pool and fails over automatically when an exit is refused. Removing the RESIDENTIAL group typically causes runs to be refused by the site.
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.

Plan Requirement

This actor connects through Apify's residential proxy group by default, which Traveloka's hotel pages require; the connection rotates a multi-country pool automatically and fails over when an exit is refused. Removing the RESIDENTIAL group typically causes runs to be refused by the site; supply your own working proxy if you need an alternative.

FAQ

How much does it cost?

You pay per hotel record returned, with optional detail enrichment and guest reviews billed only when you switch them on. 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?

If the site refuses every request, the run stops with a clear message so "no hotels for these dates" is never confused with "nothing could be read". Run it again in a few minutes, or with a different proxy selection. 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?

Not in the current build. Loading the earlier run's saved hotel ids fails silently, so a resumed run behaves like a fresh run and re-fetches (and re-bills) every hotel the interrupted run had already saved. Until this is fixed, expect duplicate rows when combining an interrupted run's dataset with its "resumed" follow-up, and dedupe by hotelId on your side if that matters to you.

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
From $1/1K. Scrape Expedia.com hotel listings and reviews for any destination. Extract...
๐Ÿจ Trip.com Hotels, Rooms & Reviews Scraper
Pull structured hotel listings from trip.com with prices, room types, amenities...
๐Ÿจ Airbnb Scraper
Scrape Airbnb stays by destination or link: nightly and total price, rating breakdown...
๐Ÿจ Vrbo Scraper
Extract vacation rental data from Vrbo.com by location, region, coordinates, property...

๐Ÿ‘‰ 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.