# VRBO Vacation Rentals \[$2.5💰] Scraper (`memo23/vrbo-scraper`) Actor

\[💰$2.5/1K] VRBO vacation-rentals scraper — paste any VRBO property URL and get one structured row: title, price/night, bedrooms, sleeps, lat/lng, photos, amenities, host. Pure HTTP via Apify Residential, no auth required. Same Expedia Group backend as memo23/expedia-scraper.

- **URL**: https://apify.com/memo23/vrbo-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Agents, Travel, Automation
- **Stats:** 21 total users, 20 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## VRBO Vacation Rentals Scraper — Property + Calendar + Reviews + Rates

**Scrape any VRBO property at $2.50 per 1,000 results.** One actor, six input forms (URL, bare ID, location text, region, coords, `/reviews` suffix). Returns full property data (title, sleeps, bedrooms, photos, amenities, host), the **date-by-date availability calendar with per-night pricing**, a **full rates breakdown** (service fee + cleaning fee + taxes + total), and the **complete paginated review history** (up to 1,250 reviews per property) — all from VRBO's own GraphQL backend. Pure HTTP via Apify Residential, **no auth required, no login needed**.

#### How it works

![How VRBO Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-vrbo.png)

#### ✨ Why use this scraper?

VRBO is the #2 vacation-rental marketplace globally (acquired by Expedia Group in 2015, 2M+ properties). Most VRBO scrapers on Apify either require account permissions (security risk), scrape thin DOM data (incomplete), or haven't shipped updates in months. **This one is different**:

- 🏖 **No auth required.** Public property pages + VRBO's public GraphQL endpoint only. No login, no captcha solving, no account permissions.
- 📋 **Apollo state extraction + paginated GraphQL.** For property data we extract VRBO's own `window.__APOLLO_STATE__` cache (canonical source). For reviews we additionally hit VRBO's `productReviewDetails` GraphQL endpoint to paginate **full history** — not just the 5–10 reviews the HTML page ships inline.
- 🌍 **Expedia Group backend.** VRBO uses `siteId 9001001 / tpid 9001 / eapid 1` on the shared Expedia GraphQL stack — same infrastructure proven at 5.0★ on [memo23/expedia-scraper](https://apify.com/memo23/expedia-scraper) (the #1 Expedia scraper on Apify).
- 🛡 **Akamai-cleared with per-session retry chain.** 3 rotated sessions on Apify Residential primary → optional Evomi fallback. Same proven stack as the Expedia actor.
- 📤 **Two row types from one actor.** `kind: "property"` and `kind: "review"`, both pushed to the same dataset for downstream filtering.
- 💰 **Predictable PPE pricing.** $2.50 per 1,000 result rows, no monthly flat fee.

#### 🎯 Use cases

| Team | What they build |
|---|---|
| **Vacation-rental data platforms** | Power your aggregator with structured VRBO inventory + calendars + reviews |
| **Property managers** | Track competitor pricing per-night by season; benchmark service fees, cleaning fees vs market |
| **Market analysts** | City-level ADR (Average Daily Rate) trends from the calendar; review sentiment over time |
| **Sentiment / reputation analysts** | Pull every review with manager responses → analyze recovery patterns |
| **Real-estate investors** | Score short-term-rental potential by scraping comparable properties + their occupancy proxies |
| **Travel-tech integrators** | Build VRBO support into a multi-platform travel app without Expedia Partner Solutions |

#### 📥 Supported inputs (6 forms)

Paste any of these — the actor classifies each input automatically.

| Input form | Example | Behaviour |
|---|---|---|
| **Full URL** | `https://www.vrbo.com/2165911` | One property row |
| **Bare ID** | `2165911` or `4193579ha` | Auto-prepends `https://www.vrbo.com/` — one property row |
| **Dotted-ID** | `321.2880733.3452770` | Legacy VRBO/Expedia format — one property row |
| **Reviews suffix** | `2165911/reviews` or `4193579ha/reviews` | **All paginated reviews** for that property (up to 1,250) |
| **Location text** | `Bali`, `New York`, `Tulum Mexico` | Builds search URL → walks pagination → fans out to detail (up to 500 properties) |
| **Region ID** | `region:2554` | Builds region-search URL |
| **Coordinates** | `-7.7956, 110.3695` | Builds coords-search URL |
| **Full search URL** | `https://www.vrbo.com/search/keywords:bali/` | Walks pagination + fan-out |

#### 🔄 How it works (step by step)

**For detail/property inputs**:
1. **Classify** the input form → resolve to canonical URL
2. **Inject date range** (default: tomorrow + 30 days) so the Apollo cache loads with calendar + per-night pricing
3. **Fetch** via impit + Apify Residential (Chrome 144 TLS fingerprint, **per-session rotation × 3 attempts** before fallback)
4. **Extract `window.__APOLLO_STATE__`** via the `JSON.parse("...")` escape pattern
5. **Map `PropertyInfo:{id}`** to a flat row + extract calendar, rates, policies

**For reviews inputs** (extra steps):
6. **Resolve `vrboId → expediaPropertyId`** from the Apollo cache
7. **POST to `https://www.vrbo.com/graphql`** with the `productReviewDetails` operation, iOS-app-style headers
8. **Paginate `pageIndex: 0 → 1 → 2 → …`** at 25 reviews/page until empty or maxItems hit
9. **Emit one row per review** with the full schema (rating, text, "liked" tags, travel type, verified flag, manager response)

**For search inputs**:
10. **Walk search-result pages** (up to 50 pages), extract property IDs (up to 500)
11. **Fan out as detail requests** — each emits its own row

#### ⚙️ Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array of strings | — *(required)* | Any of the 6 input forms above. Mix freely. |
| `checkIn` | string (YYYY-MM-DD) | tomorrow | Loads calendar + rates into Apollo cache. Required for full data. |
| `checkOut` | string (YYYY-MM-DD) | check-in + 30 days | Together with check-in defines the calendar window. |
| `adultsCount` | integer | `2` | Used by VRBO to compute the rates breakdown. |
| `includeReviews` | boolean | `false` | **v1.7** — when on, every property visited (direct URL **and** search fan-out) also emits paginated review rows. Pair with `maxReviewsPerProperty` to cap blowout. |
| `maxReviewsPerProperty` | integer | `50` | Per-property hard cap on review rows when `includeReviews=true` or input is in `{id}/reviews` form. Max 1,000. |
| `maxItems` | integer | `100` | Soft cap per source. Detail = 1 row; reviews = up to N rows; search = up to N fan-out. |
| `maxConcurrency` | integer | `4` | Parallel HTTP requests. Sweet spot 3–6 via Apify Residential. |
| `maxRequestRetries` | integer | `6` | Per-URL retry budget on Akamai 429/403. |
| `proxy` | object | Apify Residential | Required — direct connections are blocked. |
| `fallbackProxyUrl` | string | (none) | Optional Evomi URL when Apify Residential exhausts retries. |

##### How to get reviews — two ways

| You want | How |
|---|---|
| Reviews for **one specific property**, no detail row | Paste `2165911/reviews` (or full URL `vrbo.com/2165911/reviews`) in `startUrls` |
| **Property + its reviews** | Paste the property URL **and** check `includeReviews` |
| **Every property in a city + their reviews** | Paste `Bali` (or any search URL) **and** check `includeReviews` |

**Cost math** (at $2.50/1k rows): a `Bali` search hitting 50 properties × `maxReviewsPerProperty=50` = 50 property rows + 2,500 review rows = **~$6.38**. Lower the per-property cap or `maxItems` if you want to dial that back.

#### Example input — mixed forms in one run

```json
{
  "startUrls": [
    "https://www.vrbo.com/2165911",
    "4193579ha",
    "Bali",
    "2165911/reviews",
    "region:2554",
    "-7.7956, 110.3695"
  ],
  "checkIn": "2026-07-01",
  "checkOut": "2026-07-31",
  "adultsCount": 4,
  "includeReviews": false,
  "maxReviewsPerProperty": 50,
  "maxItems": 100,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
````

#### Example input — property + full review history in one run (v1.7)

```json
{
  "startUrls": ["https://www.vrbo.com/3444303"],
  "includeReviews": true,
  "maxReviewsPerProperty": 200,
  "maxItems": 250
}
```

Emits **1 property row + up to 200 review rows** for the listing.

#### 📊 Output overview

Two row shapes — both pushed to the same dataset. Filter by `kind` field to split downstream.

- **`kind: "property"`** — one row per property (from detail / search-fanout / bare-ID / location inputs)
- **`kind: "review"`** — one row per review (from `/reviews` inputs, paginated)

#### 📦 Output sample — property row

```jsonc
{
  "kind": "property",
  "vrboId": "2165911",                          // VRBO URL ID
  "expediaPropertyId": "63066336",              // Expedia Group internal ID
  "listingUrl": "https://www.vrbo.com/2165911?startDate=2026-07-01&endDate=2026-07-31&adultsCount=4",
  "title": "Piccolo Ponte 3 BR Caribbean Villa. Pool & Hot Tub. Canal front, SUP, Bikes.",
  "headline": "Caribbean Villa with private pool and canal access",

  // Pricing summary (single nightly rate)
  "pricePerNight": 1094,
  "priceCurrency": "USD",
  "priceFormatted": "$1,094 per night",

  // Capacity
  "sleeps": 6,
  "bedrooms": 3,
  "bathrooms": 3,
  "propertyType": "villa",

  // Location
  "address": {
    "full": "Providenciales, Turks and Caicos Islands",
    "city": "Providenciales",
    "country": "Turks and Caicos Islands",
    "countryCode": "TC"
  },
  "coordinates": { "lat": 21.81669, "lng": -72.15198 },

  // Content + media
  "description": "Charming canal-front villa in the heart of Providenciales...",
  "photos": [ { "url": "https://images.trvl-media.com/.../1.jpg", "caption": "Pool view" } ],
  "photosCount": 28,

  // Amenities (categorised)
  "amenities": [
    { "id": "pool", "label": "Private pool", "group": "Pool & Spa" },
    { "id": "hot_tub", "label": "Hot tub", "group": "Pool & Spa" }
  ],
  "amenitiesCount": 42,

  // Review summary (full reviews via separate /reviews input)
  "rating": 9.4,
  "ratingScale": 10,
  "reviewCount": 87,

  // Host
  "host": {
    "name": "Piccolo Ponte Rentals",
    "type": "property-manager",
    "superhost": true,
    "responseRate": "100%"
  },

  // ── v1.2: Detailed rates breakdown ──
  "rates": {
    "nightlyRate": 1094,
    "serviceFee": 287,
    "cleaningFee": 250,
    "taxes": 178,
    "totalPrice": 33914,
    "currency": "USD",
    "nights": 30,
    "checkIn": "2026-07-01",
    "checkOut": "2026-07-31"
  },

  // ── v1.2: Date-by-date availability calendar ──
  "calendar": [
    { "date": "2026-07-01", "available": true,  "pricePerNight": 1094, "currency": "USD", "minStay": 3 },
    { "date": "2026-07-02", "available": true,  "pricePerNight": 1094, "currency": "USD", "minStay": 3 },
    { "date": "2026-07-03", "available": false, "pricePerNight": null, "currency": null, "minStay": null }
    // ... up to 730 days ...
  ],
  "calendarLength": 30,

  // ── v1.2: Policy flags ──
  "policies": {
    "pets": true,
    "smoking": false,
    "events": false,
    "children": true,
    "rawText": "House rules: No smoking. Pets welcome. Quiet hours 10pm-7am."
  },

  "cancellationPolicy": "Strict — 50% refund if cancelled 60 days before check-in",
  "minimumNights": 4,
  "scrapedAt": "2026-05-25T15:00:00.000Z"
}
```

#### 📦 Output sample — review row

```jsonc
{
  "kind": "review",
  "vrboId": "3444303",
  "expediaPropertyId": "...",
  "listingUrl": "https://www.vrbo.com/3444303/reviews",
  "reviewId": "6a00d16b320ff804c7f9e55a",

  // Rating
  "rating": 10,
  "ratingMax": 10,
  "ratingLabel": "Excellent",

  // Content
  "title": null,
  "text": "Comfy, cozy, and whimsical. We will return when we can spend more time.",
  "liked": "cleanliness, check-in, communication, location, listing accuracy, value for money",

  // Author + verification
  "authorName": "Heidi S.",
  "authorLocation": null,
  "isVerified": true,

  // Stay metadata
  "submittedAt": "2026-05-12T00:00:00.000Z",
  "stayPeriod": "Stayed 2 nights in May 2026",
  "travelType": "Traveled with family",

  // Engagement
  "helpfulVotes": 3,
  "managerResponse": {
    "text": "Thank you Heidi! We loved hosting you and look forward to your return!",
    "respondedAt": "2026-05-15T14:22:00.000Z"
  },

  "scrapedAt": "2026-05-25T15:00:00.000Z"
}
```

#### 🗂 Key output fields

| Group | Fields |
|---|---|
| **Identity** | `kind`, `vrboId`, `expediaPropertyId`, `listingUrl`, `rawInput`, `title`, `headline` |
| **Pricing summary** | `pricePerNight`, `priceCurrency`, `priceFormatted` |
| **Rates breakdown** *(v1.2)* | `rates.{nightlyRate, serviceFee, cleaningFee, taxes, totalPrice, currency, nights, checkIn, checkOut}` |
| **Calendar** *(v1.2)* | `calendar[].{date, available, pricePerNight, currency, minStay}`, `calendarLength` |
| **Capacity** | `sleeps`, `bedrooms`, `bathrooms`, `propertyType` |
| **Location** | `address.{full,city,region,country,countryCode,postalCode}`, `coordinates.{lat,lng}` |
| **Content + media** | `description`, `photos[]`, `photosCount` |
| **Amenities** | `amenities[].{id,label,group}`, `amenitiesCount` |
| **Review summary** | `rating`, `ratingScale`, `reviewCount` |
| **Host** | `host.{name,type,superhost,responseRate}` |
| **Policies** *(v1.2)* | `policies.{pets,smoking,events,children,rawText}` |
| **Per-review row** *(v1.4)* | `reviewId`, `rating`, `ratingMax`, `ratingLabel`, `text`, `liked`, `authorName`, `isVerified`, `stayPeriod`, `travelType`, `helpfulVotes`, `managerResponse.{text,respondedAt}` |

#### 💰 Pricing

**$2.50 per 1,000 result rows.** Pay only for rows in the dataset.

| Event | Charged when | Rate |
|---|---|---|
| `apify-actor-start` | Each run starts (Akamai-clearance overhead, proxy warm-up) | $0.008 / run |
| `apify-default-dataset-item` | Each row written (property OR review) | **$2.50 per 1,000** |

Cost examples:

- 100 property rows in Bali → ~$0.26
- 1 property + its 250 reviews → ~$0.64
- 50 properties × 30 reviews each = 1,500 review rows + 50 property rows → ~$3.88
- Daily monitoring of 500 properties → ~$1.26/day = ~$38/month

#### What makes this richer than the competition

| Capability | Other VRBO scrapers | This actor |
|---|---|---|
| **Auth requirement** | Some require login | **No auth — pure HTTP** |
| **Data source** | DOM scraping (fragile) | **Apollo state + GraphQL endpoint** (canonical) |
| **Calendar / availability** | Often missing | ✅ Date-by-date with per-night pricing |
| **Rates breakdown** | Just nightly rate | ✅ Service fee + cleaning fee + taxes + total |
| **Reviews depth** | First 5–10 only (inline only) | ✅ **Full paginated history** up to 1,250 per property via direct GraphQL |
| **Review fields** | Basic rating + text | ✅ Rating + label + "liked" categories + travel type + verified flag + **manager responses** |
| **Policies (pets/smoking/events)** | Buried in amenities array | ✅ Top-level booleans for filterability |
| **Input flexibility** | URL only | ✅ 6 forms: URL, bare ID, dotted-ID, location, region, coords, /reviews |
| **Akamai handling** | Brittle (issues queue full of 429s) | ✅ Per-session retry chain × 3 + optional Evomi fallback |
| **Pricing** | $1–$30/month flat or $0.90–$3/1k | **$2.50/1k PPE** — predictable |

#### Notes & limitations

- **Calendar requires a date range on the URL.** v1.2 auto-injects tomorrow + 30 days by default. Pass `checkIn` + `checkOut` for a different window. Without a date range, VRBO ships only a sparse Apollo cache (no calendar, no rates).
- **Review pagination caps at 1,250 per property.** That's 50 GraphQL pages × 25 reviews/page. Most properties have far fewer reviews; the cap exists to prevent runaway billing on the rare 5,000-review listing.
- **`/reviews` mode emits review rows only** (no property summary). To get both, paste the property URL AND its `/reviews` variant.
- **Search-URL fan-out caps at 500 properties per source.** Use multiple search URLs (different cities / property types) to scale beyond that.
- **Apify Residential US/default pool works.** Apify Residential **GB** is on Akamai's blocklist for VRBO — use US or default.

#### ❓ FAQ

**Q. Does this require a VRBO account or any auth?**
No. The actor only accesses public property pages + VRBO's public GraphQL endpoint. No login, no account-permission grant, no captcha solving.

**Q. How is this different from existing VRBO scrapers on Apify?**
Existing scrapers ship only the first 5–10 reviews (inline Apollo cache only). This actor uses VRBO's own paginated GraphQL endpoint to return the **full review history**. Plus we ship calendar + rates breakdown + policies — features competitors haven't shipped despite buyers asking since 2023.

**Q. Why same pricing as `memo23/expedia-scraper`?**
VRBO is an Expedia Group property, uses the same backend, and the same Akamai protection. Same per-row cost makes bundling clean for buyers managing both vacation-rentals (VRBO) and hotels (Expedia / Hotels.com) data.

**Q. Can I get the calendar for the next 12 months?**
Yes — pass `checkIn: "2026-06-01"` and `checkOut: "2027-06-01"`. The Apollo cache will load a year-wide availability matrix when the date range is that long.

**Q. What's the realistic throughput?**
Default `maxConcurrency: 4`. ~2–4 properties/sec for detail rows; ~5–10 review pages/sec for reviews mode. So 1,000 properties in ~5 min; 250 reviews per property in ~50s.

**Q. Reviews pagination — do I pay per page or per review?**
Per review. Each review row → one `$0.0025` event. Pages are free roundtrips; you only pay for what lands in the dataset.

**Q. Can I get historical bookings, occupancy, or transaction data?**
No. VRBO doesn't expose closed-booking data publicly. The actor sees what's currently listed (price, description, calendar, public reviews).

**Q. What if Apify Residential gets rate-limited mid-run?**
The actor retries the primary proxy up to 3 times with rotated sessions automatically. Beyond that, set `fallbackProxyUrl` to an Evomi residential URL.

#### 💬 Support

- For issues or feature requests, please use the **Issues** tab on the actor's Apify Console page.
- Author's website: <https://muhamed-didovic.github.io/>
- Email: <muhamed.didovic@gmail.com>

#### 🛠 Additional services

- **Need Expedia / Hotels.com / Things-To-Do reviews?** Use the sibling [memo23/expedia-scraper](https://apify.com/memo23/expedia-scraper) — same architecture, $2.50/1k, covers all 3 Expedia Group brands.
- Custom output shape, additional fields, or a private build: <muhamed.didovic@gmail.com>
- Need other vacation-rental platforms (Airbnb, Booking.com, Vacasa, FlipKey, HomeToGo)? Drop an email.
- For API access (no Apify fee, just a usage fee for the API): <muhamed.didovic@gmail.com>

#### 🔎 Explore more scrapers

See other scrapers at [memo23's Apify profile](https://apify.com/memo23) — Expedia/Hotels.com reviews, ImmoScout24 DACH residential, LoopNet US+UK commercial, Stepstone DE jobs, Naukri India jobs, and more.

***

### 🧪 Smoke-test matrix (for developers / contributors)

Before every `apify push`, run the full input-form matrix AND the field-level assertion suite to catch regressions across input kinds AND data quality:

```bash
apify push --force
apify call --build <new-version> -f scripts/smoke-matrix.json
## Copy the "Export results: https://console.apify.com/storage/datasets/<id>" line
node scripts/smoke-assertions.mjs <datasetId>
```

The matrix (`scripts/smoke-matrix.json`) exercises all 10 input forms in a single run (~25 s, ~23 rows). The assertion suite (`scripts/smoke-assertions.mjs`) runs ~350 field-level checks on the resulting dataset and exits non-zero on any failure.

**Why two layers?** The matrix proves rows LAND; the assertions prove rows aren't STUBS. A real-world bug (May 2026) shipped rows with `title: null` + `rating: 0` because VRBO's API briefly returned a thin shell that the matrix counted as "success." The assertion suite catches that class of regression at ship time.

Sample assertions:

- `property.title` non-empty (catches stub responses)
- `property.rating` in `[0, 10]` (catches encoding bugs)
- `property.photosCount > 0` AND `property.amenitiesCount > 0` (every real VRBO property has both)
- `property.rating > 0` when `reviewCount > 0` (catches the specific stub pattern where both go to 0)
- Property-specific: `2165911` must contain "Piccolo Ponte" in title, rating ≥ 9.5, reviewCount ≥ 30, countryCode `TCA`
- Coverage: every search input (`Bali`, `New York`, `region:6054439`, coords, `keywords:Tulum/`) must produce at least one fan-out property row

The matrix:

| ## | Input | Resolved kind | Expected output |
|---|---|---|---|
| 1 | `https://www.vrbo.com/2165911` | detail | 1 property row |
| 2 | `2165911` | detail | deduped with #1 (same kind+canonical) |
| 3 | `4193579ha` | detail | 1 property row (`.ha`-suffix support) |
| 4 | `2165911/reviews` | reviews | up to `maxItems` review rows |
| 5 | `4193579ha/reviews` | reviews | up to `maxItems` review rows |
| 6 | `Bali` | search → fan-out | `maxItems` property rows |
| 7 | `New York` | search → fan-out | `maxItems` property rows |
| 8 | `region:6054439` | search → fan-out | `maxItems` property rows |
| 9 | `-7.7956, 110.3695` | search (coords) → fan-out | `maxItems` property rows |
| 10 | `https://www.vrbo.com/search/keywords:Tulum/` | search → fan-out | `maxItems` property rows |

**Pass criteria** (two layers — both must be green):

1. **Matrix layer**: `details: 17 scraped, 0 failed` AND `reviews: ≥6 row(s)` AND `searches: 5 processed, 0 failed` (at `maxItems: 3`).
2. **Assertion layer**: `node scripts/smoke-assertions.mjs <datasetId>` exits 0. ~350 assertions; any failure prints `✗ [row] reason` and blocks the push.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by VRBO, Expedia Group Inc., HomeAway, or any of their subsidiaries or related entities**. All VRBO-, Expedia-, HomeAway-, and Expedia Group-related trademarks, service marks, trade names, and logos are the property of their respective owners and are used here solely for descriptive purposes — to identify the public vacation-rental listings from which this scraper extracts data.

This Actor accesses only publicly available property-listing pages and the public GraphQL endpoint on `www.vrbo.com`. **You are solely responsible** for ensuring that your use of the data complies with VRBO's Terms of Service, applicable copyright law, the EU Database Directive, GDPR (where applicable), the US Computer Fraud and Abuse Act, and any other regulations that may apply to your jurisdiction or use case. Do not use this Actor to bulk-republish copyrighted content, to harass hosts or guests, to circumvent any rate-limiting or technical protection measure intentionally imposed by VRBO, or to enable unsolicited outreach in violation of CAN-SPAM / e-Privacy consent rules.

The author provides this Actor "as is", without warranty of any kind, and disclaims any liability for damages arising from its use.

***

### SEO Keywords

vrbo scraper, vrbo api, vrbo.com scraper, vacation rentals scraper, vrbo vacation rental api, vrbo property scraper, vrbo calendar scraper, vrbo availability api, vrbo reviews scraper, vrbo all reviews api, vacation rental reviews api, vrbo pricing data, vrbo service fee api, vrbo cleaning fee data, vacation rental analytics, vrbo amenities scraper, vrbo photos api, vrbo host scraper, manager response scraper, vacation rental aggregator data, short term rental data, vrbo competitor of airbnb, vacation rental market intelligence, vrbo no auth scraper, apify vrbo actor, vrbo without login scraping, vacation rental adr benchmark, expedia partner solutions alternative, str data api, vacation rental scraper apify, vrbo paginated reviews

# Actor input Schema

## `startUrls` (type: `array`):

Paste any combination of the six input forms below. The actor classifies each input automatically and dispatches to the right handler.

**Examples:**

- `https://www.vrbo.com/2165911` — full property URL → one property row
- `2165911` — bare numeric ID → one property row (auto-prepends `https://www.vrbo.com/`)
- `4193579ha` — VRBO-style ID with `ha` suffix → one property row
- `2165911/reviews` or `4193579ha/reviews` — scrape **review rows** for the property
- `Bali`, `New York`, `Tulum Mexico` — free-text **location search** → walks pagination + fans out to each property
- `region:2554` — search by region ID
- `-7.7956, 110.3695` — search by coordinates (lat, lon)
- `https://www.vrbo.com/search/keywords:bali/` — full search URL → walks pagination

Non-paying users are capped at 25 total rows.

## `checkIn` (type: `string`):

ISO date (YYYY-MM-DD). Reserved for the upcoming per-night rates + availability calendar feature. Leave empty to use a sensible default (tomorrow). Does not affect property or review scraping.

## `checkOut` (type: `string`):

ISO date (YYYY-MM-DD). Default: check-in + 30 days. Reserved for the upcoming per-night rates + availability calendar feature.

## `adultsCount` (type: `integer`):

Adult guest count used for rate calculation when the calendar feature is enabled. Default 2.

## `includeReviews` (type: `boolean`):

When checked, **every property the actor visits** (direct URLs, bare IDs, AND properties discovered from a location/region/coordinates search) also produces paginated review rows — not just the property row.

Leave **unchecked** to get only the property row. Use the `{id}/reviews` URL form (e.g. `2165911/reviews`) to grab reviews for a specific property without touching this flag.

**Cost example:** a `Bali` search hitting 50 properties × `maxReviewsPerProperty=50` = **2,500 review rows ≈ $7.50** at $3.00/1k, plus 50 property rows ≈ $0.15. Always paired with `maxReviewsPerProperty` to cap the blowout.

Default: off.

## `maxReviewsPerProperty` (type: `integer`):

Hard cap on review rows emitted **per property** when `includeReviews` is on (or when an input is in `{id}/reviews` form). VRBO publishes some properties with 500+ reviews; without a cap, one property can dominate your dataset and budget. The actor walks GraphQL pagination (25 reviews per request) until this cap is hit or VRBO returns an empty page. Default 50, max 1,000.

## `maxItems` (type: `integer`):

Soft cap on rows emitted per source input. Detail inputs always emit 1 row each. Search inputs walk pagination until this cap. Reviews inputs emit N review rows (one per review). Default 100. Non-paying users are capped at 25 total.

## `maxConcurrency` (type: `integer`):

Parallel HTTP requests. VRBO is Akamai-protected — sweet spot 3–6 via residential proxies to avoid rate-limit retries.

## `maxRequestRetries` (type: `integer`):

Per-URL retry budget on 429/403 responses and proxy connection failures. The actor also rotates proxy sessions internally before falling back to a secondary pool, so this controls the higher-level retry budget on top of that.

## `proxy` (type: `object`):

Residential proxies are required — VRBO blocks datacenter IPs. Leave the default unless you have a specific reason to change.

## `fallbackProxyUrl` (type: `string`):

Optional secondary residential proxy URL (Evomi / Decodo / BrightData / your own) to try when the primary pool returns 429 after 3 retry attempts with rotated sessions. Format: `http://user:pass@host:port`. Leave empty unless you've burned through the default pool.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.vrbo.com/2165911",
    "4193579ha",
    "Bali",
    "2165911/reviews"
  ],
  "adultsCount": 2,
  "includeReviews": false,
  "maxReviewsPerProperty": 50,
  "maxItems": 100,
  "maxConcurrency": 4,
  "maxRequestRetries": 6,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "startUrls": [
        "https://www.vrbo.com/2165911",
        "4193579ha",
        "Bali",
        "2165911/reviews"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/vrbo-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 = {
    "startUrls": [
        "https://www.vrbo.com/2165911",
        "4193579ha",
        "Bali",
        "2165911/reviews",
    ],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/vrbo-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 '{
  "startUrls": [
    "https://www.vrbo.com/2165911",
    "4193579ha",
    "Bali",
    "2165911/reviews"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call memo23/vrbo-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "VRBO Vacation Rentals [$2.5💰] Scraper",
        "description": "[💰$2.5/1K] VRBO vacation-rentals scraper — paste any VRBO property URL and get one structured row: title, price/night, bedrooms, sleeps, lat/lng, photos, amenities, host. Pure HTTP via Apify Residential, no auth required. Same Expedia Group backend as memo23/expedia-scraper.",
        "version": "1.0",
        "x-build-id": "EUKyCa46pvcpfunWK"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/memo23~vrbo-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-memo23-vrbo-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/memo23~vrbo-scraper/runs": {
            "post": {
                "operationId": "runs-sync-memo23-vrbo-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/memo23~vrbo-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-memo23-vrbo-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": [
                    "startUrls"
                ],
                "properties": {
                    "startUrls": {
                        "title": "VRBO inputs — URL, ID, location, region, coordinates, or reviews",
                        "type": "array",
                        "description": "Paste any combination of the six input forms below. The actor classifies each input automatically and dispatches to the right handler.\n\n**Examples:**\n- `https://www.vrbo.com/2165911` — full property URL → one property row\n- `2165911` — bare numeric ID → one property row (auto-prepends `https://www.vrbo.com/`)\n- `4193579ha` — VRBO-style ID with `ha` suffix → one property row\n- `2165911/reviews` or `4193579ha/reviews` — scrape **review rows** for the property\n- `Bali`, `New York`, `Tulum Mexico` — free-text **location search** → walks pagination + fans out to each property\n- `region:2554` — search by region ID\n- `-7.7956, 110.3695` — search by coordinates (lat, lon)\n- `https://www.vrbo.com/search/keywords:bali/` — full search URL → walks pagination\n\nNon-paying users are capped at 25 total rows.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "checkIn": {
                        "title": "Check-in date",
                        "type": "string",
                        "description": "ISO date (YYYY-MM-DD). Reserved for the upcoming per-night rates + availability calendar feature. Leave empty to use a sensible default (tomorrow). Does not affect property or review scraping."
                    },
                    "checkOut": {
                        "title": "Check-out date",
                        "type": "string",
                        "description": "ISO date (YYYY-MM-DD). Default: check-in + 30 days. Reserved for the upcoming per-night rates + availability calendar feature."
                    },
                    "adultsCount": {
                        "title": "Adults (for rate calculation)",
                        "minimum": 1,
                        "maximum": 16,
                        "type": "integer",
                        "description": "Adult guest count used for rate calculation when the calendar feature is enabled. Default 2.",
                        "default": 2
                    },
                    "includeReviews": {
                        "title": "Also scrape full review history for every property",
                        "type": "boolean",
                        "description": "When checked, **every property the actor visits** (direct URLs, bare IDs, AND properties discovered from a location/region/coordinates search) also produces paginated review rows — not just the property row.\n\nLeave **unchecked** to get only the property row. Use the `{id}/reviews` URL form (e.g. `2165911/reviews`) to grab reviews for a specific property without touching this flag.\n\n**Cost example:** a `Bali` search hitting 50 properties × `maxReviewsPerProperty=50` = **2,500 review rows ≈ $7.50** at $3.00/1k, plus 50 property rows ≈ $0.15. Always paired with `maxReviewsPerProperty` to cap the blowout.\n\nDefault: off.",
                        "default": false
                    },
                    "maxReviewsPerProperty": {
                        "title": "Max reviews per property",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Hard cap on review rows emitted **per property** when `includeReviews` is on (or when an input is in `{id}/reviews` form). VRBO publishes some properties with 500+ reviews; without a cap, one property can dominate your dataset and budget. The actor walks GraphQL pagination (25 reviews per request) until this cap is hit or VRBO returns an empty page. Default 50, max 1,000.",
                        "default": 50
                    },
                    "maxItems": {
                        "title": "Max items per source",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Soft cap on rows emitted per source input. Detail inputs always emit 1 row each. Search inputs walk pagination until this cap. Reviews inputs emit N review rows (one per review). Default 100. Non-paying users are capped at 25 total.",
                        "default": 100
                    },
                    "maxConcurrency": {
                        "title": "Max parallel requests",
                        "minimum": 1,
                        "maximum": 12,
                        "type": "integer",
                        "description": "Parallel HTTP requests. VRBO is Akamai-protected — sweet spot 3–6 via residential proxies to avoid rate-limit retries.",
                        "default": 4
                    },
                    "maxRequestRetries": {
                        "title": "Max request retries",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Per-URL retry budget on 429/403 responses and proxy connection failures. The actor also rotates proxy sessions internally before falling back to a secondary pool, so this controls the higher-level retry budget on top of that.",
                        "default": 6
                    },
                    "proxy": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Residential proxies are required — VRBO blocks datacenter IPs. Leave the default unless you have a specific reason to change.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    },
                    "fallbackProxyUrl": {
                        "title": "Fallback proxy URL (optional)",
                        "type": "string",
                        "description": "Optional secondary residential proxy URL (Evomi / Decodo / BrightData / your own) to try when the primary pool returns 429 after 3 retry attempts with rotated sessions. Format: `http://user:pass@host:port`. Leave empty unless you've burned through the default pool."
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
