# Hostelworld Scraper – Hostels, Dorm Prices & Reviews (`rl1987/hostelworld-api-scraper`) Actor

Search Hostelworld by city or look up hostels by ID or URL, and choose what a row is: a hostel, a dorm or private room with its own price, or a guest review. Flat columns only - ratings, dorm and private prices, pre-discount figures, facilities, policies, geo. Billed per hostel.

- **URL**: https://apify.com/rl1987/hostelworld-api-scraper.md
- **Developed by:** [R.L.](https://apify.com/rl1987) (community)
- **Categories:** Travel
- **Features:** Limited permissions
- **Stats:** 24 total users, 14 monthly users, 99.6% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.27 / 1,000 hostel (listing)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Hostelworld Scraper – Hostels, Dorm Prices & Reviews

**Hostelworld Scraper** searches Hostelworld by city, or works on hostels you name, and extracts images, facilities, dorm and private room prices, the full ratings breakdown, geo, address, policies and guest reviews — from the app's own mobile API. It reads Hostelworld's API directly, so it's **fast, reliable and complete** — no flaky HTML parsing, no headless browser. Export to **JSON, CSV, Excel, or an API**.

**Every column is a scalar or a list of text.** Pick the mode for the shape you want — a row per hostel, a row per room type, or a row per review — and the export opens as a table, with no nested objects to unpack.

***

### Why use this Hostelworld Scraper?

- **No code required** — set your input, click **Start**, download your data.
- **Fast & accurate** — reads Hostelworld's own mobile API and returns structured JSON, not scraped HTML.
- **Rich, structured data** — every field below, clean and ready to use. Flat columns only: no nested objects, so a CSV or Excel export opens as a table.
- **You choose the grain** — a row per hostel, a row per room type, or a row per review, and the bill is per hostel either way.
- **A whole city in one run** — Hostelworld returns a city's entire property list in a single response, so there are no pagination gaps to worry about.
- **Transparent pay-per-event pricing** — you are charged for what a run delivers; the **Pricing** tab lists the events and their current prices.
- **Export anywhere** — JSON, CSV, Excel, XML, or pull it live via the Apify API.

***

### What can you do with Hostelworld data?

- **Market & competitor research** — compare every hostel in a city on price, rating and facilities.
- **Price & availability monitoring** — set a check-in date and track dorm and private rates over time, per hostel or per room type.
- **Review mining** — every review of every hostel in a city, with its sub-scores and traveller type, one row each.
- **Data science & trend analysis** — build clean datasets of hostel pricing and ratings.
- **Lead generation & enrichment** — feed hostel names, addresses and geo into your own tools.

***

### What data does the Hostelworld Scraper extract?

A hostel row carries `id`, `name`, `type`, `url`, `overallRating`, `numberOfRatings`, the
seven rating sub-scores, `pricePerNight`, `dormPricePerNight`, `privatePricePerNight`, their
average and pre-discount counterparts, `discountPercent`, `currency`, `city`, `country`,
`district`, `address1`, `address2`, `latitude`, `longitude`, `distanceFromCentreKm`,
`overview`, `images`, `imagesGallery`, `facilities`, `facilitiesFree`, `facilitiesPaid` and
`promotions` — plus the profile columns (`description`, `directions`, `checkInFrom`,
`policies`, `taxes`, …) when profiles are on. A room row is one dorm or private room with its
own price, capacity and rate plan; a review row is one review with its ratings and who wrote
it. See the full **field table** below.

***

### How do I scrape Hostelworld? (3 steps)

1. **Pick a mode** — it decides what one row is: a hostel, a room type, or a review.
2. **Say which hostels** — a location in `q`, or IDs and URLs in `propertyUrls`. Both work in every mode. Everything else has a default.
3. **Run & export** — click **Start**, then download the dataset as JSON/CSV/Excel or fetch it via the API.

***

### Which mode do I want?

The mode decides **what one row is**. The hostel source is a separate choice, so any mode
can run on a whole city (`q`) or on hostels you name (`propertyUrls`).

| `mode` | One row is | Rows per hostel | Billed per hostel |
| --- | --- | --- | --- |
| `search` | a hostel, as the search lists it, plus whichever `include…` extras you switch on | 1 | `property-row`, plus each extra you add |
| `properties` | a hostel with its profile — description, directions, facilities, policies, cancellation terms, fees, check-in times | 1 | `property-row` + `detailed-profile` |
| `availability` | a hostel's prices for your dates — lowest dorm and private, pre-discount, promotions, beds free | 1 | `property-row` + `room-availability` |
| `rooms` | **one dorm or private room on offer**, with its own price, capacity, rate plan and photos | one per room type | `property-row` + `room-availability` |
| `reviews` | **one guest review**, with its per-category ratings and the traveller who wrote it | one per review | `property-row` + `review-item` per review |

`availability` and `rooms` make the **same request** and cost the same — one gives you a
hostel per row, the other a room per row. Pick `rooms` when you want dorm prices as a table
rather than a lowest-price summary.

A mode **fetches only its own slice**: asking for reviews cannot also bill you for profiles.
Measured on live runs — `reviews` for two hostels at three reviews each charges
`property-row=2` and `review-item=6`, and nothing else.

Searching is not a prerequisite. Every slice endpoint is addressed by property ID, so a run
on `propertyUrls` costs one request per hostel per slice with no listing call in front of it.

#### Finding a hostel's ID

`propertyUrls` takes a numeric ID or any hostelworld.com property URL:

```
98483
https://www.hostelworld.com/hostels/p/98483/avenue-hostel-and-suites/
```

The name in the URL is cosmetic — Hostelworld resolves the page by ID and redirects — so a
stale or truncated slug still works. Duplicates are collapsed before anything is billed.

***

### What inputs does it take?

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | What one row is: `search`, `properties`, `availability`, `rooms` or `reviews`. |
| `q` | string | `Lisbon` | A location to work on: a city, neighbourhood, or property name (e.g. 'Lisbon'). Resolved via Hostelworld's own mobile autocomplete. **Works in every mode.** Ignored when `propertyUrls` is also set. |
| `propertyUrls` | array | `[]` | Work on these hostels instead of searching, as numeric IDs or hostelworld.com property URLs. **Works in every mode**, and wins over `q`. |
| `includeDetails` | boolean | `true` | **Mode `search` only.** Add each hostel's profile to the row. One extra request per hostel, billed as `detailed-profile`. |
| `includeAvailability` | boolean | `false` | **Mode `search` only.** Add the dates' prices, promotions and beds-free columns. Billed as `room-availability`. Setting `dateStart` switches this on by itself. |
| `reviewsPerProperty` | integer | `10` | **Mode `reviews`.** How many reviews per hostel, in `reviewsSort` order — one row and one charge each. Values above 50 are fetched in pages of 50. |
| `reviewsSort` | string | `-date` | `-date` newest, `date` oldest, `-rating` best, `rating` worst first. Worst-first is the one for complaint monitoring. |
| `dateStart` | string | – | Check-in date `YYYY-MM-DD`, not in the past. Defaults to **tomorrow** when availability is fetched. |
| `daysAhead` | integer | – | Price this many days from the run's start date instead — for scheduled monitoring, where a fixed date goes stale. Setting both is rejected. |
| `numNights` | integer | `2` | Number of nights to price, 1-30. Hostelworld refuses 31 even though its own error message says 31 is allowed — measured. |
| `numberOfGuests` | integer | `2` | Guests to price for. Changes which beds and rooms are offered at all. |
| `currency` | string | `EUR` | Three-letter ISO currency code for every price on the row. All modes. Case does not matter: `usd` is sent as `USD`, because Hostelworld rejects a lower-case code outright. |
| `maxItems` | integer | `50` | Maximum **hostels** to work on (`0` = all). It caps hostels, not rows, because the hostel is the billing unit: 20 hostels in `rooms` mode is 20 charges and perhaps 150 rows. |
| `proxyConfiguration` | object | Apify Proxy | Route requests through a proxy (Apify Proxy by default). |

> **About `q`:** it's resolved through Hostelworld's own autocomplete. The actor prefers a **city** match among the suggestions and otherwise takes the first result. A city name (e.g. `"Lisbon"`) returns every property in that city (capped by `maxItems`); a specific property name (e.g. `"Lisbon Calling"`) returns just that one property.
>
> **Hostelworld indexes cities and properties, not districts or regions**, and it answers a name it does not know with its best guess rather than with nothing. Measured: `Alfama` — a Lisbon neighbourhood — resolves to **Munich, Germany**, and `Europe` to **Andorra la Vella**. Gibberish (`zzzzqqqq`) correctly returns no match and the run says so. Because of that guess, **every search run's status message names the place it actually searched**, and says so loudly when the match shares no name with your query:
>
> ```
> Searched Lisbon, Portugal (Hostelworld's match for 'Lisbon'). Delivered 5 rows from 5 hostels.
> Check the location: Hostelworld matched 'Alfama' to 'Munich, Germany', which shares no name
> with what you asked for. If that is the place you meant, nothing is wrong.
> ```
>
> A native name is not a typo, so `lisboa` → Lisbon and `Roma` → Rome pass without a warning. To scrape a neighbourhood, search the city and filter on the `district` column, or put the hostels' IDs in `propertyUrls`.

> **`includeReviews` is gone.** It was a search-mode toggle that nested a list of reviews
> inside a hostel row. A row now holds only scalars, so reviews have their own grain: use
> `mode: "reviews"`, with the same `q` or `propertyUrls`. A run that still sends
> `includeReviews: true` is refused with that message rather than quietly dropping the data.

#### Example input

Search a city, with dorm prices for a two-night stay:

```jsonc
{
  "mode": "search",
  "q": "Lisbon",
  "maxItems": 50,
  "includeAvailability": true,
  "daysAhead": 14,
  "numNights": 2,
  "numberOfGuests": 2,
  "currency": "EUR",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

Track the dorm price of three hostels you already know — no search, no profile charge.
With `daysAhead` the target night moves with the schedule instead of going stale:

```jsonc
{
  "mode": "availability",
  "propertyUrls": [
    "https://www.hostelworld.com/hostels/p/98483/avenue-hostel-and-suites/",
    "65432",
    "52411"
  ],
  "daysAhead": 21,
  "numNights": 2,
  "numberOfGuests": 2,
  "maxItems": 0
}
```

Watch for complaints — the five worst reviews of every hostel in a city. One row per
review, and `maxItems` caps the hostels, not the rows:

```jsonc
{
  "mode": "reviews",
  "q": "Lisbon",
  "reviewsPerProperty": 5,
  "reviewsSort": "rating",
  "maxItems": 25
}
```

Dorm prices as a table — one row per room type, with its capacity, rate plan and
pre-discount price:

```jsonc
{
  "mode": "rooms",
  "propertyUrls": ["98483", "52411"],
  "daysAhead": 14,
  "numNights": 2,
  "numberOfGuests": 2,
  "maxItems": 0
}
```

***

### What does a Hostelworld row look like?

A hostel row, from `mode: "search"` with profiles and the dates' prices on (long text
truncated here):

```jsonc
{
  "id": 98483,
  "name": "Avenue Hostel and Suites",
  "url": "https://www.hostelworld.com/hostels/p/98483/avenue-hostel-and-suites/",
  "city": "Lisbon",
  "country": "Portugal",
  "currency": "EUR",
  "pricePerNight": "17.93",
  "dormPricePerNight": "17.93",
  "privatePricePerNight": "24.05",
  "averagePricePerNight": "17.91",
  "averageDormPricePerNight": "17.91",
  "averagePrivatePricePerNight": "46.17",
  "originalPricePerNight": "24.87",
  "originalDormPricePerNight": "24.87",
  "originalPrivatePricePerNight": "64.12",
  "discountPercent": 28.0,
  "type": "HOSTEL",
  "overallRating": 91,
  "numberOfRatings": "2154",
  "ratingValueForMoney": 89, "ratingSecurity": 93, "ratingLocation": 91,
  "ratingStaff": 94, "ratingAtmosphere": 86, "ratingCleanliness": 92,
  "ratingFacilities": 88,
  "district": null,
  "address1": "Rua Rodrigues Sampaio nº138",
  "address2": null,
  "latitude": 38.724085,
  "longitude": -9.147335,
  "distanceFromCentreKm": 1.35,
  "distanceUnits": "km",
  "starRating": 0,
  "overview": "More than an Hostel, AVENUE is a home for travelers where …",
  "isFeatured": true, "isPromoted": false, "isNew": false,
  "hostelworldRecommends": false,
  "freeCancellationAvailable": false,
  "freeCancellationAvailableUntil": "2026-10-14T13:46:09+01:00",
  "promotions": ["ALLPLATFORMS -20%", "MOBILEPROMOTION -10%"],
  "images": ["https://a.hwstatic.com/propertyimages/9/98483/wmoautdkocyrrcmjhmsc.jpg"],
  "imagesGallery": ["https://a.hwstatic.com/image/upload/f_auto,q_auto/v1/propertyimages/9/98483/wmoautdkocyrrcmjhmsc"],
  "facilities": ["ATM", "Bar", "Board games", "…"],
  "facilitiesFree": ["Free City Maps", "Free WiFi", "Free Internet Access", "Free City Tour"],
  "facilitiesPaid": ["Towels for hire"],

  // profile columns — `includeDetails`, or mode `properties`
  "description": "More than an Hostel, AVENUE is a home for travelers where …",
  "directions": "From airport by subway: 1. Take the Metro (Red line) …",
  "hostelworldSays": null,
  "checkInFrom": "15:00",
  "checkInUntil": "23:00",
  "checkOutUntil": "12:00",
  "maxGuestsPerBooking": 8,
  "policies": ["Age Restriction", "Non Smoking", "Taxes Not Included"],
  "paymentMethods": ["Electron", "JCB", "Maestro", "Mastercard", "Visa", "VisaDebit"],
  "cancellationPolicy": "This property has a 2 day cancellation policy. …",
  "cancellationPolicyDays": 2,
  "freeCancellationDaysInAdvance": 3,
  "thingsToNote": "AVENUE Hostel & Suites Policies and Conditions: …",
  "taxesIncluded": false,
  "taxes": ["City Tax 4 per person per night"],
  "feesIncluded": true,
  "depositPercentage": 15,
  "videos": [],

  // availability columns — `includeAvailability`, or mode `availability`
  "checkInDate": "2026-10-30",
  "nights": 2,
  "guests": 2,
  "vat": 0,
  "cancellationOptions": ["Free Cancellation rates", "Non-refundable rate"],
  "dormTypesAvailable": 3,
  "privateTypesAvailable": 4,
  "bedsAvailable": 87
}
```

A **room row**, from `mode: "rooms"` — one per dorm or private room on offer:

```jsonc
{
  "id": 98483,
  "name": "Avenue Hostel and Suites",
  "url": "https://www.hostelworld.com/hostels/p/98483/avenue-hostel-and-suites/",
  "city": "Lisbon",
  "country": "Portugal",
  "checkInDate": "2026-10-30", "nights": 2, "guests": 2,
  "roomId": 547681,
  "roomName": "Basic 4 Bed Mixed Dorm",
  "roomLabel": "Bed in 4-Bed Mixed Dormitory Room",
  "roomCategory": "dorm",
  "roomType": "Mixed Dorm",
  "roomGrade": "Basic",
  "capacity": 4,
  "isEnsuite": false,
  "bedsAvailable": 12,
  "roomsAvailable": null,
  "currency": "EUR",
  "pricePerNight": "20.01",
  "averagePricePerNight": "20.18",
  "originalAveragePricePerNight": "28.03",
  "discountPercent": 28.0,
  "totalPrice": "40.36",
  "originalTotalPrice": "56.06",
  "ratePlanCount": 2,
  "cheapestPaymentOption": "Non-refundable",
  "paymentOptions": ["Deposit only", "Non-refundable"],
  "roomDescription": "Dormitory style and range in size from 2 beds upwards. …",
  "roomImages": ["https://a.hwstatic.com/image/upload/f_auto,q_auto/v1/propertyimages/9/98483/cgeliftecjhyzzpm1akq"]
}
```

A **review row**, from `mode: "reviews"` — one per review, with the hostel's review mix on
each so the export reads on its own:

```jsonc
{
  "id": "98483",
  "name": "Avenue Hostel and Suites",
  "url": "https://www.hostelworld.com/hostels/p/98483/avenue-hostel-and-suites/",
  "city": "Lisbon",
  "country": "Portugal",
  "propertyReviewCount": 348,
  "propertyPositiveReviews": 138,
  "propertyNegativeReviews": 5,
  "propertySoloPercentage": 89.13,
  "propertyCouplesPercentage": 8.7,
  "propertyGroupsPercentage": 2.17,
  "reviewId": "16698211",
  "reviewDate": "2026-10-11",
  "reviewNotes": "very good for the price. But because shower/toilet are shared …",
  "reviewLanguage": "en",
  "isMachineTranslated": true,
  "ownerComment": null,
  "reviewRatingOverall": 91,
  "reviewRatingValueForMoney": 100,
  "reviewRatingSafety": 100,
  "reviewRatingLocation": 100,
  "reviewRatingStaff": 100,
  "reviewRatingAtmosphere": 80,
  "reviewRatingCleanliness": 80,
  "reviewRatingFacilities": 80,
  "guestType": "MALE",
  "guestAge": "25-30",
  "tripTypes": ["REGULARVACATION"],
  "guestNickname": "aFWFJgaI0yG0ZcCf",
  "guestGender": "Female",
  "guestNationality": "France",
  "guestReviewCount": 1
}
```

***

### Which fields are in each row?

**Every row of a run has the same columns, in the same order.** A `null` means Hostelworld
does not offer that value for that hostel; a column that is **absent** is one the mode could
never fill. Each grain has its own view in the dataset tab, so the export has the right
headers without being trimmed by hand.

| Columns | Modes that have them |
| --- | --- |
| identity: `id`, `name`, `url`, `city`, `country` | all five |
| listing, ratings, lowest prices, flags, images, facilities | `search`, `properties`, and the price columns in `availability` |
| profile (`description` … `videos`) | `properties`, and `search` with `includeDetails` |
| availability (average and pre-discount prices, `discountPercent`, `promotions`, `checkInDate` … `bedsAvailable`) | `availability`, and `search` with `includeAvailability` |
| room (`roomId` … `roomImages`) | `rooms` |
| review (`reviewId` … `guestReviewCount`) | `reviews` |

#### Hostel columns

| Field | Type | Description |
| --- | --- | --- |
| `id`, `name`, `type` | text | Hostelworld property ID, name, and property type (e.g. `HOSTEL`) |
| `url` | link | Property page on hostelworld.com. Built from the name's slug when the hostel came from a search; from the ID alone (`/hostels/p/98483/hostel/`, which redirects to the canonical page) in the modes whose payload carries no name |
| `overallRating` | number | Overall score on Hostelworld's **0-100** scale |
| `numberOfRatings` | text | How many guest ratings the score is based on |
| `ratingValueForMoney`, `ratingSecurity`, `ratingLocation`, `ratingStaff`, `ratingAtmosphere`, `ratingCleanliness`, `ratingFacilities` | number | The seven sub-scores, same 0-100 scale. Hostelworld sends these under two different sets of names depending on the endpoint; both are mapped onto these columns |
| `currency` | text | Currency of every price on the row |
| `pricePerNight` | text | Lowest nightly price across all room types |
| `dormPricePerNight` / `privatePricePerNight` | text | Lowest dorm bed / private room nightly price. **`null`, not `0`, when the property has none of that kind** — 29 of 82 Lisbon properties are hotels, guest houses and B\&Bs with no dorms at all, and Hostelworld sends `0.00` for them rather than omitting the key (measured 2026-11-01). **Their basis depends on where the hostels came from** — see *Which price am I looking at?* below |
| `city`, `country`, `district` | text | City, country, and the neighbourhood name when Hostelworld assigns one (31 of 82 Lisbon properties) |
| `address1`, `address2` | text | Street address lines |
| `latitude`, `longitude` | number | Coordinates |
| `distanceFromCentreKm` / `distanceUnits` | number / text | Distance from the city centre and its unit |
| `starRating` | number | Hotel-style star rating (`0` for most hostels) |
| `overview` | text | Short property blurb, HTML stripped |
| `isFeatured` / `isPromoted` / `isNew` / `hostelworldRecommends` | boolean | Placement and editorial flags |
| `freeCancellationAvailable` / `freeCancellationAvailableUntil` | boolean / text | Free-cancellation eligibility and deadline |
| `images` / `imagesGallery` | array | Full-size property photo URLs and the extended gallery |
| `facilities` | array | Every facility, as a flat sorted list of names |
| `facilitiesFree` / `facilitiesPaid` | array | The facilities Hostelworld files under *Free* and *Available for a Fee*, so "Towels for hire" does not read as a free towel |

#### Profile columns — `includeDetails`, or mode `properties`

| Field | Type | Description |
| --- | --- | --- |
| `description` / `directions` / `hostelworldSays` | text | The hostel's own description, how to get there, and Hostelworld's editorial note (set on 2 of 12 sampled hostels) |
| `checkInFrom` / `checkInUntil` / `checkOutUntil` | text | Check-in window and latest check-out, as `HH:MM` |
| `maxGuestsPerBooking` | number | Largest party Hostelworld will take in one booking |
| `policies` | array | House policies, e.g. `Age Restriction`, `Non Smoking` |
| `paymentMethods` | array | Cards accepted at the property |
| `cancellationPolicy` / `cancellationPolicyDays` | text / number | The deposit-rate cancellation terms and their notice period in days |
| `freeCancellationDaysInAdvance` | number | How far ahead a free cancellation has to be made |
| `thingsToNote` | text | The property's own long-form conditions |
| `taxesIncluded` / `taxes` | boolean / array | Whether tax is in the price, and each tax as `City Tax 4 per person per night` |
| `feesIncluded` | boolean | Whether property fees are in the price |
| `depositPercentage` | number | Deposit taken at booking, as a percentage |
| `videos` | array | Property video URLs (YouTube), when the hostel has any |

#### Availability columns — `includeAvailability`, or mode `availability`

Only the availability request carries these. The search listing has keys of
its own for the average, pre-discount and promotion figures and they are empty
on **every property measured** (762 across six cities, dated and undated), so
a mode that makes no availability request does not carry the columns at all
rather than carrying eight permanent nulls.

| Field | Type | Description |
| --- | --- | --- |
| `checkInDate` / `nights` / `guests` | text / number | What the prices on this row are for, so an export says so without its input |
| `averagePricePerNight` / `averageDormPricePerNight` / `averagePrivatePricePerNight` | text | Average nightly price over the stay. **Needs availability** — the search listing leaves these null even when dates are given (measured on 762 properties across six cities) |
| `originalPricePerNight` / `originalDormPricePerNight` / `originalPrivatePricePerNight` | text | Pre-discount price of the **average** nightly price above, when a promotion applies. Comes from the matching rate plan, so it pairs with `average…`, not with the lowest price — Hostelworld publishes no pre-discount figure for the lowest single night |
| `discountPercent` | number | Total discount on that rate plan, stacked promotions included (20% + 10% = 28%). **Needs availability** |
| `promotions` | array | Active promotions as `NAME -20%`, in the order they stack. **Needs availability** — the search listing's own promotions field is empty on every property measured |
| `vat` | number | VAT percentage applied |
| `cancellationOptions` | array | The rate-plan families on offer, e.g. `Free Cancellation rates` |
| `dormTypesAvailable` / `privateTypesAvailable` | number | How many dorm and private room types are bookable |
| `bedsAvailable` | number | Beds free across all of them |

#### Room columns — mode `rooms`

| Field | Type | Description |
| --- | --- | --- |
| `roomId`, `roomName`, `roomLabel` | text | Hostelworld's room ID, its name, and the label the site shows |
| `roomCategory` | text | `dorm` or `private` |
| `roomType` / `roomGrade` | text | e.g. `Mixed Dorm` / `Basic`, `Standard`, `Deluxe` |
| `capacity` / `isEnsuite` | number / boolean | Beds in the room, and whether the bathroom is private |
| `bedsAvailable` / `roomsAvailable` | number | Free beds in this room type, and free rooms where Hostelworld reports it |
| `pricePerNight` | text | The room's own lowest nightly price |
| `averagePricePerNight` / `originalAveragePricePerNight` | text | Average per night over the stay on the cheapest rate plan, and its pre-discount price |
| `totalPrice` / `originalTotalPrice` | text | The whole stay on that plan — `totalPrice` is what you pay, `originalTotalPrice` the pre-discount figure it was cut from |
| `discountPercent` | number | That plan's total discount |
| `ratePlanCount` / `cheapestPaymentOption` / `paymentOptions` | number / text / array | How many rate plans the room has, which one the prices above are from, and all of their payment terms — the cheapest is often the non-refundable one, so the price does not travel without it |
| `roomDescription` / `roomImages` | text / array | The room's description and photos |

#### Review columns — mode `reviews`

| Field | Type | Description |
| --- | --- | --- |
| `reviewId` / `reviewDate` | text | Review ID and the date it was left |
| `reviewNotes` | text | What the guest wrote |
| `reviewLanguage` / `isMachineTranslated` | text / boolean | Original language, and whether Hostelworld translated it |
| `ownerComment` | text | The property's reply (3 of 150 sampled reviews have one) |
| `reviewRatingOverall` | number | The review's own score, 0-100 — the mean of the seven below |
| `reviewRatingValueForMoney`, `reviewRatingSafety`, `reviewRatingLocation`, `reviewRatingStaff`, `reviewRatingAtmosphere`, `reviewRatingCleanliness`, `reviewRatingFacilities` | number | The reviewer's sub-scores |
| `guestType` | text | The traveller type Hostelworld records for the booking: `MALE` or `FEMALE` (a solo traveller), `COUPLE`, `MIXEDGROUP` |
| `guestAge` / `tripTypes` | text / array | Age band and trip purpose, e.g. `25-30`, `REGULARVACATION` |
| `guestNickname` / `guestGender` / `guestNationality` / `guestReviewCount` | text / number | The reviewer's profile. `guestGender` is self-reported and null on about two thirds of reviews |
| `propertyReviewCount` | number | How many reviews the hostel has in total, so you can tell a sample from the lot |
| `propertyPositiveReviews` / `propertyNegativeReviews` | number | Hostelworld's own positive/negative split for the hostel |
| `propertySoloPercentage` / `propertyCouplesPercentage` / `propertyGroupsPercentage` | number | Who stays there, as a percentage of its reviewers |

***

### Which facility categories does Hostelworld report?

Hostelworld groups facilities into categories. The grouping is organisational — a bar is
filed under *Food & Drinks* and a lift under *Accessibility* — so `facilities` flattens it
into one sorted list of names you can filter on directly, e.g. `"Bar" in facilities`.

These are the fifteen categories seen across twelve hostels in three cities:

| Category | Category | Category |
| --- | --- | --- |
| General | Bedroom | Kitchen |
| Services | Food & Drinks | Laundry |
| Entertainment | Safety & Security | Outdoors |
| Social Areas | Wellness | Accessibility |
| **Free** | **Available for a Fee** | Hostelworld Policies |

Two of them change what a facility *costs*, which a flat list would lose, so they keep their
own columns: `facilitiesFree` and `facilitiesPaid`. That is how you can tell free WiFi from
`Towels for hire`.

***

### What do Hostelworld's price and rating terms mean?

- **Dorm vs private** — a *dorm* price is per bed in a shared room. A *private* price is per person on the search listing and **for the whole room** in the availability payload. `dormPricePerNight` and `privatePricePerNight` are the cheapest of each, and `pricePerNight` is the cheaper of the two.
- **Which price am I looking at?** `dormPricePerNight`, `privatePricePerNight` and `pricePerNight` are filled by whichever payload the run fetched, and the two payloads quote on different bases. Measured over 30 Lisbon properties for the same stay, 2026-11-01:

  | hostels named by | source | matches the figure on hostelworld.com |
  | --- | --- | --- |
  | `q` (a location search) | the city listing | `privatePricePerNight` **28 of 30**, `dormPricePerNight` **26 of 30** |
  | `propertyUrls` (a list of IDs) | the availability request | `privatePricePerNight` **0 of 30**, `dormPricePerNight` 4 of 30 |

  Neither is wrong — they answer different questions. The listing quotes the
  "From" price the site advertises, which for a private room is **per person**;
  the availability request quotes what the room actually costs for your dates,
  which for a private room is **the whole room**. On hostel 98483 that is 19.35
  against 35.40 for the same night. If you need the figure a traveller sees on
  the site, search by location; if you need the bookable cost, use the
  `rooms` or `availability` mode and read `totalPrice` and `averagePricePerNight`,
  which agree across both sources.
- **`numberOfGuests` sizes a search, not a price** — the city listing honours it,
  but Hostelworld's availability request does not. Measured 2026-11-01 on three
  hostels, `numberOfGuests` of 1, 2, 4, 8, 16, 40 and 999 returned **identical**
  room types, bed counts and prices every time, including a 1-bed single private
  offered to a party of 999. The `guests` column records what you asked for, not
  a constraint Hostelworld applied, so read a private room's `capacity` to know
  who fits.
- **Lowest vs average vs original** — *lowest* is the cheapest single night found, *average* is the mean across the nights of your stay, and *original* is the pre-discount price shown when a promotion applies. All three are strings with the currency in `currency`.
- **Rate plans** — one room is sold on several plans (deposit-only, non-refundable, free cancellation), each with its own price. The `average…`, `total…` and `discountPercent` columns come from the **cheapest** plan, which is Hostelworld's own convention for a "lowest" price — and often the non-refundable one, so `cheapestPaymentOption` says which plan it is.
- **0-100 rating** — Hostelworld scores properties out of 100, not out of 5 or 10. `94` means 9.4/10 in the app's display.
- **`starRating`** — the hotel-style star count, which is `0` for most hostels. Use `overallRating` for quality, not this.
- **Distance from centre** — straight-line distance from the city centre, in the unit given by `distanceUnits` (km).

***

### How much does it cost?

The current price of every event is on the **Pricing** tab of this Actor's page in Apify Store. That tab is generated from the Actor's own pricing record, so it is always current, and your plan's rung is applied automatically. A figure repeated here would be a second copy that drifts out of date.

You can cap what any single run may spend with **Max total charge**.

### How do I run it from the API?

Run it programmatically with the [Apify API](https://docs.apify.com/api/v2):

```bash
curl -X POST "https://api.apify.com/v2/acts/hostelworld-api-scraper/runs?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
  "mode": "search",
  "q": "Lisbon",
  "maxItems": 50,
  "numberOfGuests": 2,
  "numNights": 2,
  "currency": "EUR",
  "includeDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}'
```

Or with the Apify CLI: `apify call hostelworld-api-scraper -i input.json -o`.

***

### FAQ

**Do I need a Hostelworld account or API key?** No. The actor talks to Hostelworld's mobile API for you — you only need an Apify account.

**How do I get dorm and private room prices per room type?** Use `mode: "rooms"` — one row per dorm or private room, with its capacity, rate plan, pre-discount price and photos. `mode: "availability"` makes the same request for the same price and gives you one row per hostel instead, with the lowest dorm and private prices. Either needs a date: set `dateStart`, or `daysAhead` for a scheduled run, and it defaults to tomorrow if you set neither. Without a date you still get the listing's `pricePerNight`, `dormPricePerNight` and `privatePricePerNight` — but not the average-price, pre-discount, `discountPercent` or promotion columns, which only the availability payload carries.

**Can I scrape hostels I already know, without searching?** Yes, in every mode. Put numeric IDs or hostelworld.com property URLs in `propertyUrls` and pick the mode you want. No location search runs, so nothing in the listing is fetched or billed. Note that `name`, `city` and `country` are then null in `rooms` and `reviews` rows — neither payload carries the hostel's name — but `url` is always there to join on, and `mode: "properties"` gives you the names.

**Can I scrape a whole city, not just the first page?** Yes. Hostelworld's city endpoint returns the city's entire property list in one response, so a run with `maxItems: 0` gets every property in that city. There is no page-by-page crawl to stall halfway.

**Does it return guest reviews?** Yes — `mode: "reviews"` returns one row per review, with its per-category ratings and the traveller who wrote it. It takes a location as happily as a list of IDs, so `{"mode": "reviews", "q": "Lisbon"}` collects reviews across a whole city. Reviews are billed per review and `reviewsPerProperty` sets how many (10 by default, newest first; `reviewsSort: "rating"` gives you the worst first). Asking for reviews does not also charge you for profiles.

**Can I schedule it?** Yes. Use Apify's **Schedules** to run it hourly or daily and append to the same dataset — the usual setup for tracking hostel prices for a city over time.

**How is cost bounded?** Set `maxItems` — it is a hard ceiling on the number of **hostels**, which is the billing unit, and every charge is per hostel except reviews, which are per review and capped by `reviewsPerProperty`. In `rooms` and `reviews` one hostel is many rows, so the row count can exceed `maxItems` while the bill cannot. The run also stops as soon as your **max total charge** is spent, and says so in its status message rather than looking like it ran out of hostels. When you paste a long list of IDs, raise `maxItems` or set it to `0`.

**I got a wall of HTTP 400 errors — what now?** You should not any more: the four things Hostelworld refuses with a 400 are all checked, fixed or explained before the first request.

| What you send | What used to happen | What happens now |
| --- | --- | --- |
| `currency: "usd"` | 400 on **every** request (it wants upper case), empty dataset | sent as `USD` |
| `currency: "eurr"` | same | refused before the run starts, with the reason |
| `numNights: 31` | 400 on every availability request | the input tops out at 30, the measured ceiling |
| `dateStart` in the past | 400 on every availability request | refused before the run starts, pointing you at `daysAhead` |
| `reviewsPerProperty` past a hostel's review count | the page after the last one 400s and **every** review of that hostel was lost | read as "no more pages"; you get all it has |

And if the upstream refuses something we have not seen, the run stops at the **first** hostel rather than repeating it for every one, and the status message carries Hostelworld's own words — rather than one log line per hostel and an empty dataset. The same applies when three hostels in a row fail the same way for any other reason. Nothing is delivered or charged in either case. A 400 and a 404 are answers, not blips, so neither is retried; 403, 429, 5xx and Cloudflare's 52x are retried with backoff, and a `Retry-After` the server sends is obeyed.

**What happens if one property fails?** Profile, availability and review fetches are best-effort per hostel: a failure leaves that hostel out and the run continues, so one bad ID never kills the run. Nothing is delivered or charged for it, and the run's status message names the IDs that were skipped — a hostel whose request failed and one that simply has no reviews yet are reported differently, because what you do next is different.

**Do I need a proxy?** A proxy is recommended for reliable runs; set `proxyConfiguration` (Residential is safest for geo-restricted or bot-protected sites).

***

### How does it work?

The actor impersonates Hostelworld's official Android app and calls the same mobile API endpoints it does: autocomplete to resolve your location, the city properties endpoint to list it, then per-hostel profile, availability and reviews. The three per-hostel endpoints are addressed by property ID alone, which is why a run on `propertyUrls` can skip the search entirely. Each response is normalised into the flat rows above — the mode decides the grain, nothing is nested, and no column is a column Hostelworld leaves empty on every hostel. No browser, no HTML scraping — just clean, structured data.

***

### Good to know

- Scrape only public listing data and respect Hostelworld.com's Terms of Service and robots policy.
- For personal data (reviews carry reviewer details), comply with GDPR/CCPA and applicable law — you are the data controller.
- Hostels are de-duplicated by property `id` **before** anything is charged, so the same hostel is never billed twice — including when a search ranking repeats it or your own list contains it twice.
- `rooms` and `reviews` rows share their hostel's `id` by design: the row key is `id` plus `roomId` or `reviewId`.

***

### Travel & booking toolkit

Part of the **Travel & booking toolkit** — search and pricing data across accommodation, transport, and travel-planning sites worldwide:

- [Booking.com API Scraper](https://apify.com/rl1987/booking-api-scraper) — Scrape Booking.com properties, live prices and full room-rate tables.
- [Agoda API Scraper](https://apify.com/rl1987/agoda-scraper) — Agoda search results, pricing and property details via GraphQL.
- [Airbnb API Scraper](https://apify.com/rl1987/airbnb-api-scraper) — Airbnb listings, details, and reviews via GraphQL API.
- [FlixBus API Scraper](https://apify.com/rl1987/flixbus-api-scraper) — FlixBus trips between cities — times, stations, duration, prices.
- [Changi Airport Timetable Scraper](https://apify.com/rl1987/sin-airport-timetable) — Arrival and departure flight timetables from Changi Airport (SIN).
- [eSIMDB Travel eSIM Scraper](https://apify.com/rl1987/esimdb-scraper) — Travel eSIM data plans from esimdb.com by country/region.

### Did you find this useful?

Rate this actor on Apify! Your feedback helps other users find it and helps us keep improving it.

# Actor input Schema

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

The mode decides <b>what one row is</b>; the next section decides <i>which hostels</i>. Every mode accepts either a location or a list of hostels, so "every review in Lisbon" and "the reviews of these three hostels" are the same mode. <b>Availability</b> and <b>Room types</b> fetch the same request and cost the same — one gives you a hostel per row, the other a room per row.

## `q` (type: `string`):

A location to work on: a city, neighbourhood, or property name (e.g. <code>Lisbon</code>). Resolved via Hostelworld's own mobile autocomplete, so a misspelling returns no match rather than a guess. Works in every mode. Ignored when <b>Hostel URLs or IDs</b> is also filled in.

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

Work on these hostels instead of searching. Accepts a numeric property ID (<code>98483</code>) or any hostelworld.com property URL (<code>https://www.hostelworld.com/hostels/p/98483/avenue-hostel-and-suites/</code>). The name in the URL is cosmetic — only the ID matters, so a stale or truncated slug still works. Duplicates are collapsed before anything is billed.

## `includeDetails` (type: `boolean`):

Add each hostel's own profile to the row: description, directions, facilities, policies, cancellation terms, fees, taxes and check-in/check-out times. One extra request per hostel, billed as <b>Hostel profile</b> ($3.00 per 1,000 from 26 Oct 2026). Mode <i>Hostel profiles</i> does this on its own.

## `includeAvailability` (type: `boolean`):

Add live availability for the dates below: lowest dorm and private price per night, pre-discount prices, promotions, deposit terms and how many beds are free. One extra request per hostel, billed as <b>Room and dorm availability</b> ($3.00 per 1,000 from 26 Oct 2026). Setting a <b>Check-in date</b> turns this on by itself, which is how it worked before this input existed. For a row per room type, use mode <i>Room types</i>.

## `reviewsPerProperty` (type: `integer`):

How many reviews to fetch per hostel, in the order below. Each review is a row and each review is billed, so this is your cost dial: a hostel can have hundreds (348 on one sampled hostel), and the default of 10 is one request. Values above 50 are fetched in pages of 50. Minimum 1 — unlike <b>Max items</b>, 0 is not "all reviews"; to skip reviews, use another mode.

## `reviewsSort` (type: `string`):

Which reviews you get when you ask for fewer than a hostel has. <b>Lowest rated first</b> is the one to use for complaint monitoring. Hostelworld ignores an unrecognised value silently, so anything outside this list is rejected before the run starts.

## `dateStart` (type: `string`):

Check-in date as <code>YYYY-MM-DD</code>. Defaults to tomorrow when availability is being fetched — not today, because same-day beds are often sold out and that reads as a broken actor rather than a full hostel. Must not be in the past — Hostelworld will not price a night that has gone, so a stale saved date is rejected before the run starts; use <b>Days ahead</b> for scheduled runs. In mode <i>Hostels</i>, setting this also switches room prices on. The date, nights and guest count are columns on every availability and room row, so an export says what the prices are for.

## `daysAhead` (type: `integer`):

Price this many days from the day the run starts — <code>0</code> tonight, <code>1</code> tomorrow, <code>7</code> a week out. Use this instead of <b>Check-in date</b> for scheduled monitoring: a relative target stays correct on every run, where a fixed date goes stale and eventually prices the past. Setting both is rejected.

## `numNights` (type: `integer`):

Number of nights to price, 1 to 30. Hostelworld refuses 31 with an error on every hostel, even though its own message says the limit is 31 — measured, so 30 it is.

## `numberOfGuests` (type: `integer`):

Guests to price for. Affects which dorm beds and private rooms are offered at all. A dorm price is per bed; a private price is for the whole room at this party size.

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

Three-letter ISO currency code for every price in the output (<code>EUR</code>, <code>USD</code>, <code>GBP</code>, …). Applies to all modes. Case does not matter — <code>usd</code> is sent as <code>USD</code>, because Hostelworld rejects a lower-case code with an error on every request.

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

Maximum number of <b>hostels</b> to work on (<code>0</code> = all of them). It caps hostels, not rows, because the hostel is what you are billed for: 20 hostels in mode <i>Room types</i> is 20 charges and perhaps 150 rows. When you paste a long list of IDs, raise it or set it to 0.

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

Route requests through a proxy (Apify Proxy by default).

## Actor input object example

```json
{
  "mode": "search",
  "q": "Lisbon",
  "propertyUrls": [],
  "includeDetails": true,
  "includeAvailability": false,
  "reviewsPerProperty": 10,
  "reviewsSort": "-date",
  "dateStart": "",
  "numNights": 2,
  "numberOfGuests": 2,
  "currency": "EUR",
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "mode": "search",
    "q": "Lisbon",
    "propertyUrls": [],
    "dateStart": "",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rl1987/hostelworld-api-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 = {
    "mode": "search",
    "q": "Lisbon",
    "propertyUrls": [],
    "dateStart": "",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("rl1987/hostelworld-api-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "mode": "search",
  "q": "Lisbon",
  "propertyUrls": [],
  "dateStart": "",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call rl1987/hostelworld-api-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rl1987/hostelworld-api-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/UTde4VZrQatuan7D2/builds/rglVeo9wnVXOedW4m/openapi.json
