<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Factden</title>
    <description>The latest articles on DEV Community by Factden (@factden).</description>
    <link>https://dev.to/factden</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3962014%2Ff955bba2-cff2-46e4-af7d-48e34fb307d4.png</url>
      <title>DEV Community: Factden</title>
      <link>https://dev.to/factden</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/factden"/>
    <language>en</language>
    <item>
      <title>How to Scrape India MCA Company and Director Contacts in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Wed, 19 Aug 2026 05:23:42 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-india-mca-company-and-director-contacts-in-2026-python-a-no-code-shortcut-4ila</link>
      <guid>https://dev.to/factden/how-to-scrape-india-mca-company-and-director-contacts-in-2026-python-a-no-code-shortcut-4ila</guid>
      <description>&lt;p&gt;If you sell into India, the contact that actually moves a deal is a decision maker, a company's director, not a generic info@ inbox. India's Ministry of Corporate Affairs (MCA) registry is where that lives: every registered company, its status, capital and filings, the full board, and on each director's DIN their personal email and phone. Getting it out cleanly, and matching a director to every company they sit on, is the hard part.&lt;/p&gt;

&lt;p&gt;This guide shows both ways: a runnable Python recipe, and a no-code shortcut with the &lt;a href="https://apify.com/factden/mca-company-director-scraper?fpr=factden" rel="noopener noreferrer"&gt;MCA India Company and Director Data Scraper&lt;/a&gt; that skips the hard parts entirely. Run it by industry and state to build a whole segment of B2B leads at once, then enrich the board with director contacts.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/PhZmoVHMjTE"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Company master for a CIN: legal name, status, company type and class, incorporation date, industry (NIC), state, authorised and paid-up capital, ROC, registered addresses, board size, and three years of filing history.&lt;/li&gt;
&lt;li&gt;The full board for a company: every director with DIN, name, designation, and dates.&lt;/li&gt;
&lt;li&gt;Director master for a DIN: status, date of birth, gender, nationality, and the full directorship network, every company they sit on with designation and dates.&lt;/li&gt;
&lt;li&gt;Director contacts as an opt-in enrichment: the director's personal email and phone as registered with MCA on their DIN or DIR-3 KYC record.&lt;/li&gt;
&lt;li&gt;Discovery by industry and state: pick a sector and a state and pull a whole segment of companies at once, not one CIN at a time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why scraping MCA directly is hard
&lt;/h2&gt;

&lt;p&gt;The MCA portal does not hand you company and director records in simple HTML you can select with CSS. Most lookups sit behind a session and token flow, and the pieces you want, company master, the board, each director's master record, and the directorship graph, come from different calls that you then have to stitch into one clean row. Discovery is its own problem: finding every active company in an industry and state means understanding how a CIN prefix encodes industry and paginating the full result set. You can solve all of this, but it is real engineering, and it breaks whenever the registry reshapes a response.&lt;/p&gt;

&lt;p&gt;That is the whole reason a maintained actor exists: it absorbs the token and lookup work and hands you a flat table.&lt;/p&gt;

&lt;h2&gt;
  
  
  DIY vs actor vs official data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Write it yourself&lt;/th&gt;
&lt;th&gt;MCA scraper (actor)&lt;/th&gt;
&lt;th&gt;Official MCA portal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Token flow, lookups, response parsing&lt;/td&gt;
&lt;td&gt;Paste a CIN, DIN, name, or pick industry and state&lt;/td&gt;
&lt;td&gt;Manual per-company lookups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Company plus board plus directorship graph&lt;/td&gt;
&lt;td&gt;You stitch several calls&lt;/td&gt;
&lt;td&gt;Included, one clean row&lt;/td&gt;
&lt;td&gt;One company at a time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discovery by industry and state&lt;/td&gt;
&lt;td&gt;You decode CIN prefixes&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Not offered as a bulk export&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Director email and phone&lt;/td&gt;
&lt;td&gt;Separate KYC lookup to solve&lt;/td&gt;
&lt;td&gt;Opt-in enrichment&lt;/td&gt;
&lt;td&gt;Not exposed in bulk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost model&lt;/td&gt;
&lt;td&gt;Free but you build it&lt;/td&gt;
&lt;td&gt;Free tier, then pay per result&lt;/td&gt;
&lt;td&gt;Per-document fees, manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access&lt;/td&gt;
&lt;td&gt;You own every breakage&lt;/td&gt;
&lt;td&gt;Maintained for you&lt;/td&gt;
&lt;td&gt;Their portal, no bulk export&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no open MCA bulk API that hands you company and director records for a whole segment. The portal is built for one-company-at-a-time lookups. For open collection into your own pipeline, the choice is really "build and maintain it" or "call an actor and pay per run".&lt;/p&gt;

&lt;h2&gt;
  
  
  The no-code way (about a minute)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a free &lt;a href="https://console.apify.com/sign-up?fpr=factden" rel="noopener noreferrer"&gt;Apify account&lt;/a&gt; and open the actor page.&lt;/li&gt;
&lt;li&gt;Click Try for free. The input is pre-filled with an example.&lt;/li&gt;
&lt;li&gt;Pick a mode (Companies or Directors), then give it a CIN, DIN, name, or an industry and state, and click Start.&lt;/li&gt;
&lt;li&gt;Download the results from the Output tab as JSON, CSV, or Excel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To refresh a segment on a schedule, save your input as a Task and attach a Schedule. Each run appends a fresh snapshot, so a lead list stays current as new companies register.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it from Python
&lt;/h2&gt;

&lt;p&gt;The actor runs on Apify, so you drive it with the Apify client. Install it with &lt;code&gt;pip install apify-client&lt;/code&gt;, grab your token from Apify Settings, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Discover a whole segment: active IT companies in Maharashtra, with their boards.
&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;industries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Information and communication&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;states&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Maharashtra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;companyStatuses&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includeDirectors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxCompanies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/mca-company-director-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To pull directors by name and enrich them with personal contacts instead, switch the mode and turn on the enrichment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;run_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;director&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;directorNames&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Nandan Nilekani&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;directorLeads&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# opt-in email + phone enrichment
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includeCompanies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxDirectors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mode&lt;/code&gt; selects the run: &lt;code&gt;company&lt;/code&gt; drives discovery by CIN, company name, or industry and state; &lt;code&gt;director&lt;/code&gt; drives lookups by DIN or director name. Only the inputs the selected mode needs are used. &lt;code&gt;directorLeads&lt;/code&gt; is the opt-in switch for personal email and phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the output looks like
&lt;/h2&gt;

&lt;p&gt;Trimmed from a real &lt;code&gt;director&lt;/code&gt; run for a single director (contacts shown as dummy values here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"din"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"02790976"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NANDAN M. NILEKANI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Approved"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gender"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Male"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nationality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Indian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nandan.nilekani@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+919876543210"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"directorshipsCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INFOSYS LIMITED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"L85110KA1981PLC013115"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"designation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Director"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fields you get depend on the mode you run:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What each row carries&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;company&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One row per company: CIN, name, status, type, capital, industry, state, ROC, addresses, filings, board size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;company&lt;/code&gt; + &lt;code&gt;includeDirectors&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The company plus its board, one director per row with designation and dates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;director&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One row per directorship: DIN, name, status, DOB, gender, and the company with designation and dates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;director&lt;/code&gt; + &lt;code&gt;directorLeads&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The same, plus the director's personal email and phone (charged once per unique DIN, only when found)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Field names are documented on the listing and in the repo below rather than invented here, so what you build against matches what the actor emits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on a real dataset first
&lt;/h2&gt;

&lt;p&gt;If you want to see the shape before running anything, there is a free &lt;a href="https://factden.com/sample-mca-company-director-scraper" rel="noopener noreferrer"&gt;sample MCA director dataset&lt;/a&gt; you can open in the browser, and the &lt;a href="https://github.com/factden/mca-company-director-scraper" rel="noopener noreferrer"&gt;input, snippets, and field reference&lt;/a&gt; are on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need an MCA login or API key?&lt;/strong&gt;&lt;br&gt;
No. There is no MCA portal login, no account, and no API key. You only need an Apify token to run the actor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I search by company name, director name, or industry?&lt;/strong&gt;&lt;br&gt;
Yes. By exact CIN or DIN, by company or director name (a prefix match on the registered name), or by industry mapped to the NIC code, narrowed by state, status, type, and listing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How are director contacts charged?&lt;/strong&gt;&lt;br&gt;
Once per unique director (DIN) and only when a real email or phone is found, never per row. A director who sits on five boards is billed once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I keep a lead list current?&lt;/strong&gt;&lt;br&gt;
Save your input as a Task and attach a Schedule. Each run appends a fresh snapshot, so new registrations in your target segment show up over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it legal?&lt;/strong&gt;&lt;br&gt;
Company and director master data is public-record. The director email and phone enrichment returns personal data protected under India's DPDP Act, 2023, so you must have a lawful basis to obtain and process it. Use the output responsibly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;p&gt;Building B2B lead and market data across sources? The same team maintains a &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;G2 reviews scraper&lt;/a&gt; for software buyer intelligence and an &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed jobs scraper&lt;/a&gt; for hiring and labor-market signals, so you can line up firmographics, buyer sentiment, and hiring in the same format.&lt;/p&gt;

&lt;p&gt;Full write-up with more examples: &lt;a href="https://factden.com/blog/how-to-scrape-mca-company-director-data" rel="noopener noreferrer"&gt;how to scrape MCA company and director data&lt;/a&gt;, and the &lt;a href="https://factden.com/mca-company-director-scraper" rel="noopener noreferrer"&gt;MCA data hub&lt;/a&gt; has the field reference and use cases.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>b2b</category>
      <category>leadgeneration</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Scrape Booking.com Hotels, Prices and Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Wed, 19 Aug 2026 05:21:30 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-bookingcom-hotels-prices-and-reviews-in-2026-python-a-no-code-shortcut-2c4b</link>
      <guid>https://dev.to/factden/how-to-scrape-bookingcom-hotels-prices-and-reviews-in-2026-python-a-no-code-shortcut-2c4b</guid>
      <description>&lt;p&gt;Booking.com has the largest pool of hotel prices and guest reviews on the web, and no public data API to get any of it out. Its partner APIs are for accredited hotels managing their own listings, not for market research. So if you want to shop rates across a city, watch availability over time, or build a guest-sentiment dataset, you have to read the public pages yourself.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull, why the DIY route is harder than it looks, working Python, a no-code shortcut, and a plain comparison. There is a fuller reference on how to &lt;a href="https://factden.com/booking-com-scraper" rel="noopener noreferrer"&gt;scrape Booking.com&lt;/a&gt;, plus a step-by-step &lt;a href="https://factden.com/blog/how-to-scrape-booking-com" rel="noopener noreferrer"&gt;blog walkthrough&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/mxaye6x1E8U"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull
&lt;/h2&gt;

&lt;p&gt;Give it a city, an exact hotel, or a Booking.com link, and you get four clean, separate outputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hotels&lt;/strong&gt; (one row each): name, star class, guest score and review count, coordinates, address, currency, per-night and total price, &lt;code&gt;availableRooms&lt;/code&gt;, and a scarcity message ("only 2 left").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rooms and rates&lt;/strong&gt;: one row per bookable room, with price per night, meal plan, refundable flag, free-cancellation deadline, max occupancy and room size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews&lt;/strong&gt; (optional): score out of 10, positive and negative text, reviewer country, traveller type, stay date and nights, and the hotel's reply. Rating-only reviews are included and flagged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability calendar&lt;/strong&gt; (optional): one row per forward day, with the date, available or sold-out, and the minimum nightly price.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every hotel and review row also carries a self-contained &lt;code&gt;markdownContent&lt;/code&gt; block, ready to drop into a vector DB or an LLM prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Booking.com is hard
&lt;/h2&gt;

&lt;p&gt;Three reasons the DIY route eats your week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No single page has everything.&lt;/strong&gt; Discovery, the price breakdown, the room list, reviews and the calendar all come from different places, each with its own parameters. Stitching them into one clean row per hotel is most of the work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prices are personalized and date-bound.&lt;/strong&gt; The same hotel returns different rates by dates, party size, currency and device. You have to pin all of that or your numbers drift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume and blocking.&lt;/strong&gt; A city has hundreds of hotels; a hotel has thousands of reviews. Pulling that reliably, at rate, without getting blocked, is a full-time job on its own.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DIY in Python&lt;/strong&gt; - full control, but you own discovery, pagination, the price/room/review/calendar stitching, retries and blocking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A no-code actor / API&lt;/strong&gt; - you send input, it returns clean JSON. No parsing, no infra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The official partner API&lt;/strong&gt; - not an option unless you are an accredited Booking.com partner managing your own inventory.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;The honest starting point is discovery: find the hotels, then enrich each one. Expect to maintain the request parameters, pagination and retry logic yourself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="c1"&gt;# You resolve a destination, page through its hotels, then for each hotel
# fetch price + rooms, reviews and the calendar - each a separate call.
&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.booking.com/searchresults.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rome&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-09-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-09-02&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ...then parse the results, follow each hotel, and merge prices, rooms,
# reviews and availability into one row. This is where the week goes.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That runs, but you still have to build the whole pipeline around it. When you want reliability at scale, the shortcut wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://apify.com/factden/booking-com-scraper" rel="noopener noreferrer"&gt;Booking.com Scraper&lt;/a&gt; on Apify takes input and returns clean JSON. Call it from Python with the Apify client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_APIFY_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/booking-com-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchLocation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rome&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includePrices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includeReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includeAvailabilityCalendar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calendarDays&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxResults&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotelName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;perNightPrice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewScore&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap &lt;code&gt;searchLocation&lt;/code&gt; for &lt;code&gt;searchTerms&lt;/code&gt; (a hotel name) or &lt;code&gt;startUrls&lt;/code&gt; (Booking.com links) when you want specific properties. Prices, reviews and the calendar are independent toggles, so you pay only for what you pull.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back
&lt;/h2&gt;

&lt;p&gt;One flat row per hotel, plus separate reviews and calendar datasets. A trimmed hotel row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Savoy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"London"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;9.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1446&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GBP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"perNightPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;733.33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"availableRooms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scarcityMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We have 7 left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rooms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"roomName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Superior Queen Room"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pricePerNight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;733.33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"refundable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export as JSON, CSV, Excel or HTML, or pull it from the Apify API on a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the shape before you run anything? Grab a &lt;a href="https://factden.com/sample-booking" rel="noopener noreferrer"&gt;free Booking.com sample&lt;/a&gt; as clean CSV.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does Booking.com have a public API?&lt;/strong&gt; No. Its Connectivity and Demand APIs are for accredited partners managing their own listings, not for market research.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I discover every hotel in a city?&lt;/strong&gt; Yes. Put a city, region or country in &lt;code&gt;searchLocation&lt;/code&gt; and you get every hotel there, paginated and de-duplicated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it get rating-only reviews?&lt;/strong&gt; Yes, and there is no 500-review cap. Rating-only reviews (a score with no text) are included and flagged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much does it cost?&lt;/strong&gt; Pay-per-event, no start fee: hotel $0.003, prices $0.005 per priced hotel, review $0.001, calendar day $0.0005. New Apify accounts get free monthly credit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;p&gt;Building a travel-data pipeline? Pair this with the &lt;a href="https://apify.com/factden/google-hotels-scraper?fpr=factden" rel="noopener noreferrer"&gt;Google Hotels Scraper&lt;/a&gt; for the OTA rate ladder, or the &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia Reviews Scraper&lt;/a&gt;. Full list at &lt;a href="https://apify.com/factden?fpr=factden" rel="noopener noreferrer"&gt;apify.com/factden&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Agoda Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Thu, 06 Aug 2026 05:16:25 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-agoda-reviews-in-2026-python-a-no-code-shortcut-o9h</link>
      <guid>https://dev.to/factden/how-to-scrape-agoda-reviews-in-2026-python-a-no-code-shortcut-o9h</guid>
      <description>&lt;p&gt;Here is the thing most people hit when they try to &lt;a href="https://www.agoda.com" rel="noopener noreferrer"&gt;scrape Agoda reviews&lt;/a&gt;: there is no public endpoint you can just call. Agoda's partner and affiliate APIs cover rates and availability only. None of them let you read a property's guest reviews. So the moment you want the actual words guests wrote, you are on your own.&lt;/p&gt;

&lt;p&gt;And it gets trickier. The reviews you see on an Agoda hotel page are not in the HTML you get from a plain request. They load separately, after the page. On top of that, Agoda serves reviews in 33 languages, and by default they are fragmented across those languages, so a naive scrape gives you a lopsided slice of what guests actually said.&lt;/p&gt;

&lt;p&gt;This post shows the honest Python starting point, why it stalls, and then a no-code shortcut that returns clean rows with one call. If you would rather read a longer writeup, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-agoda-reviews" rel="noopener noreferrer"&gt;how to scrape Agoda reviews&lt;/a&gt; guide on our site too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Every review row includes the core identity (&lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;), the rating on Agoda's 0 to 10 score scale plus a word label (&lt;code&gt;score&lt;/code&gt;, &lt;code&gt;ratingText&lt;/code&gt;), the written content (&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, and where available &lt;code&gt;positives&lt;/code&gt; / &lt;code&gt;negatives&lt;/code&gt;), the stay context (&lt;code&gt;travelerType&lt;/code&gt;, &lt;code&gt;roomType&lt;/code&gt;, &lt;code&gt;checkInDate&lt;/code&gt;, &lt;code&gt;checkOutDate&lt;/code&gt;, &lt;code&gt;lengthOfStay&lt;/code&gt;), the reviewer basics (&lt;code&gt;reviewerName&lt;/code&gt;, &lt;code&gt;reviewerCountry&lt;/code&gt;), the date (&lt;code&gt;reviewDate&lt;/code&gt;), any attached &lt;code&gt;reviewPhotos&lt;/code&gt;, the hotel's &lt;code&gt;ownerResponse&lt;/code&gt; if the property replied, and a &lt;code&gt;markdownContent&lt;/code&gt; block that is ready to drop straight into an LLM prompt.&lt;/p&gt;

&lt;p&gt;One honesty note: &lt;code&gt;positives&lt;/code&gt; and &lt;code&gt;negatives&lt;/code&gt; (the split liked / disliked fields) are usually empty for Agoda-native reviews. Agoda's own reviews come as one body of text. The separated liked / disliked structure mostly shows up on reviews sourced from Booking.com or Priceline, which you can optionally pull in. Do not build a pipeline that assumes every review has neat liked / disliked lists, because most will not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Agoda reviews is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;There is no public reviews API. The official Agoda APIs are for rates and availability, so reviews are simply not on offer through a supported channel.&lt;/li&gt;
&lt;li&gt;Reviews load dynamically. The hotel page HTML you get from a simple GET does not contain the structured review text, so parsing the page source gets you nothing useful.&lt;/li&gt;
&lt;li&gt;Language fragmentation. With 33 review languages, a single-pass scrape tends to return whatever the page defaults to, not the full picture.&lt;/li&gt;
&lt;li&gt;Two scoring worlds. Agoda uses a 0 to 10 score, not 1 to 5 stars, so any tooling that assumes a five-star scale will mangle your data.&lt;/li&gt;
&lt;li&gt;Blocking and URL shape. Hotel URLs vary by region and Agoda actively guards against automated access. Handling that reliably is a project on its own.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Handles blocking + dynamic load&lt;/th&gt;
&lt;th&gt;Reviews coverage&lt;/th&gt;
&lt;th&gt;Cost model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DIY Python&lt;/td&gt;
&lt;td&gt;You write and maintain it&lt;/td&gt;
&lt;td&gt;You build it yourself&lt;/td&gt;
&lt;td&gt;Whatever you can reach&lt;/td&gt;
&lt;td&gt;Your time + proxies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agoda Hotel Reviews Scraper (Apify)&lt;/td&gt;
&lt;td&gt;Paste a URL or ID&lt;/td&gt;
&lt;td&gt;Yes, done for you&lt;/td&gt;
&lt;td&gt;All reviews, all 33 languages, optional extra sources&lt;/td&gt;
&lt;td&gt;Pay per review returned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Official Agoda API&lt;/td&gt;
&lt;td&gt;Partner account&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;None, reviews are not offered&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;Start where everyone starts, a request to the hotel page. The problem shows up immediately: the review text is not in the response body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.agoda.com/bayswater-inn-hotel/hotel/london-gb.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewComment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# -&amp;gt; False, the reviews are not in the page HTML
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reviews arrive through a separate call the page makes after it loads, and that call is guarded. To do this properly you would reverse engineer that flow, rotate proxies, deal with region-specific URL shapes, page through results, normalize the 0 to 10 score, and keep it all working as the site changes. Doable, but it is a maintenance job, not a one-off script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;If you would rather skip the maintenance, the &lt;a href="https://apify.com/factden/agoda-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Agoda Hotel Reviews Scraper&lt;/a&gt; on Apify does the fetching, paging, and normalizing for you. The actor handles blocking and URL resolution for you, so you just hand it hotels and get rows back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/agoda-hotel-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotelUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.agoda.com/bayswater-inn-hotel/hotel/london-gb.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;11019&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewSources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agoda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;languages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ratingText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can paste full Agoda hotel URLs or bare numeric hotel IDs like &lt;code&gt;"11019"&lt;/code&gt;. Mix and match in the same list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;languages: []&lt;/code&gt; means give me everything in one combined stream. Pass a subset of Agoda's 33 languages if you only want specific ones.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reviewSources: ["agoda"]&lt;/code&gt; keeps it Agoda-native. Add Booking.com or Priceline, or clear the list to pull all sources.&lt;/li&gt;
&lt;li&gt;Filter with &lt;code&gt;sortBy&lt;/code&gt;, &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt;, and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt; (remember, that is on the 0 to 10 scale).&lt;/li&gt;
&lt;li&gt;There are two datasets. The default Reviews dataset is one row per guest review. The Hotels dataset gives you one aggregate row per hotel with the overall score, review counts, per-source breakdown, and location.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Track reputation over time
&lt;/h3&gt;

&lt;p&gt;Reviews are most useful as a trend, not a snapshot. Set &lt;code&gt;fromDate&lt;/code&gt; to the day after your last pull and put the run on an Apify Schedule (say, every morning). Each run then grabs only the reviews posted since last time, so you build an incremental reputation feed for a property or a whole competitive set without re-pulling history.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: the fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;score&lt;/code&gt; (0 to 10), &lt;code&gt;ratingText&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;positives&lt;/code&gt;, &lt;code&gt;negatives&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stay&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;travelerType&lt;/code&gt;, &lt;code&gt;roomType&lt;/code&gt;, &lt;code&gt;checkInDate&lt;/code&gt;, &lt;code&gt;checkOutDate&lt;/code&gt;, &lt;code&gt;lengthOfStay&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewerName&lt;/code&gt;, &lt;code&gt;reviewerCountry&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dates + owner&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewDate&lt;/code&gt;, &lt;code&gt;ownerResponse&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reviewPhotos&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11019&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Park Avenue Bayswater Inn Hyde Park"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.agoda.com/bayswater-inn-hotel/hotel/london-gb.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1149287470"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Agoda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Above average"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"All in all good hotel, can't complain but wasn't the best."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratingText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Good"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewerName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tiffany"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewerCountry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Australia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelerType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Couple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"roomType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Double Room"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkInDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-03"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkOutDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lengthOfStay"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ownerResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"markdownContent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"# Park Avenue Bayswater Inn Hyde Park review (Agoda)&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;**Score:** 6/10 - Good&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the real shape before running anything? Download the &lt;a href="https://factden.com/sample-agoda" rel="noopener noreferrer"&gt;free sample dataset&lt;/a&gt; and inspect the exact fields, including a few reviews with photos and owner responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is there an official Agoda reviews API?&lt;/strong&gt; No. Agoda's partner and affiliate APIs are for rates and availability. There is no supported way to read a property's guest reviews, which is the gap this actor fills, with no key and no partner account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to find hotel IDs?&lt;/strong&gt; No. Paste the normal Agoda hotel URL and you are done. If you happen to have the numeric ID, you can pass that instead, either works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get reviews in a specific language?&lt;/strong&gt; Yes. Agoda carries 33 review languages. Leave &lt;code&gt;languages&lt;/code&gt; empty for all of them in one stream, or pass the ones you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why are positives and negatives empty on some reviews?&lt;/strong&gt; Agoda-native reviews come as a single block of text, so the split liked / disliked fields are usually null. Those fields populate mainly on reviews sourced from Booking.com or Priceline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does pricing work?&lt;/strong&gt; Pay per review returned, with no start fee, and nothing at all if a run returns zero reviews. Rates run from 4.00 USD per 1,000 reviews down to 2.00 USD per 1,000 at higher volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I monitor reviews on a schedule?&lt;/strong&gt; Yes. Combine &lt;code&gt;fromDate&lt;/code&gt; with an Apify Schedule to pull only new reviews each run and build an incremental reputation feed. Full walkthrough in the &lt;a href="https://apify.com/factden/agoda-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Agoda hotel reviews API&lt;/a&gt; listing and the &lt;a href="https://github.com/factden/agoda-hotel-reviews-scraper" rel="noopener noreferrer"&gt;docs repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia Hotel Reviews Scraper&lt;/a&gt; if you need the same for Expedia.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip Reviews Scraper&lt;/a&gt; for Trip.com and Ctrip properties.&lt;/li&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Airbnb Prices, Availability and Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Wed, 05 Aug 2026 05:46:36 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-airbnb-prices-availability-and-reviews-in-2026-python-a-no-code-shortcut-2255</link>
      <guid>https://dev.to/factden/how-to-scrape-airbnb-prices-availability-and-reviews-in-2026-python-a-no-code-shortcut-2255</guid>
      <description>&lt;p&gt;If you manage short-term rentals, the numbers you actually care about move every day: nightly prices, forward availability, occupancy, ADR, and RevPAR. A revenue manager does not pull those once. They pull them on a schedule, watch how a market drifts week over week, and price against it. Airbnb is where most of that signal lives, and getting it out cleanly is the hard part.&lt;/p&gt;

&lt;p&gt;This guide shows both ways: a runnable Python recipe, and a no-code shortcut with the &lt;a href="https://apify.com/factden/airbnb-data-scraper?fpr=factden" rel="noopener noreferrer"&gt;Airbnb data scraper&lt;/a&gt; that skips the hard parts entirely. Schedule it daily to track prices, availability and occupancy over time and feed your revenue-management pipeline, the same job you would otherwise pay AirDNA to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Listings for a whole market by location: nightly price, property details, coordinates, rating, and host, one clean row per listing.&lt;/li&gt;
&lt;li&gt;The forward calendar for a listing: up to 12 months of availability and the real price by date, so you can see how a place is booking and what it charges night to night.&lt;/li&gt;
&lt;li&gt;Occupancy, ADR and RevPAR for a listing, the core revenue-management signals, computed from the forward calendar.&lt;/li&gt;
&lt;li&gt;A market report for a location: one aggregate KPI row (ADR, occupancy, RevPAR, revenue) plus the sampled listings, an AirDNA alternative you pay for only when you run it.&lt;/li&gt;
&lt;li&gt;Reviews with sentiment: the full review history for a listing, with ratings and host replies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why scraping Airbnb directly is hard
&lt;/h2&gt;

&lt;p&gt;Airbnb does not hand you prices and availability in simple HTML you can select with CSS. The data comes from Airbnb's internal endpoints, which expect requests to look a very particular way and quietly return nothing when they do not. Prices are quoted per date and per guest count, the forward calendar is paginated and keyed on internal listing tokens, and the occupancy math has to be derived from the calendar rather than read off a field. You can solve all of this, but it is real engineering, and it breaks whenever Airbnb reshapes a response.&lt;/p&gt;

&lt;p&gt;That is the whole reason a maintained actor exists: it absorbs the endpoint work and hands you a flat table.&lt;/p&gt;

&lt;h2&gt;
  
  
  DIY vs actor vs AirDNA
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Write it yourself&lt;/th&gt;
&lt;th&gt;Airbnb data scraper (actor)&lt;/th&gt;
&lt;th&gt;AirDNA / official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Endpoint work, proxies, response parsing&lt;/td&gt;
&lt;td&gt;Paste a location or listing URL&lt;/td&gt;
&lt;td&gt;Subscription onboarding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prices and availability&lt;/td&gt;
&lt;td&gt;You reverse-engineer it&lt;/td&gt;
&lt;td&gt;Included, structured&lt;/td&gt;
&lt;td&gt;Included, but locked to their dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Occupancy, ADR, RevPAR&lt;/td&gt;
&lt;td&gt;You derive it from the calendar&lt;/td&gt;
&lt;td&gt;Computed for you&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviews with sentiment&lt;/td&gt;
&lt;td&gt;Separate endpoint to solve&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Not the focus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost model&lt;/td&gt;
&lt;td&gt;Free but you build it&lt;/td&gt;
&lt;td&gt;Free tier, then pay per result&lt;/td&gt;
&lt;td&gt;Fixed monthly subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access&lt;/td&gt;
&lt;td&gt;You own every breakage&lt;/td&gt;
&lt;td&gt;Maintained for you&lt;/td&gt;
&lt;td&gt;Their data, their platform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no open Airbnb API that lets you pull arbitrary listing prices, availability, and occupancy. AirDNA packages similar numbers, but as a fixed monthly subscription tied to their dashboard. For open collection into your own pipeline, scraping is the route, and the choice is really "build and maintain it", "rent AirDNA", or "call an actor and pay per run".&lt;/p&gt;

&lt;h2&gt;
  
  
  The no-code way (about a minute)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a free &lt;a href="https://console.apify.com/sign-up?fpr=factden" rel="noopener noreferrer"&gt;Apify account&lt;/a&gt; and open the actor page.&lt;/li&gt;
&lt;li&gt;Click Try for free. The input is pre-filled with an example.&lt;/li&gt;
&lt;li&gt;Pick a mode, then fill the one input it needs (a location, or a listing URL), then click Start.&lt;/li&gt;
&lt;li&gt;Download the results from the Output tab as JSON, CSV, or Excel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To run it every day, save your input as a Task and attach a Schedule. Each run appends a fresh snapshot, which is exactly the time series a revenue manager wants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it from Python
&lt;/h2&gt;

&lt;p&gt;The actor runs on Apify, so you drive it with the Apify client. Install it with &lt;code&gt;pip install apify-client&lt;/code&gt;, grab your token from Apify Settings, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Market report for a location: one aggregate KPI row plus sampled listings.
&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;market&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lisbon, Portugal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sampleSize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;marketMonths&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/airbnb-data-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To track one property's forward prices and occupancy instead, switch the mode and pass a listing URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;run_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;occupancy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;startUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.airbnb.com/rooms/39896685&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;months&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The five modes are &lt;code&gt;discover&lt;/code&gt; (listings by location), &lt;code&gt;availability&lt;/code&gt; (12-month calendar and price by date), &lt;code&gt;occupancy&lt;/code&gt; (occupancy, ADR, RevPAR), &lt;code&gt;reviews&lt;/code&gt; (full review history with sentiment), and &lt;code&gt;market&lt;/code&gt; (the aggregate KPI report). Only the one input the selected mode needs is used, &lt;code&gt;location&lt;/code&gt; for discover and market, &lt;code&gt;startUrls&lt;/code&gt; for availability, occupancy, and reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the output looks like
&lt;/h2&gt;

&lt;p&gt;Trimmed from a real &lt;code&gt;occupancy&lt;/code&gt; run for a single listing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"listingId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"39896685"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.airbnb.com/rooms/39896685"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"months"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"adr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;168.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"occupancy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"revpar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;121.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"availableNights"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;84&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bookedNights"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;216&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fields you get depend on the mode you run:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What each row carries&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;discover&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One row per listing: nightly price, property details, coordinates, rating, host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;availability&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The forward calendar, with the real price for each available date (opt-in)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;occupancy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Occupancy, ADR, RevPAR and the booked-vs-available night counts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reviews&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Each review with its rating, text, host reply, and sentiment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;market&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One aggregate KPI row (ADR, occupancy, RevPAR, revenue) plus the sampled listings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Field names are documented on the listing and in the repo below rather than invented here, so what you build against matches what the actor emits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on a real dataset first
&lt;/h2&gt;

&lt;p&gt;If you want to see the shape before running anything, there is a free &lt;a href="https://factden.com/sample-airbnb" rel="noopener noreferrer"&gt;sample Airbnb dataset&lt;/a&gt; you can open in the browser, and the &lt;a href="https://github.com/factden/airbnb-data-scraper" rel="noopener noreferrer"&gt;input, snippets, and field reference&lt;/a&gt; are on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need an Airbnb API key or login?&lt;/strong&gt;&lt;br&gt;
No. There is no key and no Airbnb login. You only need an Apify token to run the actor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I track prices and occupancy over time?&lt;/strong&gt;&lt;br&gt;
Save your input as a Task and attach a daily Schedule. Each run appends a fresh snapshot, so you build a price-and-occupancy time series you can feed straight into a revenue-management model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this really an AirDNA alternative?&lt;/strong&gt;&lt;br&gt;
For the core numbers, yes. You get ADR, occupancy, RevPAR, forward availability, and a market KPI report, but pay per run instead of a fixed monthly subscription, and the data lands in your own pipeline rather than a dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How far forward does availability go?&lt;/strong&gt;&lt;br&gt;
Up to 12 months of the forward calendar. Occupancy is most accurate for the near term, since months far out are still mostly unbooked, which is why the market report samples a shorter forward window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which currencies and languages does it support?&lt;/strong&gt;&lt;br&gt;
Prices can be returned in 20 currencies, and names, review text, and labels in several languages, so you can match a specific market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it legal?&lt;/strong&gt;&lt;br&gt;
It collects publicly visible data. As with any scraping, use the output responsibly and follow the applicable terms and laws for your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;p&gt;Working travel data across sites? The same team maintains a &lt;a href="https://apify.com/factden/google-hotels-scraper?fpr=factden" rel="noopener noreferrer"&gt;Google Hotels scraper&lt;/a&gt; for live hotel prices and the OTA rate ladder, and an &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews scraper&lt;/a&gt;, so you can line up rates and reviews across Airbnb, Google Hotels, and Expedia in the same format.&lt;/p&gt;

&lt;p&gt;Full write-up with more examples: &lt;a href="https://factden.com/blog/how-to-scrape-airbnb-data" rel="noopener noreferrer"&gt;how to scrape Airbnb data&lt;/a&gt;, and the &lt;a href="https://factden.com/airbnb-data-scraper" rel="noopener noreferrer"&gt;Airbnb data hub&lt;/a&gt; has the field reference and use cases.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>datascience</category>
    </item>
    <item>
      <title>How to Scrape TripAdvisor Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Wed, 05 Aug 2026 05:46:01 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-tripadvisor-reviews-in-2026-python-a-no-code-shortcut-3al2</link>
      <guid>https://dev.to/factden/how-to-scrape-tripadvisor-reviews-in-2026-python-a-no-code-shortcut-3al2</guid>
      <description>&lt;p&gt;Here is the thing most people hit when they try to scrape &lt;a href="https://www.tripadvisor.com" rel="noopener noreferrer"&gt;TripAdvisor reviews&lt;/a&gt;: the official TripAdvisor Content API returns only &lt;strong&gt;3 reviews&lt;/strong&gt; per location. Three. For a hotel with eight thousand reviews, that is not a dataset, that is a teaser. And it is gated behind an approval process, with no owner responses and no subratings.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from a TripAdvisor review, why the DIY route is fiddly, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-tripadvisor-reviews" rel="noopener noreferrer"&gt;guide to scraping TripAdvisor reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/SBJPKMIv_o8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The overall star rating&lt;/strong&gt; (1 to 5), the review &lt;strong&gt;title&lt;/strong&gt;, and the full &lt;strong&gt;review text&lt;/strong&gt; with its language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-review subratings&lt;/strong&gt;: cleanliness, service, value, location, rooms, sleep quality, where the reviewer left them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dates&lt;/strong&gt;: the published date and the travel date, plus &lt;strong&gt;helpful votes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner / management responses&lt;/strong&gt;, with responder name and date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer profile&lt;/strong&gt;: username, home location, contribution count&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this works across all three TripAdvisor place types: &lt;strong&gt;hotels, restaurants, and attractions&lt;/strong&gt;. Every run also returns a &lt;strong&gt;place record&lt;/strong&gt; with property details (rating, structured city ranking, price range, hotel class, amenities), TripAdvisor's &lt;strong&gt;official category subratings&lt;/strong&gt;, and its own &lt;strong&gt;AI review summary&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping TripAdvisor is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The official API caps at 3 reviews.&lt;/strong&gt; So for anything at scale, scraping the public data is the route, and there is no owner-response or subrating data in that API anyway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three different place types, three URL formats.&lt;/strong&gt; Hotels use &lt;code&gt;Hotel_Review&lt;/code&gt;, restaurants use &lt;code&gt;Restaurant_Review&lt;/code&gt;, attractions use &lt;code&gt;Attraction_Review&lt;/code&gt;. A scraper built for one type misses the others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews are paginated and loaded dynamically&lt;/strong&gt;, not sitting in the initial HTML, so a plain request gets you a shell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; on top.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is handling each place type, paging through every review, pulling the nested subratings and owner responses, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;TripAdvisor Hotel Reviews API (actor)&lt;/th&gt;
&lt;th&gt;Official Content API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reviews per location&lt;/td&gt;
&lt;td&gt;All, if you build paging&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Only 3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Approval required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hotels + restaurants + attractions&lt;/td&gt;
&lt;td&gt;Build per type&lt;/td&gt;
&lt;td&gt;One run&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subratings + owner responses&lt;/td&gt;
&lt;td&gt;Extra parsing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Property details + AI summary&lt;/td&gt;
&lt;td&gt;Separate scrape&lt;/td&gt;
&lt;td&gt;Same run&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;Gated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not much&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a hotel page returns a JavaScript shell, not the reviews:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.tripadvisor.com/Hotel_Review-g187497-d1465497-Reviews-W_Barcelona-Barcelona_Catalonia.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# usually False; reviews load separately, paginated
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the real data you page through every review, normalize hotels vs restaurants vs attractions, dig the subratings and owner responses out of nested structures, and keep requests unblocked. Fine for one place, a lot of moving parts for a portfolio or a whole city.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/tripadvisor-hotel-reviews-api?fpr=factden" rel="noopener noreferrer"&gt;TripAdvisor Hotel Reviews API&lt;/a&gt; on Apify takes any TripAdvisor URL (or a bare location ID), and returns structured JSON: every review plus the place record. No login, no proxy setup, it handles the blocking for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/tripadvisor-hotel-reviews-api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;startUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.tripadvisor.com/Hotel_Review-g187497-d1465497-Reviews-W_Barcelona-Barcelona_Catalonia.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locationIds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1465497&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewLanguages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste URLs or bare location IDs (the &lt;code&gt;d&lt;/code&gt;-number in the URL), mix them freely. Set &lt;code&gt;maxReviews&lt;/code&gt; high for all of them. Filter by &lt;code&gt;reviewLanguages&lt;/code&gt;, bound the star range with &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;, and window the dates with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; (both &lt;code&gt;YYYY-MM-DD&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Don't know the places yet? Switch to &lt;strong&gt;Discover mode&lt;/strong&gt; and search a whole city:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/tripadvisor-hotel-reviews-api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discover&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchTerms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Barcelona&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;placeTypes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxPlaces&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discoveryDepth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns a list of the city's places with full details. Take the returned location IDs back into &lt;strong&gt;Reviews mode&lt;/strong&gt; to pull their reviews.&lt;/p&gt;

&lt;h3&gt;
  
  
  Track reputation over time
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;fromDate&lt;/code&gt; filter is the one to know if you care about reputation monitoring. Set a &lt;strong&gt;Schedule&lt;/strong&gt; in Apify, point &lt;code&gt;fromDate&lt;/code&gt; at your last run, and each run brings back &lt;strong&gt;only the new reviews and rating changes&lt;/strong&gt; since then. That is how teams track new reviews and ratings over time without re-pulling the whole history every time, one cheap incremental pull on a cron.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 16 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;placeId&lt;/code&gt;, &lt;code&gt;placeType&lt;/code&gt;, &lt;code&gt;placeName&lt;/code&gt;, &lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rating&lt;/code&gt; (1 to 5), &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;subratings&lt;/code&gt; (cleanliness, service, value, location, rooms, sleep quality)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;text&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dates &amp;amp; signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;publishedDate&lt;/code&gt;, &lt;code&gt;travelDate&lt;/code&gt;, &lt;code&gt;helpfulVotes&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ownerResponse&lt;/code&gt; (responder, text, date)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;user&lt;/code&gt; (username, userLocation, contributions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"placeId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1465497&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"placeType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hotel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"placeName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"W Barcelona"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1070232616"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Incredible stay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The rooftop and the service were exceptional..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publishedDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-26T23:11:20-04:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"helpfulVotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subratings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Location"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ownerResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"responder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GM W Barcelona"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thank you..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"publishedDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-28"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"traveler_bcn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"userLocation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"London, UK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"contributions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"markdownContent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"# Incredible stay, W Barcelona&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;**Rating:** 5/5 ..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every run also emits a &lt;strong&gt;place record&lt;/strong&gt; per place: name, geo, overall rating, structured city ranking, price range, hotel class, amenities, TripAdvisor's official category subratings, and its AI review summary. Full field list and snippets are in the &lt;a href="https://github.com/factden/tripadvisor-hotel-reviews-api" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free TripAdvisor sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-tripadvisor" rel="noopener noreferrer"&gt;factden.com/sample-tripadvisor&lt;/a&gt;&lt;/strong&gt;. Load it into pandas and the ratings and subratings are ready to plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping TripAdvisor legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check TripAdvisor's Terms of Service and your local rules (including GDPR/CCPA for the pseudonymous reviewer fields), and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there an official TripAdvisor reviews API?&lt;/strong&gt; Yes, the Content API, but it is gated and returns only 3 reviews per location, with no owner responses or subratings. Scraping the public data is the practical route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape restaurants and attractions too, not just hotels?&lt;/strong&gt; Yes. One actor covers hotels, restaurants, and attractions, with the same schema, tagged by &lt;code&gt;placeType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are ratings on the 5-star scale?&lt;/strong&gt; Yes, TripAdvisor uses 1 to 5 stars, and that is what &lt;code&gt;rating&lt;/code&gt; returns, with per-review subratings on the same scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I monitor a place for new reviews on a schedule?&lt;/strong&gt; Yes. Set a Schedule and use &lt;code&gt;fromDate&lt;/code&gt; so each run only returns reviews posted since the last pull, ideal for ongoing reputation monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; The actor handles it for you and reads the data directly. Plain requests get a JavaScript shell, not the reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen hotel scrapers: &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape MakeMyTrip and Goibibo Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Wed, 05 Aug 2026 05:45:11 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-makemytrip-and-goibibo-reviews-in-2026-python-a-no-code-shortcut-jai</link>
      <guid>https://dev.to/factden/how-to-scrape-makemytrip-and-goibibo-reviews-in-2026-python-a-no-code-shortcut-jai</guid>
      <description>&lt;p&gt;If you want honest guest sentiment for a hotel in India, one platform is never enough. MakeMyTrip and Goibibo are the country's two largest OTAs, both run by the same group, and the same property usually carries a different pool of reviews on each. A traveller who books on Goibibo often leaves a review Goibibo never shows on MakeMyTrip, and the other way round. To read a hotel's real reputation in the India travel market you need both feeds, side by side.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from a review, why the DIY route is a two-headed problem, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full guide on how to &lt;a href="https://factden.com/makemytrip-scraper" rel="noopener noreferrer"&gt;scrape MakeMyTrip and Goibibo reviews&lt;/a&gt; too, plus a step-by-step &lt;a href="https://factden.com/blog/how-to-scrape-makemytrip-reviews" rel="noopener noreferrer"&gt;blog walkthrough&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/FENk4XlX2rE"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating&lt;/strong&gt; and the review &lt;strong&gt;title&lt;/strong&gt;, plus the hotel's &lt;strong&gt;sub-ratings&lt;/strong&gt; (Location, Cleanliness, Facilities, Food, Room, Value for Money, Child-friendliness) on the free hotel profile&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The full review text&lt;/strong&gt;, and the &lt;strong&gt;&lt;code&gt;source&lt;/code&gt;&lt;/strong&gt; brand so you always know whether a row came from MakeMyTrip or Goibibo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trip context&lt;/strong&gt;: travel type (couple, family, business, solo), room name, and check-in month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer detail&lt;/strong&gt;: name (or an anonymity flag), and lifetime review count on Goibibo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review photos with AI scene-tags&lt;/strong&gt; (&lt;code&gt;images&lt;/code&gt; as &lt;code&gt;[{ url, aiTags }]&lt;/code&gt;), plus owner/management responses with a date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review (&lt;code&gt;markdownContent&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two things that separate a real dataset from a shallow one: the &lt;strong&gt;second brand's feed&lt;/strong&gt; and the &lt;strong&gt;free hotel profile&lt;/strong&gt; (star class, geo, sub-ratings) that ships with every run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping MakeMyTrip and Goibibo is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;There is no public reviews API.&lt;/strong&gt; MakeMyTrip Group offers no public reviews or hotel-data API for either brand, so scraping the public pages is the only route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is two systems, not one.&lt;/strong&gt; MakeMyTrip and Goibibo share one hotel identity but present reviews through different pages and structures. A scraper built for one usually misses the other, and the brand you skip is often where the newer reviews are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The hotel profile lives apart from the reviews.&lt;/strong&gt; Star class, property type, PIN code, geo-coordinates and the clubbed sub-ratings sit on a separate surface you have to fetch and join yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; apply on both brands.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is not parsing one page. It is handling both brands, joining the hotel profile, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;MakeMyTrip &amp;amp; Goibibo actor&lt;/th&gt;
&lt;th&gt;Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (no public reviews API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Both MakeMyTrip + Goibibo feeds&lt;/td&gt;
&lt;td&gt;Build two scrapers&lt;/td&gt;
&lt;td&gt;One run&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Free hotel profile (stars, geo, sub-ratings)&lt;/td&gt;
&lt;td&gt;Fetch and join yourself&lt;/td&gt;
&lt;td&gt;Included free&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review photos + owner responses&lt;/td&gt;
&lt;td&gt;Parse nested markup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a hotel page tends to come back blocked or JavaScript-only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.makemytrip.com/hotels/hotel-details/?hotelId=200703241029455940&amp;amp;city=CTGOI&amp;amp;country=IN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# challenge / JS-rendered shell, reviews not in the HTML
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reviews load through internal endpoints, so you end up reverse-engineering those (per brand), normalizing two different response shapes, and separately fetching the hotel profile to join on. Doable for one hotel, painful for a portfolio on a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/makemytrip-scraper?fpr=factden" rel="noopener noreferrer"&gt;MakeMyTrip &amp;amp; Goibibo Reviews Scraper&lt;/a&gt; on Apify reads &lt;strong&gt;both brands in a single run&lt;/strong&gt; and hands back structured JSON, with the full hotel profile included free. No login, no proxy setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/makemytrip-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;startUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.makemytrip.com/hotels/hotel-details/?hotelId=200703241029455940&amp;amp;city=CTGOI&amp;amp;country=IN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.goibibo.com/hotels/hard-rock-goa-hotel-in-goa-6204281054243107966/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewSource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerHotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mostRecent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;travelType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mix MakeMyTrip and Goibibo URLs freely. &lt;code&gt;reviewSource&lt;/code&gt; takes &lt;code&gt;auto&lt;/code&gt; (match each link's own brand), &lt;code&gt;makemytrip&lt;/code&gt;, &lt;code&gt;goibibo&lt;/code&gt;, or &lt;code&gt;both&lt;/code&gt; (collect both brands' reviews for the same hotel). &lt;code&gt;sortBy&lt;/code&gt; accepts &lt;code&gt;mostRelevant&lt;/code&gt;, &lt;code&gt;mostRecent&lt;/code&gt;, &lt;code&gt;helpful&lt;/code&gt;, &lt;code&gt;positive&lt;/code&gt;, or &lt;code&gt;negative&lt;/code&gt;, and you can bound results with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because this is one actor with a lean input, you can &lt;strong&gt;schedule it to monitor ratings and new reviews across both OTAs over time&lt;/strong&gt;: set &lt;code&gt;sortBy: "mostRecent"&lt;/code&gt; and slide &lt;code&gt;fromDate&lt;/code&gt; to your last run date, and each nightly pass pulls only what is new, for a few cents. That is the difference between a one-off pull and a live reputation feed for a whole portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 19 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt; (makemytrip / goibibo), &lt;code&gt;submittedAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reviewText&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewer&lt;/code&gt; (name, isAnonymous, reviewsWritten for Goibibo)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trip context&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;travelType&lt;/code&gt;, &lt;code&gt;roomName&lt;/code&gt;, &lt;code&gt;checkInMonth&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;usefulCount&lt;/code&gt;, &lt;code&gt;imagesCount&lt;/code&gt;, &lt;code&gt;images&lt;/code&gt; (url + aiTags), &lt;code&gt;ownerResponse&lt;/code&gt; (text, date)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;&lt;code&gt;extractedAt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row (a MakeMyTrip review):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"01KXQ014AHMXEWYHEBNBX9ZSCY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Caravela Beach Resort"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"makemytrip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"submittedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkInMonth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sumit Khare"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"isAnonymous"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reviewsWritten"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"COUPLE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"roomName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deluxe Room Sea View"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wonderful beachside stay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amazing experience. Great service, good food and a beautiful property..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imagesCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"images"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://.../photo.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"aiTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"palm_tree"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"grass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"house"&lt;/span&gt;&lt;span class="p"&gt;]}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ownerResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"markdownContent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"# Caravela Beach Resort review (MakeMyTrip)&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;**Rating:** 5/5..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every run also returns a free &lt;strong&gt;Hotels&lt;/strong&gt; row per property: &lt;code&gt;hotelStars&lt;/code&gt;, &lt;code&gt;propertyType&lt;/code&gt;, &lt;code&gt;hotelAddress&lt;/code&gt;, &lt;code&gt;pinCode&lt;/code&gt;, &lt;code&gt;geoLat&lt;/code&gt;, &lt;code&gt;geoLong&lt;/code&gt;, &lt;code&gt;overallRating&lt;/code&gt;, &lt;code&gt;ratingLabel&lt;/code&gt;, &lt;code&gt;reviewsCount&lt;/code&gt;, and the clubbed &lt;code&gt;subRatings&lt;/code&gt;. Full field list and snippets are in the &lt;a href="https://github.com/factden/makemytrip-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free MakeMyTrip and Goibibo sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-makemytrip" rel="noopener noreferrer"&gt;factden.com/sample-makemytrip&lt;/a&gt;&lt;/strong&gt;. It includes rows from both brands so you can see the shape before you run anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping MakeMyTrip or Goibibo legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check the sites' Terms of Service and your local rules (India's DPDP Act, GDPR, and so on), and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there an official MakeMyTrip / Goibibo reviews API?&lt;/strong&gt; No public one. MakeMyTrip Group offers no public reviews or hotel-data API for either brand, so scraping the public pages is the only route for most teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get both brands' reviews for the same hotel?&lt;/strong&gt; Yes. The two platforms share one hotel identity, so set &lt;code&gt;reviewSource: "both"&lt;/code&gt; and you get the MakeMyTrip and Goibibo review feeds for the same property, each row tagged by &lt;code&gt;source&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape both MakeMyTrip and Goibibo at once?&lt;/strong&gt; Yes, that is the point. Mix both URL types in one run (or paste raw hotel IDs) and the output uses the same schema, tagged by &lt;code&gt;source&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I track a hotel's rating over time?&lt;/strong&gt; Yes. Schedule the actor with &lt;code&gt;sortBy: "mostRecent"&lt;/code&gt; and a sliding &lt;code&gt;fromDate&lt;/code&gt;, and each run pulls only new reviews across both OTAs, cheaply, so you build a rating and sentiment trend without re-scraping everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Plain requests get challenged on both brands. This is the hard part of the job, and the actor handles it for you, no proxy or account setup required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing hotels across more platforms? See &lt;a href="https://dev.to/factden/how-to-scrape-tripcom-and-ctrip-hotel-reviews-in-2026-python-a-no-code-shortcut-in6"&gt;how to scrape Trip.com and Ctrip reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com &amp;amp; Ctrip Reviews Scraper&lt;/a&gt; and &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Google Hotels Prices and Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Wed, 22 Jul 2026 10:43:01 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-google-hotels-prices-and-reviews-in-2026-python-a-no-code-shortcut-1hd8</link>
      <guid>https://dev.to/factden/how-to-scrape-google-hotels-prices-and-reviews-in-2026-python-a-no-code-shortcut-1hd8</guid>
      <description>&lt;p&gt;Google Hotels is one of the richest travel datasets on the public web. For any property it shows a live price, a ladder of booking sources (Booking.com, Agoda, Expedia, the hotel's own site, and a dozen more), per-room rates, star class, guest ratings, and reviews. If you run a hotel, resell rooms, build a travel app, or feed a pricing model, that is exactly the data you want. Getting it out cleanly is the hard part.&lt;/p&gt;

&lt;p&gt;This guide shows both ways: a runnable Python recipe, and a no-code shortcut with the &lt;a href="https://apify.com/factden/google-hotels-scraper?fpr=factden" rel="noopener noreferrer"&gt;Google Hotels Scraper&lt;/a&gt; that skips the hard parts entirely.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/5m-_lNJ8bnw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live prices for a stay, with the full OTA rate ladder per hotel (every booking source and its price plus a deep booking link).&lt;/li&gt;
&lt;li&gt;Per-room rates where the source shares them, so you see the room-level breakdown, not just a single "from" price.&lt;/li&gt;
&lt;li&gt;Hotel facts: name, star class, guest rating, review count, property type, coordinates, and a stable Google entity token.&lt;/li&gt;
&lt;li&gt;Guest reviews with exact publish dates and owner responses, in a separate dataset.&lt;/li&gt;
&lt;li&gt;A price window: track prices across a range of dates for the same set of hotels.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why scraping Google Hotels directly is hard
&lt;/h2&gt;

&lt;p&gt;Google Hotels does not render its prices in simple HTML you can select with CSS. The data comes from Google's internal travel endpoints, which expect a specific TLS fingerprint and reject plain HTTP clients, and the price ladder is spread across several nested response containers that change shape by property and by date. On top of that, the values are keyed on internal Google entity tokens, not on anything you would guess from a URL. You can solve all of this, but it is real engineering, and it breaks whenever Google reshapes a response.&lt;/p&gt;

&lt;p&gt;That is the whole reason a maintained actor exists: it absorbs the endpoint work and hands you a flat table.&lt;/p&gt;

&lt;h2&gt;
  
  
  DIY vs actor vs official API
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Write it yourself&lt;/th&gt;
&lt;th&gt;Google Hotels Scraper (actor)&lt;/th&gt;
&lt;th&gt;Official Google API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;TLS impersonation, proxies, response parsing&lt;/td&gt;
&lt;td&gt;Paste a destination and dates&lt;/td&gt;
&lt;td&gt;Partner onboarding required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prices and OTA ladder&lt;/td&gt;
&lt;td&gt;You reverse-engineer it&lt;/td&gt;
&lt;td&gt;Included, structured&lt;/td&gt;
&lt;td&gt;Not available for open scraping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviews with dates&lt;/td&gt;
&lt;td&gt;Separate endpoint to solve&lt;/td&gt;
&lt;td&gt;Included, exact dates and owner replies&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;You own every breakage&lt;/td&gt;
&lt;td&gt;Maintained for you&lt;/td&gt;
&lt;td&gt;Contractual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access&lt;/td&gt;
&lt;td&gt;Free but you build it&lt;/td&gt;
&lt;td&gt;Free tier, then pay per result&lt;/td&gt;
&lt;td&gt;Gated to hotel owners and OTAs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no public Google API that lets you pull arbitrary hotel prices and reviews. The Hotel APIs Google does publish are gated to hotel owners and booking partners for their own inventory. For open collection, scraping is the route, and the choice is really "build and maintain it" versus "call an actor".&lt;/p&gt;

&lt;h2&gt;
  
  
  The no-code way (about a minute)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a free &lt;a href="https://console.apify.com/sign-up?fpr=factden" rel="noopener noreferrer"&gt;Apify account&lt;/a&gt; and open the actor page.&lt;/li&gt;
&lt;li&gt;Click Try for free. The input is pre-filled with an example search.&lt;/li&gt;
&lt;li&gt;Set your destination and dates, or paste exact hotels, then click Start.&lt;/li&gt;
&lt;li&gt;Download the results from the Output tab as JSON, CSV, or Excel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can search a destination the way you would type it on Google Hotels (&lt;code&gt;hotels in New York City&lt;/code&gt;, &lt;code&gt;Paris 5 star hotels&lt;/code&gt;, &lt;code&gt;resorts in Bali&lt;/code&gt;), or add exact hotels one per line: a hotel name, a Google Hotels URL, a Google Maps link, or an entity token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it from Python
&lt;/h2&gt;

&lt;p&gt;The actor runs on Apify, so you drive it with the Apify client. Install it with &lt;code&gt;pip install apify-client&lt;/code&gt;, grab your token from Apify Settings, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;run_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;searchQueries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotels in New York City&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkInDate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-08-15&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkOutDate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-08-16&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includePrices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;includeReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxResults&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/google-hotels-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Hotels dataset: one row per hotel, with the price ladder nested on the row
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;hotel&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hotel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hotel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;leadPrice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hotel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hotel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendorCount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reviews land in a separate named dataset, so you can process prices and reviews independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a hotel row looks like
&lt;/h2&gt;

&lt;p&gt;Trimmed from a real run (The Marmara Park Avenue, New York):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Marmara Park Avenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"starClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1070&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"leadPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"leadPriceTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vendorCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"offers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Booking.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"checkInDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-11-15"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"checkOutDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-11-16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"roomName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deluxe Queen (DLXQ)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"perNight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;287&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;287&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"official"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bookingLink"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.google.com/aclk?..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key fields on each hotel row:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;starClass&lt;/code&gt;, &lt;code&gt;propertyType&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Hotel identity and class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rating&lt;/code&gt;, &lt;code&gt;reviewCount&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Aggregate guest score and how many reviews&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;leadPrice&lt;/code&gt;, &lt;code&gt;leadPriceTotal&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Cheapest per-night and total for the stay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vendorCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How many booking sources sell this hotel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;offers[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The full priced table: one row per source, date, and room, each with a booking link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vendors[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Booking directory (source plus link, no prices)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A self-contained summary block, ready to drop into an LLM or RAG pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Reviews carry &lt;code&gt;publishedAt&lt;/code&gt; (the exact date, not "2 months ago"), &lt;code&gt;reviewText&lt;/code&gt;, &lt;code&gt;rating&lt;/code&gt;, and &lt;code&gt;ownerResponse&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on a real dataset first
&lt;/h2&gt;

&lt;p&gt;If you want to see the shape before running anything, there is a free &lt;a href="https://factden.com/sample-google-hotels" rel="noopener noreferrer"&gt;sample Google Hotels dataset&lt;/a&gt; you can open in the browser, and the &lt;a href="https://github.com/factden/google-hotels-scraper" rel="noopener noreferrer"&gt;input, snippets, and field reference&lt;/a&gt; are on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need a Google API key?&lt;/strong&gt;&lt;br&gt;
No. There is no key and no Google login. You only need an Apify token to run the actor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get prices for many dates at once?&lt;/strong&gt;&lt;br&gt;
Yes. Set a price-window end date and each date's prices are added to the &lt;code&gt;offers&lt;/code&gt; table for the same set of hotels, which is how you track rate movements over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it return per-room rates or just a "from" price?&lt;/strong&gt;&lt;br&gt;
Per-room rates when the booking source shares them with Google. Sources that only hand Google a single lead price show one row; the per-room breakdown for those lives only on the OTA's own site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How are reviews dated?&lt;/strong&gt;&lt;br&gt;
With the exact publish date decoded to ISO-8601, plus the hotel's owner response when there is one. Most relayed feeds only give you a relative string like "3 weeks ago".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it legal?&lt;/strong&gt;&lt;br&gt;
It collects publicly visible data. As with any scraping, use the output responsibly and follow the applicable terms and laws for your use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does it cost?&lt;/strong&gt;&lt;br&gt;
There is a free tier to try it, then you pay per result. Discovery is cheap, prices are an opt-in per hotel per date, and reviews are billed per review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;p&gt;Working hotel data across sites? The same team maintains an &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews scraper&lt;/a&gt;, a &lt;a href="https://apify.com/factden/hotels-com-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Hotels.com reviews scraper&lt;/a&gt;, and a &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip reviews scraper&lt;/a&gt;, so you can line up reviews across Google, Expedia, Hotels.com, and Trip.com in the same format.&lt;/p&gt;

&lt;p&gt;Full write-up with more examples: &lt;a href="https://factden.com/blog/how-to-scrape-google-hotels" rel="noopener noreferrer"&gt;how to scrape Google Hotels&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>datascience</category>
    </item>
    <item>
      <title>How to Scrape Hotels.com Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:04:09 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-hotelscom-reviews-in-2026-python-a-no-code-shortcut-3p7a</link>
      <guid>https://dev.to/factden/how-to-scrape-hotelscom-reviews-in-2026-python-a-no-code-shortcut-3p7a</guid>
      <description>&lt;p&gt;&lt;a href="https://www.hotels.com" rel="noopener noreferrer"&gt;Hotels.com&lt;/a&gt; is part of Expedia Group, which is good news and a small headache. Good news: its reviews share the same backend as Expedia, Travelocity, Orbitz, and the rest, so one approach covers all of them. The headache: Hotels.com still uses its old &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; URL format, which has to be resolved to the global property before you can pull anything.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from a Hotels.com review, why the DIY route is fiddly, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-hotels-com-reviews" rel="noopener noreferrer"&gt;guide to scraping Hotels.com reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/cDrmnCP9lhU"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating on the /10 scale&lt;/strong&gt; plus a label, and category &lt;strong&gt;sub-ratings&lt;/strong&gt;: Cleanliness, Service, Room comfort, Hotel condition, Amenities, Eco-friendliness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The full review text&lt;/strong&gt;, detected language, and a translation flag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay context&lt;/strong&gt;: check-in and check-out dates, travel companions, traveler category&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer detail&lt;/strong&gt;, verified flag, helpful votes, review photos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner responses&lt;/strong&gt;, and a &lt;code&gt;brandType&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same clean, structured shape whether the review came from Hotels.com or any sibling brand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Hotels.com is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No public reviews API and no API key on offer.&lt;/strong&gt; Scraping the public pages is the route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; legacy id.&lt;/strong&gt; A Hotels.com URL like &lt;code&gt;hotels.com/ho119566/&lt;/code&gt; does not carry the global property id directly; it has to be resolved first, or your request goes nowhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews load dynamically&lt;/strong&gt;, so a plain request returns a JavaScript shell, not the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; on top.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is resolving the legacy id, hitting the review endpoints, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Hotels.com Reviews Scraper (actor)&lt;/th&gt;
&lt;th&gt;Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (no API key)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; resolution&lt;/td&gt;
&lt;td&gt;You build it&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/10 ratings + 6 sub-ratings&lt;/td&gt;
&lt;td&gt;Parse nested markup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner responses&lt;/td&gt;
&lt;td&gt;Extra parsing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a Hotels.com page returns a shell, and the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; id is not the one the review endpoint expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.hotels.com/ho119566/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# usually False; ho-id must be resolved, reviews load separately
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the data you resolve the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; to the global property id, call the review endpoints, and normalize the response. Fine for one hotel, several moving parts for a portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/hotels-com-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Hotels.com Reviews Scraper&lt;/a&gt; on Apify resolves the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; id for you and returns structured JSON. No login, no API key, no proxy setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/hotels-com-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotelUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.hotels.com/ho119566/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;242128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerHotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass Hotels.com &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; URLs or bare ids, and any other Expedia Group brand URL works too. &lt;code&gt;sortBy&lt;/code&gt; takes &lt;code&gt;newest&lt;/code&gt;, &lt;code&gt;oldest&lt;/code&gt;, &lt;code&gt;highestRating&lt;/code&gt;, or &lt;code&gt;lowestRating&lt;/code&gt;; bound results with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 27 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;brandType&lt;/code&gt;, &lt;code&gt;submittedAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt; (/10), &lt;code&gt;ratingLabel&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (cleanliness, service, room comfort, condition, amenities, eco)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewText&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;, &lt;code&gt;isMachineTranslated&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stay&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;checkInDate&lt;/code&gt;, &lt;code&gt;checkOutDate&lt;/code&gt;, &lt;code&gt;travelCompanions&lt;/code&gt;, &lt;code&gt;travelerCategories&lt;/code&gt;, &lt;code&gt;roomTypeId&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewerName&lt;/code&gt;, &lt;code&gt;reviewerLocation&lt;/code&gt;, &lt;code&gt;verified&lt;/code&gt;, &lt;code&gt;isAnonymous&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;helpfulVotes&lt;/code&gt;, &lt;code&gt;imagesCount&lt;/code&gt;, &lt;code&gt;reviewPhotos&lt;/code&gt;, &lt;code&gt;ownerResponse&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brandType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hotels.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Venetian Resort"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratingLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wonderful"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cleanliness: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service: 9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hotel condition: 9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amenities: 10"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Great suite, easy check-in, would stay again."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelerCategories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Couple"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkInDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkOutDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"helpfulVotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full field list and snippets are in the &lt;a href="https://github.com/factden/hotels-com-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Hotels.com sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-hotels" rel="noopener noreferrer"&gt;factden.com/sample-hotels&lt;/a&gt;&lt;/strong&gt;. Load it into pandas and the /10 ratings and sub-ratings are ready to plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Hotels.com legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check Hotels.com's Terms of Service and your local rules, and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there a Hotels.com API?&lt;/strong&gt; No public reviews API and no key on offer. Scraping the public pages is the route.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; thing?&lt;/strong&gt; Hotels.com uses a legacy property id in its URLs that does not match the global Expedia Group id. It has to be resolved first, which the actor does automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape Expedia and the other brands too?&lt;/strong&gt; Yes. Hotels.com shares the Expedia Group backend, so the same actor handles Expedia, Travelocity, Orbitz, Wotif, CheapTickets, and ebookers, same schema, tagged by &lt;code&gt;brandType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies and slow pacing, or a tool that resolves the id and reads the review endpoints directly. Plain requests get a JavaScript shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Expedia Hotel Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:02:08 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-expedia-hotel-reviews-in-2026-python-a-no-code-shortcut-3m9o</link>
      <guid>https://dev.to/factden/how-to-scrape-expedia-hotel-reviews-in-2026-python-a-no-code-shortcut-3m9o</guid>
      <description>&lt;p&gt;Here is the thing most people miss about scraping &lt;a href="https://www.expedia.com" rel="noopener noreferrer"&gt;Expedia&lt;/a&gt;: Expedia, Hotels.com, Travelocity, Orbitz, Wotif, CheapTickets, and ebookers all run on the &lt;strong&gt;same backend&lt;/strong&gt;. The same hotel shows up across all seven brands, and their reviews pool together. If you scrape only &lt;code&gt;expedia.com&lt;/code&gt;, you leave most of the data on the table.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from an Expedia Group review, why the DIY route is fiddly, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-expedia-reviews" rel="noopener noreferrer"&gt;guide to scraping Expedia reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/V9XmWuaHp5E"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating on the /10 scale&lt;/strong&gt; plus a label, and category &lt;strong&gt;sub-ratings&lt;/strong&gt;: Cleanliness, Service, Room comfort, Hotel condition, Amenities, Eco-friendliness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The full review text&lt;/strong&gt;, detected language, and a translation flag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay context&lt;/strong&gt;: check-in and check-out dates, travel companions, traveler category&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer detail&lt;/strong&gt;, verified flag, helpful votes, review photos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner responses&lt;/strong&gt;, and a &lt;code&gt;brandType&lt;/code&gt; telling you which of the seven brands the review came from&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;brandType&lt;/code&gt; field plus the shared backend are what let you pull every brand's reviews for a property in one pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Expedia is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;There is no public reviews API.&lt;/strong&gt; Expedia Group does not offer one, so scraping the public pages is the route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seven brands, different URL formats.&lt;/strong&gt; Six brands embed the global property id in a &lt;code&gt;.h&amp;lt;id&amp;gt;.&lt;/code&gt; path; Hotels.com uses a legacy &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; id that has to be resolved. A scraper built for one brand misses the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews are loaded dynamically&lt;/strong&gt;, not sitting in the initial HTML, so a plain request gets you a shell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; on top.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is resolving ids across brands, hitting the review endpoints, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Expedia Reviews Scraper (actor)&lt;/th&gt;
&lt;th&gt;Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (no public reviews API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All 7 Expedia Group brands&lt;/td&gt;
&lt;td&gt;Build a scraper per brand&lt;/td&gt;
&lt;td&gt;One run&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/10 ratings + 6 sub-ratings&lt;/td&gt;
&lt;td&gt;Parse nested markup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner responses&lt;/td&gt;
&lt;td&gt;Extra parsing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a hotel page returns a JavaScript shell, not the reviews:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.expedia.com/Las-Vegas-Hotels-Bellagio.h140596.Hotel-Information&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# usually False; reviews load via internal endpoints
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the data you reverse-engineer the review endpoints, map each brand's URL format to the global property id (including the Hotels.com &lt;code&gt;/ho&amp;lt;id&amp;gt;/&lt;/code&gt; resolution), and normalize the responses. Fine for one hotel, a lot of moving parts for a portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia Reviews Scraper&lt;/a&gt; on Apify takes any Expedia Group hotel URL (or a bare property id), resolves it, and returns structured JSON. No login, no proxy setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/expedia-hotel-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hotelUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.expedia.com/Las-Vegas-Hotels-Bellagio.h140596.Hotel-Information&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;140596&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.hotels.com/ho115902/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerHotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;newest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brandType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mix brands and bare ids freely. &lt;code&gt;sortBy&lt;/code&gt; takes &lt;code&gt;newest&lt;/code&gt;, &lt;code&gt;oldest&lt;/code&gt;, &lt;code&gt;highestRating&lt;/code&gt;, or &lt;code&gt;lowestRating&lt;/code&gt;; bound results with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;; and use &lt;code&gt;reviewSources&lt;/code&gt; to keep only certain brands' reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 27 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;brandType&lt;/code&gt;, &lt;code&gt;submittedAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt; (/10), &lt;code&gt;ratingLabel&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (cleanliness, service, room comfort, condition, amenities, eco)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewText&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;, &lt;code&gt;isMachineTranslated&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stay&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;checkInDate&lt;/code&gt;, &lt;code&gt;checkOutDate&lt;/code&gt;, &lt;code&gt;travelCompanions&lt;/code&gt;, &lt;code&gt;travelerCategories&lt;/code&gt;, &lt;code&gt;roomTypeId&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewerName&lt;/code&gt;, &lt;code&gt;reviewerLocation&lt;/code&gt;, &lt;code&gt;verified&lt;/code&gt;, &lt;code&gt;isAnonymous&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;helpfulVotes&lt;/code&gt;, &lt;code&gt;imagesCount&lt;/code&gt;, &lt;code&gt;reviewPhotos&lt;/code&gt;, &lt;code&gt;ownerResponse&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"brandType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Expedia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bellagio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratingLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Exceptional"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cleanliness: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hotel condition: 10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Amenities: 10"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rooms were great. Buffet was awesome. Nice casino."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelerCategories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Friends"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkInDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-01"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checkOutDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-05-04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"helpfulVotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full field list and snippets are in the &lt;a href="https://github.com/factden/expedia-hotel-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Expedia sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-expedia" rel="noopener noreferrer"&gt;factden.com/sample-expedia&lt;/a&gt;&lt;/strong&gt;. Load it into pandas and the /10 ratings and sub-ratings are ready to plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Expedia legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check Expedia's Terms of Service and your local rules, and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there an official Expedia reviews API?&lt;/strong&gt; No public one. Scraping the public pages is the route for most teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape Hotels.com and the other brands too?&lt;/strong&gt; Yes. All seven Expedia Group brands share the backend, so one actor covers Expedia, Hotels.com, Travelocity, Orbitz, Wotif, CheapTickets, and ebookers, with the same schema tagged by &lt;code&gt;brandType&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are ratings on the 5-star or 10-point scale?&lt;/strong&gt; Expedia Group uses /10, and that is what the actor returns for &lt;code&gt;overallRating&lt;/code&gt; and the sub-ratings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies and slow pacing, or a tool that reads the review endpoints directly. Plain requests get a JavaScript shell, not the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/hotels-com-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Hotels.com reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Trip.com and Ctrip Hotel Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 08:00:07 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-tripcom-and-ctrip-hotel-reviews-in-2026-python-a-no-code-shortcut-in6</link>
      <guid>https://dev.to/factden/how-to-scrape-tripcom-and-ctrip-hotel-reviews-in-2026-python-a-no-code-shortcut-in6</guid>
      <description>&lt;p&gt;Most "Trip.com scrapers" only see half the picture. &lt;a href="https://www.trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt; is the international brand, but the same hotel usually has a much larger pool of reviews on its Chinese-domestic sibling, &lt;code&gt;hotels.ctrip.com&lt;/code&gt; (携程). If you want honest guest sentiment for a property in Asia, you need both, and you need the Chinese ones translated.&lt;/p&gt;

&lt;p&gt;This guide covers what you can pull from a Trip.com or Ctrip review, why the DIY route is a two-headed problem, working Python, a no-code shortcut, and a plain comparison. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-ctrip-trip-reviews" rel="noopener noreferrer"&gt;guide to scraping Trip.com and Ctrip reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/se_1GF9pEZU"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from a review
&lt;/h2&gt;

&lt;p&gt;Per review you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating&lt;/strong&gt; and a label ("Outstanding"), plus &lt;strong&gt;sub-ratings&lt;/strong&gt;: Cleanliness, Location, Service, Facilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The review text, plus an English translation&lt;/strong&gt; of Chinese reviews (&lt;code&gt;reviewTextTranslated&lt;/code&gt;), and the detected &lt;code&gt;language&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trip context&lt;/strong&gt;: travel type (family, couple, business, solo), room name, check-in month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer detail&lt;/strong&gt;: tier ("Review star"), lifetime review count, and for Ctrip the reviewer's Chinese province&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner responses&lt;/strong&gt; (text and date), useful counts, photo/video flags&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An LLM-ready markdown block&lt;/strong&gt; per review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two things that separate a real dataset from a shallow one: the &lt;strong&gt;Chinese-domestic feed&lt;/strong&gt; and its &lt;strong&gt;translation&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Trip.com and Ctrip is hard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;There is no public reviews API.&lt;/strong&gt; Trip.com Group does not offer one, so scraping the public pages is the only route.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is two systems, not one.&lt;/strong&gt; &lt;code&gt;trip.com&lt;/code&gt; (international) and &lt;code&gt;hotels.ctrip.com&lt;/code&gt; / 携程 (Chinese) have different structures and languages. A scraper built for one usually misses the other, and the Chinese pool is often the larger one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chinese reviews need translation&lt;/strong&gt; to be usable in an English pipeline, which is a whole extra step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot protection and rate limits&lt;/strong&gt; apply on both.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is not parsing one page. It is handling both locales, translating, and staying unblocked, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get the data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Trip.com &amp;amp; Ctrip actor&lt;/th&gt;
&lt;th&gt;Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (no public reviews API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Both Trip.com + Ctrip feeds&lt;/td&gt;
&lt;td&gt;Build two scrapers&lt;/td&gt;
&lt;td&gt;One run&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chinese review translation&lt;/td&gt;
&lt;td&gt;Add a translation step&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-ratings + owner responses&lt;/td&gt;
&lt;td&gt;Parse nested markup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;A plain request to a hotel page tends to come back blocked or JavaScript-only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.trip.com/hotels/macau-hotel-detail-344983/galaxy-hotel/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# challenge / JS-rendered shell, reviews not in the HTML
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reviews load through internal endpoints, so you end up reverse-engineering those (per locale), then normalizing two different response shapes, then translating the Chinese text. Doable for one hotel, painful for a portfolio on a schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you want clean rows, the &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com &amp;amp; Ctrip Reviews Scraper&lt;/a&gt; on Apify reads &lt;strong&gt;both feeds in a single run&lt;/strong&gt; and hands back structured JSON with the Chinese reviews already translated. No login, no proxy setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/ctrip-trip-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;startUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.trip.com/hotels/macau-hotel-detail-344983/galaxy-hotel/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://hotels.ctrip.com/hotels/1286148.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerHotel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortBy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mostRecent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;travelType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mix &lt;code&gt;trip.com&lt;/code&gt; and &lt;code&gt;hotels.ctrip.com&lt;/code&gt; URLs freely. &lt;code&gt;sortBy&lt;/code&gt; takes &lt;code&gt;mostRelevant&lt;/code&gt;, &lt;code&gt;mostRecent&lt;/code&gt;, &lt;code&gt;ratingHighToLow&lt;/code&gt;, or &lt;code&gt;ratingLowToHigh&lt;/code&gt;, and you can bound results with &lt;code&gt;fromDate&lt;/code&gt; / &lt;code&gt;toDate&lt;/code&gt; and &lt;code&gt;minRating&lt;/code&gt; / &lt;code&gt;maxRating&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 23 fields per review
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewId&lt;/code&gt;, &lt;code&gt;hotelId&lt;/code&gt;, &lt;code&gt;hotelName&lt;/code&gt;, &lt;code&gt;hotelUrl&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt; (trip.com / ctrip), &lt;code&gt;submittedAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt;, &lt;code&gt;ratingLabel&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (Cleanliness, Location, Service, Facilities)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewText&lt;/code&gt;, &lt;code&gt;reviewTextTranslated&lt;/code&gt;, &lt;code&gt;isMachineTranslated&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewer&lt;/code&gt; (name, tier, lifetime reviews, Chinese province for Ctrip)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trip context&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;travelType&lt;/code&gt;, &lt;code&gt;roomName&lt;/code&gt;, &lt;code&gt;checkInMonth&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signals&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;recommends&lt;/code&gt;, &lt;code&gt;usefulCount&lt;/code&gt;, &lt;code&gt;imagesCount&lt;/code&gt;, &lt;code&gt;hasVideo&lt;/code&gt;, &lt;code&gt;ownerResponse&lt;/code&gt; (text, date)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markdownContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row (a Chinese review, translated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ctrip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hotelName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Galaxy Hotel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratingLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Outstanding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Cleanliness: 5.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Location: 5.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service: 5.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Facilities: 5.0"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"酒店很棒，服务一流，位置方便。"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewTextTranslated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Great hotel, first-class service, convenient location."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isMachineTranslated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"travelType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Family"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rising review star"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lifetimeReviews"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ownerResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"感謝您的正面評價..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-15"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full field list and snippets are in the &lt;a href="https://github.com/factden/ctrip-trip-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Trip.com/Ctrip sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-trip" rel="noopener noreferrer"&gt;factden.com/sample-trip&lt;/a&gt;&lt;/strong&gt;. It includes both English and translated-Chinese reviews so you can see the shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Trip.com or Ctrip legal?&lt;/strong&gt; The reviews are publicly visible. As with any scraping, check the sites' Terms of Service and your local rules, and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there an official Trip.com / Ctrip reviews API?&lt;/strong&gt; No public one. Scraping the public pages is the only route for most teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I get the Chinese reviews in English?&lt;/strong&gt; Yes. Ctrip reviews come with &lt;code&gt;reviewTextTranslated&lt;/code&gt; and an &lt;code&gt;isMachineTranslated&lt;/code&gt; flag, alongside the original.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I scrape both Trip.com and Ctrip at once?&lt;/strong&gt; Yes, that is the point. Mix both URL types in one run and the output uses the same schema, tagged by &lt;code&gt;source&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies and slow pacing, or a tool that reads the internal endpoints per locale. Plain requests will get challenged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing B2B software instead of hotels? See &lt;a href="https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/expedia-hotel-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Expedia hotel reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed jobs and salary data&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape Indeed Jobs in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 07:56:17 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-indeed-jobs-in-2026-python-a-no-code-shortcut-o5j</link>
      <guid>https://dev.to/factden/how-to-scrape-indeed-jobs-in-2026-python-a-no-code-shortcut-o5j</guid>
      <description>&lt;p&gt;Scraping &lt;a href="https://www.indeed.com" rel="noopener noreferrer"&gt;Indeed&lt;/a&gt; sounds simple until you try it. There is no public API anymore, the site sits behind Cloudflare, and the salary you actually want arrives as a messy string like &lt;code&gt;"$120,000 - $160,000 a year"&lt;/code&gt;. This guide covers what you can pull from an Indeed job, why the DIY route is harder than it looks, working Python, a no-code shortcut, and a straight comparison so you can pick a lane. If you want the deeper reference, there is a full &lt;a href="https://factden.com/blog/how-to-scrape-indeed-jobs" rel="noopener noreferrer"&gt;guide to scraping Indeed jobs&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/cunIg0VWD6I"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can pull from an Indeed job
&lt;/h2&gt;

&lt;p&gt;Per listing you can get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Title, company, and location&lt;/strong&gt; (plus city, state, country code, and lat/long)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary, parsed into numbers&lt;/strong&gt;: &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryPeriod&lt;/code&gt;, &lt;code&gt;currency&lt;/code&gt;, not just the raw string&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Job type, remote/hybrid flag, and the full description text&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Benefits, date posted, easy-apply and urgent-hire flags&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Company data on demand&lt;/strong&gt;: rating, review count, industry, size, revenue, founded, website, and socials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Parsed salary and company enrichment are the two things people actually come for, and the two things a naive scrape gets wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping Indeed is hard
&lt;/h2&gt;

&lt;p&gt;Three reasons DIY is more work than it looks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;There is no official API.&lt;/strong&gt; Indeed retired its public job-search API to new developers years ago, so there is no supported programmatic route. Scraping is the only option left.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare and rate limits.&lt;/strong&gt; A plain request to a search results page tends to return a &lt;code&gt;403&lt;/code&gt; or a challenge. Hammer it and you get blocked fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messy salary strings.&lt;/strong&gt; Indeed shows salary as free text. Turning &lt;code&gt;"$120,000 - $160,000 a year"&lt;/code&gt; into clean &lt;code&gt;salaryMin&lt;/code&gt;/&lt;code&gt;salaryMax&lt;/code&gt;/&lt;code&gt;period&lt;/code&gt; reliably, across currencies and 40 countries, is fiddly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the work is not the parsing of one page. It is staying unblocked, normalizing salary, and pulling company data from separate pages, on repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get Indeed job data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;Indeed Jobs Scraper (actor)&lt;/th&gt;
&lt;th&gt;Indeed Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Not available (retired)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anti-bot handled&lt;/td&gt;
&lt;td&gt;You (Cloudflare, blocks)&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salary as numbers&lt;/td&gt;
&lt;td&gt;You parse the string&lt;/td&gt;
&lt;td&gt;Yes: &lt;code&gt;salaryMin&lt;/code&gt; / &lt;code&gt;salaryMax&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Company data&lt;/td&gt;
&lt;td&gt;Separate scrape&lt;/td&gt;
&lt;td&gt;Free, one toggle&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Countries&lt;/td&gt;
&lt;td&gt;You handle localization&lt;/td&gt;
&lt;td&gt;40+&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;Pay-per-result&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off search&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Not an option&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The official-API column is the real story: since Indeed closed its API, a scraper (yours or a ready-made one) is the only way to get this data programmatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;See the wall first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.indeed.com/jobs?q=software+engineer&amp;amp;l=New+York&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# often 403 / Cloudflare challenge
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do get HTML back, the job cards parse roughly like this (selectors change often, so re-check against the live page):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;div.job_seen_beacon&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h2.jobTitle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;comp&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[data-testid=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;company-name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sal&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[data-testid=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attribute_snippet_testid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;comp&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;sal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sal&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# raw string, still needs parsing
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you still have to normalize that salary string into numbers, follow each job to its full description, and scrape the company page separately. For one search that is fine. For a pipeline, the upkeep adds up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you just want clean rows, the &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed Jobs Scraper&lt;/a&gt; on Apify talks to Indeed's structured endpoints (no fragile HTML path), handles the blocking, and returns parsed JSON. No login, no proxy setup.&lt;/p&gt;

&lt;p&gt;From Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/indeed-jobs-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;software engineer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;New York, NY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# native salary-range filter
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scrapeCompany&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# attach company profiles for free
&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;salaryMax&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;country&lt;/code&gt; to any of 40+ codes (&lt;code&gt;US&lt;/code&gt;, &lt;code&gt;GB&lt;/code&gt;, &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;DE&lt;/code&gt;, &lt;code&gt;SG&lt;/code&gt;, ...), filter by &lt;code&gt;remote&lt;/code&gt;, &lt;code&gt;datePosted&lt;/code&gt;, &lt;code&gt;jobType&lt;/code&gt;, or &lt;code&gt;experienceLevel&lt;/code&gt;, or pass &lt;code&gt;startUrls&lt;/code&gt; / &lt;code&gt;jobKeys&lt;/code&gt; to fetch specific listings.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 26 structured fields per job
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;company&lt;/code&gt;, &lt;code&gt;location&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;countryCode&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;jobKey&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salary&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;salary&lt;/code&gt; (raw), &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryPeriod&lt;/code&gt;, &lt;code&gt;currency&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job detail&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;jobType&lt;/code&gt;, &lt;code&gt;remote&lt;/code&gt;, &lt;code&gt;occupations&lt;/code&gt;, &lt;code&gt;benefits&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;datePosted&lt;/code&gt;, &lt;code&gt;isUrgentHire&lt;/code&gt;, &lt;code&gt;easyApply&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geo&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;latitude&lt;/code&gt;, &lt;code&gt;longitude&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Company (with &lt;code&gt;scrapeCompany&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;companyRating&lt;/code&gt;, &lt;code&gt;companyReviewCount&lt;/code&gt;, &lt;code&gt;companyPageUrl&lt;/code&gt; + a full company profile (industry, size, revenue, founded, website, socials)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Senior Software Engineer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Plaid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New York, NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$120,000 - $160,000 a year"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salaryMin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salaryMax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"salaryPeriod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jobType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Full-time"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"benefits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Health insurance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"401(k)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"datePosted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"companyRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.indeed.com/viewjob?jk=6d50b3ebeb3fb122"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jobKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6d50b3ebeb3fb122"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numeric salary fields and the on-demand company profile are what make this usable for labor-market analysis without a cleanup pass. Full field list and snippets are in the &lt;a href="https://github.com/factden/indeed-jobs-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data first? There is a free Indeed jobs sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample-indeed" rel="noopener noreferrer"&gt;factden.com/sample-indeed&lt;/a&gt;&lt;/strong&gt;. Drop it into pandas and the parsed salary columns are ready to plot.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping Indeed legal?&lt;/strong&gt; The listings are publicly visible. As with any scraping, check Indeed's Terms of Service and your local rules (especially around personal data), and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Indeed have an API?&lt;/strong&gt; Not a usable one. Indeed closed its public job-search API to new developers, which is exactly why people scrape the public pages now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies, real headers, and slow pacing, or a tool that talks to Indeed's structured endpoints instead of scraping HTML. Plain requests to the search pages will get challenged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get salary as numbers?&lt;/strong&gt; Yes. &lt;code&gt;salaryMin&lt;/code&gt;, &lt;code&gt;salaryMax&lt;/code&gt;, &lt;code&gt;salaryPeriod&lt;/code&gt;, and &lt;code&gt;currency&lt;/code&gt; are parsed from the raw string, so you can filter and aggregate directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get company data too?&lt;/strong&gt; Yes. Turn on &lt;code&gt;scrapeCompany&lt;/code&gt; and each job carries the company's rating, size, industry, revenue, and profile links, no second scrape needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which countries are supported?&lt;/strong&gt; 40+, including US, UK, Canada, India, Germany, Singapore, Australia, and more, via the &lt;code&gt;country&lt;/code&gt; code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Doing competitive intelligence instead of hiring data? See &lt;a href="https://dev.to/factden/i-scraped-50000-g2-reviews-to-map-the-2026-saas-battlecard-atlas-12jg"&gt;how to scrape G2 reviews&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;G2 software reviews&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Scrape G2 Reviews in 2026 (Python + a No-Code Shortcut)</title>
      <dc:creator>Factden</dc:creator>
      <pubDate>Tue, 14 Jul 2026 07:52:34 +0000</pubDate>
      <link>https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb</link>
      <guid>https://dev.to/factden/how-to-scrape-g2-reviews-in-2026-python-a-no-code-shortcut-18eb</guid>
      <description>&lt;p&gt;If you have tried to &lt;strong&gt;scrape G2 reviews&lt;/strong&gt; with a quick &lt;code&gt;requests.get()&lt;/code&gt;, you already know how it goes: a &lt;code&gt;403&lt;/code&gt;, a CAPTCHA, or a blank page. G2 is one of the tougher public sites to pull at scale. But the data behind it (ratings, structured pros and cons, and which competitor a reviewer switched away from and why) is worth the trouble if you do any competitive intelligence.&lt;/p&gt;

&lt;p&gt;This guide covers what a G2 review actually contains, why the naive approach fails, a Python path that works, a no-code shortcut, and a plain comparison so you can pick the right route. If you want a deeper field reference, there is a longer &lt;a href="https://factden.com/blog/how-to-scrape-g2-reviews" rel="noopener noreferrer"&gt;guide to scraping G2 reviews&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/CFqYAR4xoTk"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What a G2 review actually contains
&lt;/h2&gt;

&lt;p&gt;One &lt;a href="https://www.g2.com" rel="noopener noreferrer"&gt;G2&lt;/a&gt; review holds more than it looks. Per review you can pull:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Overall rating&lt;/strong&gt; (1 to 5) plus &lt;strong&gt;six sub-ratings&lt;/strong&gt; (ease of use, ease of setup, quality of support, meets requirements, and more)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured pros, cons, and "problems solved"&lt;/strong&gt; as separate fields, not one text blob&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switching data&lt;/strong&gt;: the competitor the reviewer came from, and why they left (the field most people are actually after)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer context&lt;/strong&gt;: industry, role, company size, country&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dates, verification, and the incentivized flag&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sub-ratings and switching fields are the reason to scrape G2 instead of skimming it by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why scraping G2 is hard: DataDome
&lt;/h2&gt;

&lt;p&gt;G2 runs &lt;a href="https://datadome.co/" rel="noopener noreferrer"&gt;DataDome&lt;/a&gt; bot protection behind a Cloudflare CDN. In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A plain &lt;code&gt;requests&lt;/code&gt; or &lt;code&gt;httpx&lt;/code&gt; GET comes back &lt;code&gt;403&lt;/code&gt; almost immediately.&lt;/li&gt;
&lt;li&gt;Swapping the &lt;code&gt;User-Agent&lt;/code&gt; does nothing. DataDome fingerprints TLS, headers, and behavior.&lt;/li&gt;
&lt;li&gt;Datacenter IPs get flagged fast. You need residential proxies plus a real browser fingerprint, or a scraping API that brings both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can absolutely scrape G2 yourself. The hard part is doing it reliably, at scale, without babysitting proxies. That maintenance tax is bigger than the parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to get G2 review data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Python&lt;/th&gt;
&lt;th&gt;G2 Reviews Scraper (actor)&lt;/th&gt;
&lt;th&gt;G2 Official API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;Hours to days&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;td&gt;Weeks (sales + contract)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anti-bot handled&lt;/td&gt;
&lt;td&gt;You (proxies + browser)&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured fields&lt;/td&gt;
&lt;td&gt;You parse them&lt;/td&gt;
&lt;td&gt;27, incl. switching data&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-ratings + "switched from"&lt;/td&gt;
&lt;td&gt;Hard to parse&lt;/td&gt;
&lt;td&gt;Yes, resolved to product names&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Proxies + eng time&lt;/td&gt;
&lt;td&gt;$0.004/row, ~1,250 free&lt;/td&gt;
&lt;td&gt;Enterprise contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;One-off, few pages&lt;/td&gt;
&lt;td&gt;Scheduled, at scale&lt;/td&gt;
&lt;td&gt;Large enterprises&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Option A: DIY in Python
&lt;/h2&gt;

&lt;p&gt;See the wall for yourself first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.g2.com/products/slack/reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# 403 (DataDome)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting past it needs a residential proxy and a browser-grade fingerprint (a stealth headless browser, or a scraping API). Once you have the HTML, parsing review cards with BeautifulSoup looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;review&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ratingValue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_one&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[itemprop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reviewBody&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things that will cost you time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Selectors drift. G2 changes its markup, so re-check against the live page.&lt;/li&gt;
&lt;li&gt;Sub-ratings and switching data sit in nested markup that is fiddly to parse consistently.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;DIY is fine for a one-off across a handful of pages. For hundreds of products on a schedule, the proxy and anti-bot upkeep usually costs more than it saves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: the no-code / API shortcut
&lt;/h2&gt;

&lt;p&gt;When you just want clean rows, the &lt;a href="https://apify.com/factden/g2-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;G2 Reviews Scraper&lt;/a&gt; on Apify handles the DataDome, proxy, and parsing layer and hands back structured JSON. No login, no proxy setup.&lt;/p&gt;

&lt;p&gt;From Python, using the Apify client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;apify_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApifyClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ApifyClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_APIFY_TOKEN&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;factden/g2-reviews-scraper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;startUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.g2.com/products/slack/reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxReviewsPerProduct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sortReviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;helpful&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;defaultDatasetId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;iterate_items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overallRating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewTitle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;previousCompetitors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass full product URLs or bare slugs (&lt;code&gt;notion&lt;/code&gt;, &lt;code&gt;slack&lt;/code&gt;). There is also a Products mode that finds competitor products by keyword before you pull their reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back: 27 structured fields
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ratings&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;overallRating&lt;/code&gt;, &lt;code&gt;subRatings&lt;/code&gt; (ease of use, setup, support, meets requirements)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pros&lt;/code&gt;, &lt;code&gt;cons&lt;/code&gt;, &lt;code&gt;problemsSolved&lt;/code&gt;, &lt;code&gt;recommendations&lt;/code&gt;, &lt;code&gt;reviewText&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switching / battlecard&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;didSwitchFromCompetitor&lt;/code&gt;, &lt;code&gt;previousCompetitors&lt;/code&gt;, &lt;code&gt;whySwitched&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviewer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reviewerIndustry&lt;/code&gt;, &lt;code&gt;reviewerRole&lt;/code&gt;, &lt;code&gt;companySize&lt;/code&gt;, &lt;code&gt;reviewerCountry&lt;/code&gt;, &lt;code&gt;reviewerName&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;isIncentivized&lt;/code&gt;, &lt;code&gt;helpfulVotes&lt;/code&gt;, &lt;code&gt;submittedAt&lt;/code&gt;, &lt;code&gt;reviewUrl&lt;/code&gt;, &lt;code&gt;productSlug&lt;/code&gt;, &lt;code&gt;productName&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-ready&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;markdownContent&lt;/code&gt; (a self-contained markdown block per review, for RAG / vector DBs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A trimmed sample row, so you can see the shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"productName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Slack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overallRating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subRatings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"easeOfUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"easeOfSetup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qualityOfSupport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"meetsRequirements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewTitle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Runs our whole company"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pros"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Channels keep every project in one place."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Notifications get noisy at scale."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"didSwitchFromCompetitor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"previousCompetitors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Microsoft Teams"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"whySwitched"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Better threads and a faster mobile app."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"companySize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mid-Market"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reviewerRole"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IT Administrator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isIncentivized"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two fields most scrapers drop, &lt;code&gt;previousCompetitors&lt;/code&gt; + &lt;code&gt;whySwitched&lt;/code&gt; (resolved to real product names) and the per-dimension &lt;code&gt;subRatings&lt;/code&gt;, are what make this useful for battlecards. Full field list and copy-paste snippets are in the &lt;a href="https://github.com/factden/g2-reviews-scraper" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab a free sample dataset
&lt;/h2&gt;

&lt;p&gt;Want to see the data before running anything? There is a free G2 review sample (CSV/JSON) here: &lt;strong&gt;&lt;a href="https://factden.com/sample" rel="noopener noreferrer"&gt;factden.com/sample&lt;/a&gt;&lt;/strong&gt; (also mirrored on HuggingFace and Kaggle). Load it into pandas and the sub-ratings and switching fields show up straight away.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is scraping G2 reviews legal?&lt;/strong&gt; The reviews are publicly available. As with any scraping, check G2's Terms of Service and your local rules (GDPR and similar for personal data), and use the data responsibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does G2 have an API?&lt;/strong&gt; Yes, but it is enterprise-tier: a sales call, a contract, and procurement. For most teams, scraping the public pages, or using a ready-made actor, is the faster route to the same public data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I stop getting blocked?&lt;/strong&gt; Residential proxies, a real browser fingerprint, and human-like pacing, or a tool that bundles all three. Datacenter IPs and plain requests will not survive DataDome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much does it cost?&lt;/strong&gt; DIY costs proxies plus your time. The actor is pay-per-result at $0.004 per row, with about 1,250 rows free on Apify's $5 new-account credit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get the "switched from" competitor data?&lt;/strong&gt; Yes. That is the &lt;code&gt;previousCompetitors&lt;/code&gt; and &lt;code&gt;whySwitched&lt;/code&gt; fields, resolved to product names. It is the reason most people scrape G2 in the first place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I filter out incentivized (gift-card) reviews?&lt;/strong&gt; Yes. Every row carries an &lt;code&gt;isIncentivized&lt;/code&gt; flag, so you can recompute a rating on organic reviews only. G2's own UI will not let you do that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I mapped switching and sub-ratings across 25 tools in &lt;a href="https://dev.to/factden/i-scraped-50000-g2-reviews-to-map-the-2026-saas-battlecard-atlas-12jg"&gt;the 2026 SaaS Battlecard Atlas&lt;/a&gt; (50,000 reviews).&lt;/li&gt;
&lt;li&gt;Other FactDen scrapers: &lt;a href="https://apify.com/factden/indeed-jobs-scraper?fpr=factden" rel="noopener noreferrer"&gt;Indeed jobs and salary data&lt;/a&gt; and &lt;a href="https://apify.com/factden/ctrip-trip-reviews-scraper?fpr=factden" rel="noopener noreferrer"&gt;Trip.com and Ctrip hotel reviews&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, or a field you wish it extracted? Drop a comment.&lt;/p&gt;

&lt;p&gt;Found this useful? A ⭐ on the GitHub repo (&lt;a href="https://github.com/factden/g2-reviews-scraper" rel="noopener noreferrer"&gt;https://github.com/factden/g2-reviews-scraper&lt;/a&gt;) helps other people find it.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
