# BEST Agoda Scraper (`bestscraper/agoda-property-scraper`) Actor

Every row gives you: name, stars, review score, nightly price (tax-inclusive), availability, cover image, address, geo. Two modes. Search a city for every hotel + homestay, or monitor specific properties across dates with price-change deltas and next-available on sold-out. Covers Agoda Homes.

- **URL**: https://apify.com/bestscraper/agoda-property-scraper.md
- **Developed by:** [lenskii](https://apify.com/bestscraper) (community)
- **Categories:** Travel, E-commerce
- **Stats:** 13 total users, 3 monthly users, 99.1% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## BEST Agoda Scraper

Two ways to use it:

- **Search a destination.** Paste an Agoda city URL and get every hotel and homestay in that city, with prices, availability, reviews, and free cancellation flags.
- **Monitor specific properties.** Paste property URLs and get detailed per-property intel (amenities, host info, review themes, location scores) across any date range.

Covers both regular hotels and private-host Agoda Homes. Flat $2 per 1,000 rows. No tiers, no subscription, no minimums.

---

### Quick start

#### Option A: search a whole city (most common)

1. Go to [agoda.com](https://www.agoda.com) and pick a destination.
2. Copy the URL from your browser's address bar.
3. Paste it into `searchDestinations`:

```json
{
  "searchDestinations": [
    "https://www.agoda.com/city/singapore-sg.html"
  ],
  "dateMatrix": {
    "startFrom": "today",
    "scanDays": 7,
    "lengthOfStay": 2,
    "stride": 1
  },
  "adults": 2,
  "currency": "USD",
  "searchMaxResults": 200
}
````

You get about 200 properties times 7 check-in dates, so around 1,400 rows covering the next week.

#### Option B: monitor specific properties

Use this when you already know the properties you want to track (for example, hourly price monitoring):

```json
{
  "propertyUrls": [
    "https://www.agoda.com/marina-bay-sands/hotel/singapore-sg.html"
  ],
  "dateMatrix": {
    "startFrom": "2026-05-01",
    "scanDays": 30,
    "lengthOfStay": 1,
    "stride": 7
  },
  "currency": "USD"
}
```

***

### Dates and guests

Control what gets scanned via `dateMatrix`:

- `startFrom`: `"today"` or a `YYYY-MM-DD` date.
- `scanDays`: how many consecutive check-in dates.
- `lengthOfStay`: nights per booking.
- `stride`: step between check-ins. 1 means daily, 7 means weekly snapshots.

Examples:

- `{scanDays: 14, stride: 1, lengthOfStay: 2}` gives 14 rows per property, next two weeks, 2-night stays.
- `{scanDays: 90, stride: 7, lengthOfStay: 1}` gives 13 weekly price points over the next quarter.

`adults`, `rooms`, `children` shape occupancy. Agoda hides rooms that can't fit the party.

***

### Discovering every hotel in a city (sortBy + skipSeen)

Agoda caps the number of unique properties you can see under any single sort order at roughly 650-1,000 per city. To uncover the long tail, run the same destination under **all four sorts** back-to-back and dedup between runs. This typically surfaces 1.5-2× more unique inventory than a single sort.

The actor has both pieces built in:

- `sortBy`: `"Recommended"` (default), `"PriceAsc"`, `"PriceDesc"`, or `"ReviewScore"`.
- `skipSeen`: when `true`, the actor remembers every propertyId it already returned for each city (in a private per-account store) and silently drops duplicates before they hit the dataset — so you never pay twice for the same hotel.

Recipe for full-city coverage:

```json
// Run 1: Recommended sort with skipSeen on — captures the bulk.
{
  "searchDestinations": ["https://www.agoda.com/city/singapore-sg.html"],
  "dateMatrix": { "startFrom": "today", "scanDays": 7, "lengthOfStay": 1 },
  "searchMaxResults": 1000,
  "sortBy": "Recommended",
  "skipSeen": true
}
// Runs 2-4: change sortBy to PriceAsc, then PriceDesc, then ReviewScore.
// Runs 2-4 only emit rows you haven't already seen — no double charge.
```

You can also pass `skipPropertyIds: [123, 456, ...]` directly if you maintain your own dedup list in an external database.

***

### Sold-out dates — find the nearest available (findNextAvailable)

Set `findNextAvailable: true` to annotate sold-out rows with the nearest available dates in your scan. Works for both search and monitor modes. Zero extra cost — reuses data already fetched in the same run.

On a sold-out row, these fields populate:

- `nextAvailableBefore`: `{ checkIn, checkOut, nights, priceNightly, daysOffset }` — the nearest bookable check-in **before** the sold-out date (-1, -2, or -3 days).
- `nextAvailableAfter`: same shape, but for +1/+2/+3 days.
- `nextAvailableCoverage` (search rows only): on sold-out rows where no neighbor was found in the scan, this object tells you *why* and gives you a drop-in monitor-mode input block you can use to re-run with guaranteed coverage for that one property.

Your `scanDays` window has to actually cover the ± 3-day neighborhood for annotations to appear. Scanning a single sold-out date in isolation gives you nothing to compare against.

Great for "is the trip shiftable?" answers: if Monday is sold out but Sunday and Tuesday are open, the row tells you.

***

### Output fields (search mode)

Every row represents one property for one check-in date.

| Field | Type | Description |
|---|---|---|
| `propertyId` | number | Agoda internal property ID |
| `name` | string | Property name |
| `propertyType` | string | Hotel, Homestay, Apartment, Hostel, etc. |
| `starRating` | number | 0 to 5. 0 means self-classified homestay. |
| `address` | string | Area, city, country |
| `geo` | `{lat, lng}` | Coordinates |
| `coverImage` | string | First property image (https URL) |
| `imageCount` | number | Total available images on Agoda |
| `checkIn` / `checkOut` | date | The dates this row represents |
| `nights` | number | Length of stay |
| `currency` | string | ISO currency code of all prices |
| `pricePerNight` | number | Cheapest nightly price, taxes included |
| `priceTotal` | number | Total stay price, taxes included |
| `originalPricePerNight` | number or null | Crossed-out price (pre-discount) |
| `discountPct` | number or null | Discount percentage |
| `saveUpToPerNight` | number or null | Savings per night vs. crossed-out |
| `isAvailable` | boolean | Can be booked for these dates |
| `soldOut` | boolean | Explicitly flagged as sold out |
| `availableRooms` | number or null | Rooms left at the cheapest rate |
| `freeCancellation` | boolean | Offer allows free cancellation |
| `cancellationType` | string | Raw policy code (`FreeCancellation`, `NonRefundable`, etc.) |
| `paymentModel` | string | `PayNow`, `PayAtHotel`, or `PayLater` |
| `reviewScore` | number | 0 to 10 |
| `reviewCount` | number | Total reviews |
| `reviewSnippets` | array | Topic-grouped review quotes |
| `topSellingPoint` | string | Agoda marketing badge (`HighlyRated`, `GreatValue`, etc.) |
| `rank` | number | Position in Agoda's default sort |
| `priceRankInCity` | number | 1-indexed rank by nightly price within the same city and check-in. 1 = cheapest available. |
| `priceRankOfTotal` | number | Count of available properties in the same city and check-in pool (so a consumer can print `rank of total`). |
| `priceVsCityMedianPct` | number | Signed percentage vs. pool median. `-30` means 30% cheaper than median, `+45` means 45% above. |
| `priceCategory` | string | One of `cheap`, `below-average`, `average`, `above-average`, `premium` (quintile bucket within the pool). |
| `propertyUrl` | string | Canonical agoda.com URL |

Note on images: every row includes `coverImage` (the first photo) and `imageCount` (how many photos Agoda has). The full photo gallery is not emitted.

#### City day summary rows

Once per `(cityId, checkIn)` pool, a row of kind `city-day-summary` is emitted alongside the property rows. Use it to see what the whole market looks like at a glance without aggregating client-side.

| Field | Type | Description |
|---|---|---|
| `totalProperties` | number | Total properties returned in the pool. |
| `availableCount` | number | How many had a bookable nightly price. |
| `soldOutCount` | number | How many were sold out or had no price. |
| `soldOutPct` | number | Sold-out share as a percentage (0 to 100). |
| `priceMin`, `priceP25`, `priceMedian`, `priceP75`, `priceMax` | number | Price distribution across available rows. |
| `priceStddev` | number | Sample standard deviation of nightly prices. |

#### Sample row

```json
{
  "type": "search-result",
  "scrapedAt": "2026-04-16T08:30:00.000Z",
  "cityId": 4064,
  "checkIn": "2026-04-20",
  "checkOut": "2026-04-22",
  "nights": 2,
  "currency": "USD",
  "propertyId": 8435201,
  "name": "Marina Bay Sands",
  "propertyType": "Hotel",
  "starRating": 5,
  "reviewScore": 9.1,
  "reviewCount": 847,
  "address": "Marina Bay, Singapore",
  "geo": { "lat": 1.2838, "lng": 103.8591 },
  "pricePerNight": 84.5,
  "priceTotal": 169.0,
  "originalPricePerNight": 120.0,
  "discountPct": 30,
  "isAvailable": true,
  "soldOut": false,
  "availableRooms": 3,
  "freeCancellation": true,
  "cancellationType": "FreeCancellation",
  "paymentModel": "PayLater",
  "reviewSnippets": [
    { "topic": "Location", "snippets": ["Walking distance to the waterfront"] },
    { "topic": "Staff", "snippets": ["Front desk was super helpful"] }
  ],
  "topSellingPoint": "HighlyRated",
  "rank": 4,
  "priceRankInCity": 12,
  "priceRankOfTotal": 200,
  "priceVsCityMedianPct": -18,
  "priceCategory": "below-average",
  "propertyUrl": "https://www.agoda.com/marina-bay-sands/hotel/singapore-sg.html"
}
```

***

### Output fields (monitor mode)

When you use `propertyUrls`, each row is a full quote with nested property intel. Fields are grouped by what they answer.

#### Identity and type

- `property.name`, `.defaultName`, `.localeName`
- `property.type` (Hotel, Homestay, Apartment, etc.)
- `property.starRating`, `.isLuxuryHotel`
- `property.propertyUrl`, `.matterportUrl` (3D walkthrough if the property has one)

#### Description and content

- `property.description` (full property description)
- `property.coverImage`, `.imageCount` (first photo plus total photo count)
- `property.remarks` (renovation notes, for example `"Renovated 2024"`)

#### Location

- `property.address`, `.neighborhood`, `.geo: {lat, lng}`
- `property.landmarks` (nearby places with distance in km)
- `property.walkablePlaces: {totalCount, topPlaces[]}` (categorized walking-distance attractions, e.g. "356 places in walking distance")
- `property.transit[]` (airports and stations with drive duration in minutes, distance, and category)
- `property.locationSubscore: {airportScore, poiScore, transportationScore}` (per-dimension 0 to 10 location score)

#### Reviews

- `property.rating`, `.reviewCount`
- `property.reviewBreakdown` (per-dimension score for cleanliness, staff, facilities, etc.)
- `property.categoryScores` (positive-mention sentiment 0 to 100, like `pool: 98`, `cleanliness: 96`, `breakfast: 94`)
- `property.reviews[]` (recent review texts with reviewer country and traveller type)
- `property.reviewThemes[]` (tags like "Great location", "Clean")
- `property.trendingScore: {past14DaysUplift, past30DaysUplift}` (is the review score rising or falling)
- `property.aiReviewSummary: {positive, negative}` (Agoda's AI-generated pros and cons paragraph)

#### Booking logistics

- `property.checkInTime`, `.checkOutTime` (for example `"02:00 PM"`, `"12:00 PM"`)
- `property.houseRules[]` (reception hours, photo ID requirements, etc.)
- `property.policies` (`{stayFreeAgeMin, stayFreeAgeMax, childPolicy, adult, extraBed, additional}`)
- `property.isInfantCotAvailable`, `.hasHostExperience`
- `property.awards: {goldCircleYear, advanceGuarantee}` (Agoda's internal loyalty recognition)

#### Homes-specific (null for hotels)

- `property.hostName`, `.hostSuperhost`, `.hostResponseTime`, `.hostResponseRate`

#### Amenities

- `property.amenities[]` (all available amenities, typically 100 to 270 per property)

#### Pricing (the reason you're scraping)

- `quote.cheapest` (cheapest offer with price, cancellation, payment, mealPlan)
- `quote.offers[]` (every room offer for the dates: room type, bedding, deals)
- `quote.urgency` (only-X-left, viewing-now, booked-recently signals)
- `quote.availability` (`available`, `sold-out`, `last-room`, or `unknown`)

#### Engagement signals

- `property.todayBookingCount` (parsed from Agoda's "Booked 23 times today" badge)

#### Price change (cross-run)

- `priceChangeAbs`, `priceChangePct`, `priceChangeVsDate` (compares against the last price scraped for the same `(property, check-in)` within 14 days. Good for price drop alerts.)

#### Per-run summary (one per property)

In addition to per-date quotes, each property gets a summary row with:

- `priceStats: {min, max, mean, median, stddev, count}` across all scanned dates
- `cheapestDate`, `mostExpensiveDate`
- `cheapestWindow: {checkIn, checkOut, priceTotal, savingsPctVsMedian}` (cheapest contiguous N-night window. Requires `flexDays > 0`.)
- `occupancyPct`, `availableDates[]`, `soldOutDates[]`

***

### Price drop webhook alerts

Set `alertBelowPrice: <number>`. Any available row whose nightly price falls below that threshold is written to two keys in the run's key-value store:

- `HAS_PRICE_ALERTS` (boolean). Single flag to gate a webhook on.
- `PRICE_ALERTS` (array). Full payload per alert (`propertySlug`, `checkIn`, `checkOut`, `priceNightly`, `threshold`, `savingsVsThresholdPct`, `priceChangePct`).

Wire an Apify "Actor run succeeded" webhook with a condition on the KV record. The webhook fires only when prices actually drop.

Typical recipe for an hourly price drop monitor:

```json
{
  "propertyUrls": ["https://www.agoda.com/marina-bay-sands/hotel/singapore-sg.html"],
  "dateMatrix": { "startFrom": "today", "scanDays": 14, "lengthOfStay": 1 },
  "currency": "SGD",
  "changedOnly": true,
  "alertBelowPrice": 400
}
```

Schedule this hourly. The count of alerts per run is in `run-report.totals.priceAlerts`.

***

### Pricing

Flat $2 per 1,000 rows. Pay per result. No subscription, no minimums.

Example costs:

- Search a city: 200 properties times 14 dates = 2,800 rows. Cost: $5.60.
- Daily price monitor: 10 hotels times 60 dates = 600 rows per day. Cost: $1.20 per day, or about $36 per month.

***

### Benefits

- **Works on Agoda Homes and regular hotels in one actor.** Same input, same output schema. You don't run two scrapers.
- **Cross-run price deltas built in.** `priceChangePct` and `priceChangeVsDate` come out of the box so you can wire price drop alerts in minutes.
- **Webhook-ready alerting.** `alertBelowPrice` plus an Apify webhook gives you a no-code price drop notifier.
- **Full property intel per row in monitor mode.** Amenities, reviews, location scores, AI review summary, host info, and policies, without extra calls.
- **Date matrix input.** Scan a week, a month, or a quarter in one run.
- **Flat pricing.** $2 per 1,000 rows, period. No tiers to pick between.

***

### Limitations

- **No full image gallery.** Each row carries `coverImage` (the first photo) and `imageCount`. The remaining gallery URLs are not emitted.
- **No city-name full-text search.** Agoda's `/search?query=X` is a search box, not a canonical city page. Use a city URL (`https://www.agoda.com/city/...`) or a numeric `cityId`.
- **Monitor-mode currency.** Quote/summary rows are reliably priced in USD, EUR, GBP, AUD, JPY, THB, MYR, PHP, IDR, VND, and HKD. Other currencies may fall back to the property's native currency — safest bet is to pick a major currency and let Agoda convert.
- **~650–1000 unique properties per sort, per city.** Agoda paginates any single sort to a ceiling. Use the `sortBy` fanout recipe above (run the same input with `bestMatch`, `reviewScore`, `lowestPriceFirst`, `highestPriceFirst`) combined with `skipSeen: true` to dedup across runs and cover 1.5–2× more of the city.

***

### FAQ

**Does it work for Agoda Homes (private-host listings)?**
Yes. Both modes handle Agoda Homes. The output schema is the same as for regular hotels.

**Can I search by city name like "Bangkok"?**
No. Agoda's `/search?query=X` is a full-text search, not a city page. Paste the city URL (`https://www.agoda.com/city/...`) or the numeric `cityId`.

**What's the difference between `searchDestinations` and `propertyUrls`?**
Use `searchDestinations` to discover properties in a city. Use `propertyUrls` to monitor specific properties you already know.

**What currencies are supported?**
Any ISO code Agoda displays. USD, EUR, SGD, GBP, THB, JPY, AUD, VND, HKD, and more. Agoda does the conversion.

**Which proxy does it use?**
Datacenter proxy by default for the price monitoring API calls (cheaper, and validated byte-identical to residential on the endpoints we use). The initial property discovery page load uses residential, because Agoda gates datacenter IPs on that page. The default config works out of the box. You don't need to change anything.

**Can I turn off the datacenter proxy path?**
Yes. Set `apiProxyDatacenter: false` to route everything through residential. This is slower and more expensive. Only useful if you need geo-accurate local pricing on every date cell, which is rare.

**Do I get the full image gallery?**
No. Every row includes `coverImage` (the first photo) and `imageCount`. The full gallery of photo URLs is not emitted.

***

### Need a custom scraper?

Reach out if you need a different site scraped, extra fields added, or a private actor for your team. Clean schemas, reliable proxies, straightforward pricing.

# Actor input Schema

## `searchDestinations` (type: `array`):

Paste an Agoda city URL (copy from the browser address bar after choosing a destination on agoda.com). The scraper finds every hotel + homestay in that city and returns one row per property × check-in date. Example: https://www.agoda.com/city/singapore-sg.html. You can also paste a numeric cityId as a string. Full-text queries like 'Bangkok' do NOT work. Use the city URL or cityId.

## `searchCities` (type: `array`):

Alternative to Search destinations for power users who already know Agoda's internal cityId (e.g. 4064 = Singapore, 5085 = Tokyo). Same output as destination search.

## `propertyUrls` (type: `array`):

List of specific Agoda property URLs to monitor. Use this when you want rich per-property detail (amenities, host info, review themes, images) across a date range. Accepts canonical /<slug>/hotel/<location>.html URLs. Leave empty if you're using Search destinations above.

## `dateMatrix` (type: `object`):

Which dates to scan. startFrom = 'today' or a YYYY-MM-DD date. scanDays = how many consecutive check-in dates to cover. lengthOfStay = nights per booking. stride = step between check-ins (1 = every day, 7 = weekly snapshots). Example: scanDays=14, stride=1 → 14 rows per property covering the next two weeks.

## `flexDays` (type: `integer`):

Flexible-traveler mode. Also scans N days before AND after each check-in in your dateMatrix, so you can see how price shifts if the trip moves by a day or two. With one target date, flexDays=2 produces 5 rows (day-2, day-1, day, day+1, day+2). The property summary tells you which window is cheapest. Cost scales with rows. Default 0 means off.

## `adults` (type: `integer`):

Number of adult guests. Affects pricing and availability (Agoda hides rooms that can't fit the party).

## `rooms` (type: `integer`):

Number of rooms to book. Most travel searches use 1.

## `children` (type: `integer`):

Number of children. Combined with adults to determine the occupancy filter Agoda applies.

## `currency` (type: `string`):

ISO currency code for prices in the output. Search mode supports any code Agoda displays. Monitor mode (propertyUrls) is reliable on USD, EUR, GBP, AUD, JPY, THB, MYR, PHP, IDR, VND, HKD; anything else falls back to USD with a warning.

## `locale` (type: `string`):

Controls the language of text fields in the output (property names, descriptions, review snippets). Common values: en-us, en-gb, de-de, fr-fr, ja-jp, zh-cn.

## `searchMaxResults` (type: `integer`):

Cap how many properties to return per (city × check-in date). 45 = one page (cheapest). 200+ triggers pagination and scales cost linearly. Only applies when using Search destinations / Search by cityId.

## `sortBy` (type: `string`):

How Agoda ranks results. Agoda caps unique properties per sort at roughly 650-1000 per city, so different sorts expose different inventory. Running all four sorts (Recommended, PriceAsc, PriceDesc, ReviewScore) back-to-back with skipSeen=true typically reveals 1.5 to 2 times more unique hotels than a single sort.

## `skipSeen` (type: `boolean`):

Dedup across runs without setting up your own storage. When true, the actor remembers every propertyId it already returned for each city (in a private per-account store) and suppresses duplicates before they hit the dataset — so you never pay twice for the same hotel. Pair with sortBy to fan out across all four sorts: run 1 grabs the bulk, runs 2-4 only pay for new inventory each sort surfaces.

## `skipPropertyIds` (type: `array`):

Suppress emission for any propertyId in this list. Works in addition to skipSeen; both inputs are merged. Use this when you have your own database of IDs (e.g. a UI that tracks scraped hotels). The actor still fetches the search pages (compute cost unchanged) but drops matching rows before pushData so you do not pay the per-row dataset charge.

## `mode` (type: `string`):

Kept for backwards compatibility. The actor always uses the same fast JSON API path today, so this setting has no effect. Older saved configs with `api` or `browser` are accepted and silently routed to the default. Leave as is.

## `concurrency` (type: `integer`):

How many properties to scrape in parallel. Higher is faster but raises the chance of rate limiting. 5 is a safe default.

## `searchPoolConcurrency` (type: `integer`):

How many (city, check-in date) search pools to scan in parallel. A pool is one city for one check-in; pages within a pool stay sequential because Agoda's pagination needs the previous page's token. Default 5 cuts wall-clock roughly linearly on multi-city or multi-date runs. Raise carefully: too high and Agoda may rate-limit.

## `propertyCacheTtlHours` (type: `integer`):

Caches property name, rating, amenities, and host info in a named key-value store so repeat runs skip the full property detail fetch. Great for hourly price monitoring schedules. 168 = one week. Set to 0 to disable.

## `proxyConfiguration` (type: `object`):

Which proxy to use. The default (Apify Proxy, residential) is a safety net for the initial page load where Agoda blocks datacenter IPs. The price-monitoring API calls underneath use cheaper datacenter proxy automatically. You don't need to change anything unless you bring your own proxy pool.

## `proxyCountry` (type: `string`):

Pins proxy traffic to one country so Agoda's response is reproducible. This matters. Agoda serves different base prices by country (geo pricing), and forces tax inclusive display in EU, UK, and AU (regulated) while showing pre tax prices to US and most APAC markets. Pick the market you want to monitor. Default "US" gives pre tax USD. Leave blank to let proxy rotate freely. Not recommended, because it breaks cross run price deltas.

## `findNextAvailable` (type: `boolean`):

Planning mode. When a scanned date is sold out, annotate the row with the nearest available check-in BEFORE (daysOffset -1, -2, -3) and AFTER (+1, +2, +3) drawn from the same scan. Zero extra API cost, because it uses only dates already scraped in this run. Your scanDays window must actually cover the neighbors for annotations to appear (for example, scanning a single sold-out date gives no neighbors). Perfect for 'is the trip shiftable?' answers: if Monday is sold out but Sunday or Tuesday is open, the row tells you. Populated fields: nextAvailableBefore and nextAvailableAfter, each with {checkIn, checkOut, nights, priceNightly, daysOffset}. Default off.

## `changedOnly` (type: `boolean`):

Price monitor mode. When enabled, a quote row is emitted to the dataset ONLY if (a) it's the first time we've seen this (property, check-in date, currency, occupancy, proxy country), meaning no prior run to compare against, or (b) the nightly price moved vs. the prior run, or (c) availability transitioned (for example, available to sold-out). Unchanged rows are silently skipped. The run still scrapes every date (cost is unchanged). It just suppresses dataset writes for no-op rows. Perfect for hourly schedules where you only want to process diffs. Summary and run-report rows always emit. The FIRST run with changedOnly=true emits everything (no prior state). Subsequent runs emit only changes. Count of skipped rows is in run-report.totals.rowsSkippedUnchanged.

## `alertBelowPrice` (type: `integer`):

Threshold in the run's currency (see `currency`). When any available quote row has nightly price strictly below this, the row is recorded in the run's key-value store under `PRICE_ALERTS` (array of {propertySlug, checkIn, priceNightly, threshold, savingsVsThresholdPct, priceChangePct}). A boolean flag `HAS_PRICE_ALERTS` is always set so run-complete webhooks can gate on one key. Leave blank or 0 to disable. Typical recipe: schedule the actor hourly with `changedOnly: true` + `alertBelowPrice: 120`, then wire an Apify webhook that fires only when `HAS_PRICE_ALERTS=true`.

## `apiProxyDatacenter` (type: `boolean`):

Routes the price monitoring API calls (date fan out and /graphql/property) through Apify's datacenter proxy instead of residential. About 8 times cheaper bytes, and validated as byte identical to residential on both endpoints as of 2026-04-16, so this is the default. Note: the initial property discovery page load still uses residential regardless of this flag, because Agoda gates datacenter IPs on that page. Set to false only if you need geo accurate local pricing on every date cell (rare, because Agoda's final booking flow price is authoritative either way).

## `canaryTtlMinutes` (type: `integer`):

Before each run, probe 3 known-stable properties and check that critical fields (name, price, rating, etc.) come back populated. If all 3 return null on the same field, Agoda likely changed their API and the run is aborted before emitting bad data. The result is cached in a named key-value store for this many minutes so frequent runs amortize the probe. 0 disables the canary entirely. Default 60 (one probe per hour across all runs). Adds about $0.001 per run the first time each hour.

## `includeRaw` (type: `boolean`):

Attach a truncated HTML snapshot to each row for debugging selectors. Increases dataset size significantly. Leave off in production.

## Actor input object example

```json
{
  "searchDestinations": [],
  "searchCities": [],
  "propertyUrls": [
    "https://www.agoda.com/marina-bay-sands/hotel/singapore-sg.html"
  ],
  "dateMatrix": {
    "startFrom": "today",
    "scanDays": 14,
    "lengthOfStay": 2,
    "stride": 1
  },
  "flexDays": 0,
  "adults": 2,
  "rooms": 1,
  "children": 0,
  "currency": "USD",
  "locale": "en-us",
  "searchMaxResults": 45,
  "sortBy": "Recommended",
  "skipSeen": false,
  "skipPropertyIds": [],
  "mode": "api-cold",
  "concurrency": 5,
  "searchPoolConcurrency": 5,
  "propertyCacheTtlHours": 168,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "proxyCountry": "US",
  "findNextAvailable": false,
  "changedOnly": false,
  "apiProxyDatacenter": true,
  "canaryTtlMinutes": 60,
  "includeRaw": false
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped rows (hotels + homestays with prices, availability, reviews, and cancellation flags).

## `resultsCsv` (type: `string`):

Same rows in CSV for spreadsheets.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchDestinations": [],
    "searchCities": [],
    "propertyUrls": [
        "https://www.agoda.com/marina-bay-sands/hotel/singapore-sg.html"
    ],
    "dateMatrix": {
        "startFrom": "today",
        "scanDays": 14,
        "lengthOfStay": 2,
        "stride": 1
    },
    "skipPropertyIds": [],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("bestscraper/agoda-property-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchDestinations": [],
    "searchCities": [],
    "propertyUrls": ["https://www.agoda.com/marina-bay-sands/hotel/singapore-sg.html"],
    "dateMatrix": {
        "startFrom": "today",
        "scanDays": 14,
        "lengthOfStay": 2,
        "stride": 1,
    },
    "skipPropertyIds": [],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("bestscraper/agoda-property-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchDestinations": [],
  "searchCities": [],
  "propertyUrls": [
    "https://www.agoda.com/marina-bay-sands/hotel/singapore-sg.html"
  ],
  "dateMatrix": {
    "startFrom": "today",
    "scanDays": 14,
    "lengthOfStay": 2,
    "stride": 1
  },
  "skipPropertyIds": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call bestscraper/agoda-property-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=bestscraper/agoda-property-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "BEST Agoda Scraper",
        "description": "Every row gives you: name, stars, review score, nightly price (tax-inclusive), availability, cover image, address, geo. Two modes. Search a city for every hotel + homestay, or monitor specific properties across dates with price-change deltas and next-available on sold-out. Covers Agoda Homes.",
        "version": "0.1",
        "x-build-id": "IhBATxV67yvfQvjv3"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/bestscraper~agoda-property-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-bestscraper-agoda-property-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/bestscraper~agoda-property-scraper/runs": {
            "post": {
                "operationId": "runs-sync-bestscraper-agoda-property-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/bestscraper~agoda-property-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-bestscraper-agoda-property-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "dateMatrix"
                ],
                "properties": {
                    "searchDestinations": {
                        "title": "Search a destination (recommended)",
                        "type": "array",
                        "description": "Paste an Agoda city URL (copy from the browser address bar after choosing a destination on agoda.com). The scraper finds every hotel + homestay in that city and returns one row per property × check-in date. Example: https://www.agoda.com/city/singapore-sg.html. You can also paste a numeric cityId as a string. Full-text queries like 'Bangkok' do NOT work. Use the city URL or cityId.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "searchCities": {
                        "title": "Search by numeric cityId",
                        "type": "array",
                        "description": "Alternative to Search destinations for power users who already know Agoda's internal cityId (e.g. 4064 = Singapore, 5085 = Tokyo). Same output as destination search."
                    },
                    "propertyUrls": {
                        "title": "Property URLs (single-property mode)",
                        "type": "array",
                        "description": "List of specific Agoda property URLs to monitor. Use this when you want rich per-property detail (amenities, host info, review themes, images) across a date range. Accepts canonical /<slug>/hotel/<location>.html URLs. Leave empty if you're using Search destinations above.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "dateMatrix": {
                        "title": "Check-in dates & stay length",
                        "type": "object",
                        "description": "Which dates to scan. startFrom = 'today' or a YYYY-MM-DD date. scanDays = how many consecutive check-in dates to cover. lengthOfStay = nights per booking. stride = step between check-ins (1 = every day, 7 = weekly snapshots). Example: scanDays=14, stride=1 → 14 rows per property covering the next two weeks."
                    },
                    "flexDays": {
                        "title": "Flex days (±N around your dates)",
                        "minimum": 0,
                        "maximum": 7,
                        "type": "integer",
                        "description": "Flexible-traveler mode. Also scans N days before AND after each check-in in your dateMatrix, so you can see how price shifts if the trip moves by a day or two. With one target date, flexDays=2 produces 5 rows (day-2, day-1, day, day+1, day+2). The property summary tells you which window is cheapest. Cost scales with rows. Default 0 means off.",
                        "default": 0
                    },
                    "adults": {
                        "title": "Adults per booking",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Number of adult guests. Affects pricing and availability (Agoda hides rooms that can't fit the party).",
                        "default": 2
                    },
                    "rooms": {
                        "title": "Rooms per stay",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Number of rooms to book. Most travel searches use 1.",
                        "default": 1
                    },
                    "children": {
                        "title": "Children per booking",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Number of children. Combined with adults to determine the occupancy filter Agoda applies.",
                        "default": 0
                    },
                    "currency": {
                        "title": "Currency",
                        "type": "string",
                        "description": "ISO currency code for prices in the output. Search mode supports any code Agoda displays. Monitor mode (propertyUrls) is reliable on USD, EUR, GBP, AUD, JPY, THB, MYR, PHP, IDR, VND, HKD; anything else falls back to USD with a warning.",
                        "default": "USD"
                    },
                    "locale": {
                        "title": "Locale",
                        "type": "string",
                        "description": "Controls the language of text fields in the output (property names, descriptions, review snippets). Common values: en-us, en-gb, de-de, fr-fr, ja-jp, zh-cn.",
                        "default": "en-us"
                    },
                    "searchMaxResults": {
                        "title": "Max results per destination (search mode only)",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Cap how many properties to return per (city × check-in date). 45 = one page (cheapest). 200+ triggers pagination and scales cost linearly. Only applies when using Search destinations / Search by cityId.",
                        "default": 45
                    },
                    "sortBy": {
                        "title": "Sort order (search mode)",
                        "enum": [
                            "Recommended",
                            "PriceAsc",
                            "PriceDesc",
                            "ReviewScore"
                        ],
                        "type": "string",
                        "description": "How Agoda ranks results. Agoda caps unique properties per sort at roughly 650-1000 per city, so different sorts expose different inventory. Running all four sorts (Recommended, PriceAsc, PriceDesc, ReviewScore) back-to-back with skipSeen=true typically reveals 1.5 to 2 times more unique hotels than a single sort.",
                        "default": "Recommended"
                    },
                    "skipSeen": {
                        "title": "Skip properties emitted on past runs (dedup via KV store)",
                        "type": "boolean",
                        "description": "Dedup across runs without setting up your own storage. When true, the actor remembers every propertyId it already returned for each city (in a private per-account store) and suppresses duplicates before they hit the dataset — so you never pay twice for the same hotel. Pair with sortBy to fan out across all four sorts: run 1 grabs the bulk, runs 2-4 only pay for new inventory each sort surfaces.",
                        "default": false
                    },
                    "skipPropertyIds": {
                        "title": "Skip these propertyIds (manual dedup list)",
                        "type": "array",
                        "description": "Suppress emission for any propertyId in this list. Works in addition to skipSeen; both inputs are merged. Use this when you have your own database of IDs (e.g. a UI that tracks scraped hotels). The actor still fetches the search pages (compute cost unchanged) but drops matching rows before pushData so you do not pay the per-row dataset charge."
                    },
                    "mode": {
                        "title": "Performance mode (legacy)",
                        "enum": [
                            "api-cold",
                            "api",
                            "browser"
                        ],
                        "type": "string",
                        "description": "Kept for backwards compatibility. The actor always uses the same fast JSON API path today, so this setting has no effect. Older saved configs with `api` or `browser` are accepted and silently routed to the default. Leave as is.",
                        "default": "api-cold"
                    },
                    "concurrency": {
                        "title": "Parallel property scrapers",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "How many properties to scrape in parallel. Higher is faster but raises the chance of rate limiting. 5 is a safe default.",
                        "default": 5
                    },
                    "searchPoolConcurrency": {
                        "title": "Parallel search pools (search mode only)",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "How many (city, check-in date) search pools to scan in parallel. A pool is one city for one check-in; pages within a pool stay sequential because Agoda's pagination needs the previous page's token. Default 5 cuts wall-clock roughly linearly on multi-city or multi-date runs. Raise carefully: too high and Agoda may rate-limit.",
                        "default": 5
                    },
                    "propertyCacheTtlHours": {
                        "title": "Property intel cache (hours)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Caches property name, rating, amenities, and host info in a named key-value store so repeat runs skip the full property detail fetch. Great for hourly price monitoring schedules. 168 = one week. Set to 0 to disable.",
                        "default": 168
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Which proxy to use. The default (Apify Proxy, residential) is a safety net for the initial page load where Agoda blocks datacenter IPs. The price-monitoring API calls underneath use cheaper datacenter proxy automatically. You don't need to change anything unless you bring your own proxy pool."
                    },
                    "proxyCountry": {
                        "title": "Proxy country (ISO code)",
                        "enum": [
                            "US",
                            "GB",
                            "DE",
                            "FR",
                            "IT",
                            "ES",
                            "AU",
                            "JP",
                            "KR",
                            "SG",
                            "TH",
                            "VN",
                            "ID",
                            "PH",
                            "MY",
                            "IN",
                            "HK",
                            "TW",
                            "AE",
                            ""
                        ],
                        "type": "string",
                        "description": "Pins proxy traffic to one country so Agoda's response is reproducible. This matters. Agoda serves different base prices by country (geo pricing), and forces tax inclusive display in EU, UK, and AU (regulated) while showing pre tax prices to US and most APAC markets. Pick the market you want to monitor. Default \"US\" gives pre tax USD. Leave blank to let proxy rotate freely. Not recommended, because it breaks cross run price deltas.",
                        "default": "US"
                    },
                    "findNextAvailable": {
                        "title": "Surface nearest-available dates on sold-out rows (±3 days)",
                        "type": "boolean",
                        "description": "Planning mode. When a scanned date is sold out, annotate the row with the nearest available check-in BEFORE (daysOffset -1, -2, -3) and AFTER (+1, +2, +3) drawn from the same scan. Zero extra API cost, because it uses only dates already scraped in this run. Your scanDays window must actually cover the neighbors for annotations to appear (for example, scanning a single sold-out date gives no neighbors). Perfect for 'is the trip shiftable?' answers: if Monday is sold out but Sunday or Tuesday is open, the row tells you. Populated fields: nextAvailableBefore and nextAvailableAfter, each with {checkIn, checkOut, nights, priceNightly, daysOffset}. Default off.",
                        "default": false
                    },
                    "changedOnly": {
                        "title": "Emit only rows whose price or availability changed vs. prior run",
                        "type": "boolean",
                        "description": "Price monitor mode. When enabled, a quote row is emitted to the dataset ONLY if (a) it's the first time we've seen this (property, check-in date, currency, occupancy, proxy country), meaning no prior run to compare against, or (b) the nightly price moved vs. the prior run, or (c) availability transitioned (for example, available to sold-out). Unchanged rows are silently skipped. The run still scrapes every date (cost is unchanged). It just suppresses dataset writes for no-op rows. Perfect for hourly schedules where you only want to process diffs. Summary and run-report rows always emit. The FIRST run with changedOnly=true emits everything (no prior state). Subsequent runs emit only changes. Count of skipped rows is in run-report.totals.rowsSkippedUnchanged.",
                        "default": false
                    },
                    "alertBelowPrice": {
                        "title": "Alert when nightly price drops below this threshold",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Threshold in the run's currency (see `currency`). When any available quote row has nightly price strictly below this, the row is recorded in the run's key-value store under `PRICE_ALERTS` (array of {propertySlug, checkIn, priceNightly, threshold, savingsVsThresholdPct, priceChangePct}). A boolean flag `HAS_PRICE_ALERTS` is always set so run-complete webhooks can gate on one key. Leave blank or 0 to disable. Typical recipe: schedule the actor hourly with `changedOnly: true` + `alertBelowPrice: 120`, then wire an Apify webhook that fires only when `HAS_PRICE_ALERTS=true`."
                    },
                    "apiProxyDatacenter": {
                        "title": "Route price-monitoring calls via datacenter proxy (cheaper, enabled by default)",
                        "type": "boolean",
                        "description": "Routes the price monitoring API calls (date fan out and /graphql/property) through Apify's datacenter proxy instead of residential. About 8 times cheaper bytes, and validated as byte identical to residential on both endpoints as of 2026-04-16, so this is the default. Note: the initial property discovery page load still uses residential regardless of this flag, because Agoda gates datacenter IPs on that page. Set to false only if you need geo accurate local pricing on every date cell (rare, because Agoda's final booking flow price is authoritative either way).",
                        "default": true
                    },
                    "canaryTtlMinutes": {
                        "title": "Schema drift canary TTL (minutes)",
                        "minimum": 0,
                        "maximum": 1440,
                        "type": "integer",
                        "description": "Before each run, probe 3 known-stable properties and check that critical fields (name, price, rating, etc.) come back populated. If all 3 return null on the same field, Agoda likely changed their API and the run is aborted before emitting bad data. The result is cached in a named key-value store for this many minutes so frequent runs amortize the probe. 0 disables the canary entirely. Default 60 (one probe per hour across all runs). Adds about $0.001 per run the first time each hour.",
                        "default": 60
                    },
                    "includeRaw": {
                        "title": "Include raw HTML snapshot (debug)",
                        "type": "boolean",
                        "description": "Attach a truncated HTML snapshot to each row for debugging selectors. Increases dataset size significantly. Leave off in production.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
