Skip to main content
Thirdwatchthirdwatch
Reviews & ratings

Track How Agoda Management Responses Affect Hotel Ratings

Measure your hotel's review response rate, reply latency and the rating trend that follows, using structured Agoda review data with responder fields attached.

Sep 8, 2026 · 6 min read · 1,307 words
See the scraper →

Most hotels can tell you their score. Very few can tell you what share of their one- and two-star reviews got a public reply, how long that reply took, or whether the score moved afterwards. Thirdwatch's Agoda Reviews Scraper returns responderName, responseText and responseDate alongside every rating and review date, which turns response-programme performance into an ordinary reporting query.

Skip the setup: Run this as a ready-to-go task on Apify — pre-loaded with the configuration from this guide.

Why scrape Agoda reviews to measure response performance

Responding to guest reviews is one of the few reputation levers a hotel controls directly, and it is almost always managed on vibes. A revenue manager will know the property's score to one decimal place and have no idea whether the front-office team answered eleven or ninety per cent of last quarter's complaints. Most hospitality groups set a response-rate target in a brand standard and then never measure it, because the data lives one property page at a time inside Agoda.

The job-to-be-done splits three ways. A brand-standards auditor needs response rate and median latency per property, segmented by rating band, to see which hotels are quietly ignoring their negative reviews. A guest-experience lead needs the text of the replies themselves, to check whether the team is writing templated apologies or actually addressing what the guest wrote. A group executive wants the before-and-after: did the score trend change in the two quarters after a response programme started?

All three need the same shape of data — every review, its rating, its date, and whether and when the property replied — for a set of properties, on a repeatable schedule. That is what the Actor returns as flat rows.

How does this compare to the alternatives?

Three ways to get response-programme data:

Approach Cost model Reliability Setup time Maintenance
Manual audit in the browser Analyst hours per property Sampling only, not census Hours per property Repeats in full every quarter
Reputation-management suite Per-property subscription Good, but its own definitions Days, with onboarding Vendor contract
Thirdwatch Agoda Reviews Scraper Pay per review row Full census of public reviews Minutes Thirdwatch tracks Agoda changes

A manual audit is fine once and unmaintainable quarterly. A reputation-management suite will report a response rate, but on its own definition of the denominator, which is exactly the number you want to control when you are auditing your own teams. The Agoda Reviews Scraper actor page gives you the raw rows so response rate is whatever you define it to be, computed the same way every quarter.

How to measure response performance in 4 steps

Step 1: How do I pull reviews with their replies attached?

Response fields are on every row by default. For a response audit, sort oldest-scoring first and keep the low bands, because that is where the gap lives.

{
  "hotelUrls": [
    "https://www.agoda.com/the-taj-mahal-palace-mumbai/hotel/mumbai-in.html",
    "https://www.agoda.com/the-peninsula-bangkok/hotel/bangkok-th.html"
  ],
  "maxReviewsPerHotel": 500,
  "rating": ["below_expectation", "good"],
  "sortBy": "rating_low",
  "enabledProviders": ["Agoda", "Booking.com"],
  "language": "all",
  "aggregateRatings": true
}

rating accepts Agoda's own bands: exceptional, excellent, very_good, good and below_expectation. Leave it empty when you want the full population to compute response rate across all bands rather than only the low ones.

Step 2: How do I compute response rate and latency?

Every row has responseText. Non-empty means the property replied publicly.

import os
import pandas as pd
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("thirdwatch/agoda-reviews-scraper").call(run_input={
    "hotelUrls": [
        "https://www.agoda.com/the-taj-mahal-palace-mumbai/hotel/mumbai-in.html",
        "https://www.agoda.com/the-peninsula-bangkok/hotel/bangkok-th.html",
    ],
    "maxReviewsPerHotel": 500,
    "sortBy": "recent",
})
df = pd.DataFrame(client.dataset(run["defaultDatasetId"]).iterate_items())

df["answered"] = df["responseText"].fillna("").str.strip().ne("")
df["reviewDate"] = pd.to_datetime(df["reviewDate"], utc=True, errors="coerce")
df["responseDate"] = pd.to_datetime(df["responseDate"], utc=True, errors="coerce")
df["latency_days"] = (df["responseDate"] - df["reviewDate"]).dt.total_seconds() / 86400

report = (
    df.groupby(["hotelName", "ratingCategory"])
    .agg(
        reviews=("hotelReviewId", "nunique"),
        response_rate=("answered", "mean"),
        median_latency_days=("latency_days", "median"),
    )
    .reset_index()
)
print(report.sort_values(["hotelName", "response_rate"]))

Report the median latency, not the mean. One review answered eight months late will drag a mean into meaninglessness while the median still describes the team's real habit.

Step 3: How do I tell a real reply from a template?

Pull the replies themselves and look at their diversity. A property running a genuine programme produces replies that vary with the complaint; a property running a macro produces near-identical text.

answered = df[df.answered].copy()
answered["reply_len"] = answered.responseText.str.len()

template_check = (
    answered.groupby("hotelName")
    .agg(
        replies=("hotelReviewId", "count"),
        distinct_replies=("responseText", "nunique"),
        median_reply_len=("reply_len", "median"),
        responders=("responderName", "nunique"),
    )
    .reset_index()
)
template_check["uniqueness"] = (
    template_check.distinct_replies / template_check.replies
)
print(template_check)

A uniqueness ratio near 1.0 with a healthy median length is a team writing real replies. A ratio near 0.1 means one paragraph is being pasted onto every review, which reads exactly as badly to a prospective guest as no reply at all.

Step 4: How do I test whether the score trend changed?

Bucket reviews by month, compute the mean rating and the response rate in each bucket, and look at the two series together. Correlation over a portfolio is far more informative than any single property.

monthly = (
    df.assign(month=df.reviewDate.dt.to_period("M"))
    .groupby(["hotelName", "month"])
    .agg(
        mean_rating=("rating", "mean"),
        response_rate=("answered", "mean"),
        volume=("hotelReviewId", "nunique"),
    )
    .reset_index()
)
monthly["rating_3m"] = (
    monthly.groupby("hotelName").mean_rating.transform(
        lambda s: s.rolling(3, min_periods=2).mean()
    )
)
print(monthly.tail(24))

Be honest about what this shows. A reply cannot retroactively change a score that is already published, so any effect has to run through future behaviour — review volume, the mix of guests who bother to write, and how a reader weighs an old complaint that has a considered answer under it. Treat the relationship as directional evidence for a programme decision, not as a causal estimate. Schedule the pull weekly using the Apify schedules documentation so the series keeps extending itself.

Sample output

A review with a management response looks like this:

{
  "hotelId": 16082488,
  "hotelName": "The Taj Mahal Palace, Mumbai",
  "hotelReviewId": 998341207,
  "reviewProviderText": "Agoda",
  "rating": 5.2,
  "ratingText": "Below expectation",
  "ratingCategory": "below_expectation",
  "reviewTitle": "Great building, tired room",
  "reviewComments": "Heritage wing room needed maintenance and the air conditioning was noisy.",
  "reviewPositives": "Location, staff",
  "reviewNegatives": "Room maintenance, noisy air conditioning",
  "reviewDate": "2026-07-14T18:40:00+05:30",
  "reviewerCountryName": "United Kingdom",
  "reviewerGroupName": "Couple",
  "reviewerRoomTypeName": "Luxury Grande Room",
  "responderName": "Guest Relations Manager",
  "responseText": "Thank you for staying with us. The air conditioning unit in that room has since been serviced and we would welcome the chance to host you in a refurbished room.",
  "responseDate": "2026-07-17T11:05:00+05:30",
  "helpfulVotes": 4,
  "combinedAggregateReviewScore": 8.9
}

The three response fields are the whole story: responderName tells you who owns the reply (a named manager reads better than a generic desk), responseText is the reply itself, and responseDate minus reviewDate is your latency. Rows where responseText is empty are your denominator gap.

Common pitfalls

Four traps in response analytics. Counting response rate over all reviews — most properties answer their glowing reviews, so a blended rate looks healthy while every complaint goes unanswered; always segment on ratingCategory. Treating latency as a mean — a small tail of very old replies destroys the average; use the median and report the ninetieth percentile separately. Mixing providers — response availability differs between the Agoda and Booking.com review populations shown on a property page, so compute rates separately using reviewProviderText. Claiming causation — response rate and rating trend move together for several reasons, and a public reply cannot change an already-published score; describe the relationship as directional.

Thirdwatch's Actor handles pagination, deduplication and the response-field extraction so you can focus on the audit rather than the collection.

Related use cases

Frequently asked questions

Which fields carry the management response?

Three fields on every review row: responderName, responseText and responseDate. Rows with an empty responseText were never answered publicly, which makes response rate a simple non-empty count over any slice you care about.

Can I measure how quickly a property replies?

Yes. Subtract reviewDate from responseDate on rows that carry both. Median latency by property and by rating band is usually more revealing than mean latency, because a handful of very old unanswered reviews will distort an average badly.

Does replying to reviews actually raise a hotel's score?

A reply cannot change a score that is already published. What responding plausibly changes is future behaviour: how many guests bother to leave a review, and how future readers weigh an existing complaint. Measure the trend after a response programme starts, not the individual review.

How do I isolate responses to negative reviews?

Filter the rating input to below_expectation and good, or filter rows client-side on ratingCategory. Response rate on low-rated stays is the metric worth reporting, since most properties answer their five-star reviews and skip the ones that matter.

Can I pull only reviews since my last run?

Yes. Set cutoffDate to your last run date and sortBy to recent. Reviews published before the cutoff are skipped while pagination continues, so a scheduled job stays small even for a property with tens of thousands of lifetime reviews.

Do Booking.com reviews shown on Agoda carry responses too?

Response availability varies by provider and by property. Keep reviewProviderText in your analysis and compute response rate separately per provider, otherwise a property that answers on one channel and not the other will look inconsistent for no operational reason.

Related

Try it yourself

100 free credits, no credit card.

About 30 real searches. Add the MCP to Claude or Cursor in two minutes.