# Rakuten Travel Hotel Scraper (`superslowsloth/rakuten-travel-hotel-scraper`) Actor

One query, one flat table: Japanese hotels and ryokan on Rakuten Travel with lowest plan price in JPY, rating, review count and plan name for your dates.

- **URL**: https://apify.com/superslowsloth/rakuten-travel-hotel-scraper.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** Travel, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.14 / 1,000 hotel scrapeds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Rakuten Travel Hotel Scraper

One query in, one flat table out. Give it a Japanese destination and a date and
it returns the hotels and ryokan Rakuten Travel has available that night, each
with the **lowest bookable plan price in JPY**, the plan that price belongs to,
the guest rating and the review count.

Built to be called by an agent: small input, small output, no pagination to
drive, no session to keep, deterministic field names.

### Input

| field | meaning |
|---|---|
| `destination` | `tokyo`, `kyoto`, `東京都`, or narrowed: `tokyo/新宿`, `kyoto/嵐山` |
| `hotelIds` | optional; hotel numbers or Rakuten hotel URLs, instead of a search |
| `checkIn` | ISO date, e.g. `2026-09-10` |
| `nights` | length of stay; check-out is derived |
| `adults`, `rooms` | party size, which changes the price and not just the filter |
| `maxResults` | hard stop; the run costs what it collects |
| `includeUnpriced` | off by default; see below |

Areas are resolved against Rakuten Travel's own area index while the run is
happening, not against a table baked into this actor. An unrecognised area name
fails immediately with the list of areas that do exist, so a typo costs one
request rather than an empty dataset.

### Output

One row per hotel:

```json
{
  "hotel_id": "141356",
  "hotel_name": "高濃度炭酸泉　八重桜の湯　スーパーホテルＰｒｅｍｉｅｒ東京駅八重洲中央口",
  "hotel_name_romaji": null,
  "area": "新宿・中野・荻窪・四谷",
  "price_jpy": 26000,
  "plan_name": "【素泊まり】【さき楽早得型】14日前のご予約におすすめ！…",
  "rating": 4.45,
  "review_count": 4176,
  "url": "https://travel.rakuten.co.jp/HOTEL/141356/141356.html",
  "access": "アクセス　：東京駅より徒歩３分…",
  "check_in": "2026-09-10",
  "check_out": "2026-09-11",
  "nights": 1,
  "adults": 2,
  "rooms": 1
}
```

`price_jpy` is tax-inclusive, covers the **whole stay for the whole party** as
searched, and is the cheapest plan Rakuten showed for those dates. `plan_name`
is the plan that price belongs to, read from the same block, so the two always
match.

### What this actor will not do

**`hotel_name_romaji` is always `null`.** Rakuten Travel's Japanese site
publishes no romaji or English hotel name anywhere on a search result.
`travel.rakuten.com` does carry English names, but it refuses connections from
datacenter addresses outright. Rather than transliterate the Japanese name and
hand you a machine guess dressed as a fact, the field is null. The same rule
covers the rest of the row: a hotel with no reviews yet gets `rating: null` and
`review_count: null`, never `0`, because a zero reads as a measurement.

**`area` is null when you search by `hotelIds`**, because a single-hotel lookup
carries no area heading to read it from.

**A hotel with no bookable plan for your dates is dropped, not billed for.**
Rakuten sometimes lists a property on a result page with nothing bookable on
it. You asked what it costs to stay there that night, and a row with a null
price is not that answer, so it is skipped, counted in the run log as
`unpriced skipped`, and never charged for. Set `includeUnpriced` if you want
those rows anyway - for example to enumerate every property in an area
regardless of availability.

**An empty result always arrives with its reason attached.** A run that
delivers nothing sets a status message saying which of these happened: the
area was genuinely sold out, every listed hotel was unpriced, the hotel id had
no availability, or Rakuten refused the search outright. It never returns an
unexplained empty dataset.

**A refused search is never reported as a sold-out night.** `searchVacant`
answers parameters it will not accept by redirecting to Rakuten's own area
index - HTTP 200, ~48 KB of prefecture links, no "no vacancy" notice anywhere
on it. This actor detects that page, along with bot interstitials and
responses that did not come from Rakuten at all, and fails the request instead
of reading "no hotel cards" as "no hotels". Interstitials and off-site
responses are treated as transient, so the retry rotates to a fresh exit
address; a result page whose markup has genuinely been rebuilt is treated as
permanent, because no address fixes that and retrying it would only spend your
money.

**Dates that cannot be answered are refused before anything is charged.** A
check-in in the past, one more than 365 days out, or a stay longer than 30
nights fails immediately with the reason, without billing the start event.

**Prices are what Rakuten displayed, not a quote.** They move; re-run for fresh
ones.

### Anti-bot

Measured 2026-08-24: `travel.rakuten.co.jp` answers datacenter addresses with
HTTP 200, no bot check, no cookie and no JavaScript required. The proxy input
is there for large jobs and for the day that changes; it is not needed today.

### Billing

Pay per event. `actor-start` covers the fixed cost of a run - including a run
that legitimately finds nothing on a sold-out night - and `hotel-scraped` is
charged once per row that reaches the dataset. Duplicates across pages are not
charged twice, and neither unpriced hotels nor a refused search are charged for
at all.

# Actor input Schema

## `destination` (type: `string`):

Where to search. A prefecture as a romaji slug (tokyo, kyoto, osaka, hokkaido) or as its Japanese name (東京都), optionally narrowed with a slash and an area or station name: tokyo/新宿, kyoto/嵐山, osaka/難波. Areas are resolved against Rakuten Travel's own area index at run time, and an unrecognised name fails with the list of areas that do exist. Ignored when hotelIds is supplied.

## `hotelIds` (type: `array`):

Optional. Look up specific properties instead of searching an area. Each entry is a Rakuten hotel number (141356) or any Rakuten Travel hotel URL it can be read out of. When this is set, destination is ignored and one request is made per hotel.

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

Check-in date in ISO form, for example 2026-09-10. Must be today or later and no more than 365 days out - Rakuten stops quoting real availability beyond that, and a date it cannot answer is rejected before the run is charged for. Prices on Rakuten Travel are per date, so this is what the price column means.

## `nights` (type: `integer`):

Length of stay in nights, 1 to 30. Check-out is derived from it, and the price returned covers the whole stay, not one night.

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

Number of adult guests. Japanese hotels price per person as often as per room, so this changes the price rather than only the filter.

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

Number of rooms the guests are split across.

## `maxResults` (type: `integer`):

Upper bound on hotels returned, across pages and areas, after duplicates are removed. Rakuten serves 30 per page and this actor stops as soon as the bound is reached, so a small number is a cheap run.

## `includeUnpriced` (type: `boolean`):

Off by default. Rakuten sometimes lists a hotel on a result page with no bookable plan for your dates; those rows carry a null price, so they are dropped and not charged for. Turn this on to receive them anyway - for example to see the full set of properties in an area regardless of availability.

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

Optional. Measured 2026-08-24: travel.rakuten.co.jp answers datacenter addresses with no bot check, so no proxy is needed. Leave it on anyway if you run large jobs - it gives a retry a fresh address if Rakuten ever starts rate limiting.

## Actor input object example

```json
{
  "destination": "tokyo/新宿",
  "checkIn": "2026-09-10",
  "nights": 1,
  "adults": 2,
  "rooms": 1,
  "maxResults": 30,
  "includeUnpriced": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `hotels` (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 = {
    "destination": "tokyo/新宿",
    "checkIn": "2026-09-10",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/rakuten-travel-hotel-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 = {
    "destination": "tokyo/新宿",
    "checkIn": "2026-09-10",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/rakuten-travel-hotel-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 '{
  "destination": "tokyo/新宿",
  "checkIn": "2026-09-10",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/rakuten-travel-hotel-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/rakuten-travel-hotel-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/oBcdIZ7jpSYqVt1Ih/builds/WlOiLRSrmyhdBUSyX/openapi.json
