Rakuten Travel Purpose-Based Hotel Recommender
Pricing
from $0.00005 / actor start
Rakuten Travel Purpose-Based Hotel Recommender
Search Rakuten Travel hotels by area, budget, and purpose — get onsen, business, family, and solo-traveler picks with a score.
Searches hotels via the official Rakuten Travel API (楽天ウェブサービス / Rakuten Web Service) by area × budget band × purpose (onsen / business / family / solo-traveler) and returns each hotel with a transparent recommendation score — built for AI agents and travel apps to consume directly.
Features
- Search hotels by area, budget, and travel purpose
- Find onsen hotels via Rakuten's official filter
- Detect business, family, and solo-traveler hotels
- Score and rank hotels by review quality and price fit
- Get hotel images, review data, and booking links
⚠️ Disclaimer (please read)
- This Actor uses the official Rakuten Web Service API (commercial use allowed, free, instant
application ID issuance, no review) — not scraping. You need your own
applicationIdandaccessKey, issued together at https://webservice.rakuten.co.jp/app/create, to run it. Rakuten migrated its API infrastructure in 2026: both credentials (UUID-formatapplicationId,pk_-prefixedaccessKey) are required — a pre-migration,applicationId-only key will not work. recommendationScoreandpurposeTags(business/family/solo) are generated by this Actor's own logic on top of Rakuten's data. They are a convenience estimate, not an official Rakuten classification. Always verify details on the official hotel page before booking.- Rakuten enforces 1 request/second per
applicationId; exceeding it returns HTTP 429. This Actor enforces a minimum 1100ms delay between paginated calls for safety (see "Rate limit" below). - Area codes:
middleClassCode,smallClassCode, anddetailClassCodeare all required — Rakuten's own API docs suggestmiddleClassCodealone should be enough, but in practice the search rejects anything short of the full 4-level chain. Look codes up via Rakuten's Area Class API (GetAreaClass); well-known values liketokyo/tokyo/A(central Tokyo) work out of the box. - Security note: this Actor's Rakuten app has its IP allowlist set to
0.0.0.0/0(open to any IP — needed since Apify's cloud runs from dynamic, shared outbound IPs), soapplicationId/accessKeyare the only access control left on that Rakuten app. Both are markedisSecret: truein the input schema and never logged or included in output.
Purpose classification: official vs. estimated
This is the core design point of this Actor's output:
| Purpose | source | confidence | How it's determined |
|---|---|---|---|
onsen (温泉) | official | always 1.0 | Rakuten's own squeezeCondition=onsen filter on the Vacant Hotel Search API — the hotel is only included in the search results because Rakuten itself tagged it as an onsen property. |
business (ビジネス) | heuristic | 0–0.95 | Keyword-dictionary match (src/purposeClassifier.js) against the hotel's own hotelSpecial free-text description, name, and facility fields. |
family (ファミリー) | heuristic | 0–0.95 | Same keyword-dictionary approach, different dictionary. |
solo (一人旅) | heuristic | 0–0.95 | Same keyword-dictionary approach, plus a small contextual boost when the search itself specifies 1 adult (adultNum: 1). |
Each hotel's purposeTags array can contain multiple tags at once (e.g. an onsen ryokan can also match
the family dictionary). There is no official Rakuten API parameter for business/family/solo — that
gap is exactly what this Actor's keyword dictionary fills (tunable in src/purposeClassifier.js, same
pattern as classify.js in our PR TIMES Actor).
What does this Actor do?
- Calls Rakuten's Vacant Hotel Search API (
VacantHotelSearch, 2017-04-26) — the only Rakuten Travel endpoint with a real price-band filter (minCharge/maxCharge) — filtered by area (middleClassCodeetc.), budget band, stay dates, and occupancy. Whenpurposeisonsen, this call also passes Rakuten's ownsqueezeCondition=onsen. - Tags each hotel with
purposeTagsas described above. - When
purposeisbusiness/family/solo, keeps only hotels where that tag was detected (confidence > 0). - Scores every remaining hotel with
recommendationScore(0–100,src/scoring.js) combining review quality, price fit within the requested budget band, and purpose-match confidence — with ascoreBreakdownso the weighting is inspectable, not a black box. - Sorts by
recommendationScoredescending and outputs the topmaxItems.
Who is this for?
Existing similar Actors (piquno/rakuten-travel-scraper, shiokoshi356/rakuten-travel-scraper) stop at dumping raw hotel list/detail data. This Actor's differentiation (same stack as our BOOTH, SUUMO, and PR TIMES Actors: JavaScript ESM, Apify SDK, Pay-per-event pricing) is structuring the result as a curated, scored recommendation instead — and clearly labeling which part of that recommendation is official Rakuten data vs. this Actor's own estimate (see "Purpose classification" above).
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
applicationId | string (secret) | — | Required. Your Rakuten Web Service application ID (UUID format). |
accessKey | string (secret) | — | Required. Your Rakuten Web Service access key, issued alongside applicationId. |
middleClassCode | string | — | Required. Prefecture-level area code (e.g. "tokyo"). |
smallClassCode | string | — | Required. City/ward-level area code (e.g. "tokyo" for central Tokyo's 23 wards). |
detailClassCode | string | — | Required. Station/neighborhood-level area code (e.g. "A"). |
largeClassCode | string | "japan" | Top-level area code. Rarely needs to change. |
minCharge / maxCharge | integer | none | Budget band, JPY per room per night. |
checkinDate / checkoutDate | string | tomorrow / day after | Format YYYY-MM-DD. |
adultNum | integer | 2 | Number of adults. Also a contextual signal for the solo tag. |
purpose | string | "any" | any / onsen (official) / business / family / solo (estimated). |
maxItems | integer | 20 | Cap on hotels output/charged (max 100). |
requestDelayMs | integer | 1100 | Delay between paginated Rakuten API calls. Cannot go below 1100ms (Rakuten's 1 req/sec limit). |
Look up middleClassCode/smallClassCode/detailClassCode via Rakuten's Area Class API
(GetAreaClass) — see "FAQ & known limitations" for a note on making this step easier in a future version.
Output
Actual output from a real run against a live applicationId/accessKey (2026-08-06, Tokyo/Ginza area,
purpose: "onsen"):
{"hotelNo": 182801,"hotelName": "天然温泉 七宝の湯 ドーミーインPREMIUM銀座","hotelKanaName": "どーみーいんぷれみあむぎんざ","hotelSpecial": "銀座エリア唯一の”黒湯”天然温泉・高温サウナ完備! 最上階には和風プレミアムフロアを御用意しております","area": {"middleClassCode": "tokyo","smallClassCode": "tokyo","areaName": null,"address1": "東京都","address2": "中央区銀座6-16-8","nearestStation": "東銀座","latitude": 35.6676788,"longitude": 139.7657912},"priceBand": { "minCharge": null, "maxCharge": null },"hotelMinCharge": 12210,"reviewAverage": 4.62,"reviewCount": 203,"stayCondition": { "checkinDate": "2026-08-07", "checkoutDate": "2026-08-08", "adultNum": 2 },"purposeTags": [{ "type": "onsen", "label": "温泉", "source": "official", "confidence": 1, "matchedTerms": [] }],"recommendationScore": 86.2,"scoreBreakdown": { "reviewScore": 46.2, "priceScore": 10, "purposeScore": 30 },"hotelInformationUrl": "https://img.travel.rakuten.co.jp/image/tr/api/re/pvonD/?f_no=182801","hotelImageUrl": "https://img.travel.rakuten.co.jp/share/HOTEL/182801/182801.jpg","reviewUrl": "https://img.travel.rakuten.co.jp/image/tr/api/re/gJNfM/?f_hotel_no=182801","disclaimer": "Reference information generated from data retrieved via the official Rakuten Travel API. The onsen tag is confirmed data reflecting Rakuten's own official filter (squeezeCondition=onsen); the business/family/solo tags are estimates based on simple keyword matching against each property's own description text (see purposeTagConfidence for the confidence level) and are not guaranteed to be accurate. Always check the official listing page for the latest details before booking.","retrievedAt": "2026-08-06T06:28:16.361Z"}
Note: hotelName/hotelSpecial/addresses are Rakuten's own Japanese-language data — Rakuten's API has no
English fields to translate from, so only this Actor's own disclaimer text is in English (see "FAQ"
below). Also note that area.areaName returns null in practice despite Rakuten's own documentation
listing it as present on this endpoint — a discrepancy in Rakuten's API/docs, not a bug in this Actor.
Pricing
| Event | Price | Trigger |
|---|---|---|
Actor Start (apify-actor-start) | $0.00005 | Once per run (Apify's standard synthetic event) |
Hotel recommended (hotel-recommended) | $0.035 | Per hotel output, with review data and a scored recommendation |
Purpose match detected (purpose-match-detected) | $0.01 | Charged in addition to hotel-recommended when at least one purpose tag (official or estimated) matched |
A tagged hotel costs $0.045 total; an untagged one costs $0.035.
How the price was set (measured on Apify, 2026-08-06)
Two production-like runs, both Tokyo/Ginza area (middleClassCode=tokyo/smallClassCode=tokyo/
detailClassCode=A), maxCharge: 30000:
| Run | purpose | Items output | API pages fetched | Duration | Real infra cost (usageTotalUsd) | Cost per item |
|---|---|---|---|---|---|---|
| 1 | any (25 requested) | 25 | 3 | 8.0s | $0.0006275855 | $0.0000251034 |
| 2 | business (20 requested, only 1 hotel matched the dictionary) | 1 | 2 | 4.4s | $0.0003041074 | $0.0003041074 |
| Blended | — | 26 | — | — | $0.0009316928 | $0.0000358343 |
~85–95% of the cost in both runs is ACTOR_COMPUTE_UNITS; dataset/key-value-store writes are a small
fixed add-on with no meaningful per-item scaling. Two things stand out:
- Per-item cost is dominated by fixed per-run overhead (container boot + the Rakuten API calls
themselves), not by item count. Run 2 fetched 2 pages (60 candidates) but only 1 survived the
businessheuristic filter, so that page-fetch cost landed entirely on a single output item — $0.0003/item, ~12x worse than Run 1's $0.000025/item. A purpose-filtered run that matches few or zero hotels is the worst case for per-item cost, and a zero-match run earns nohotel-recommendedrevenue at all (only the flat $0.00005apify-actor-startcharge) despite making the same Rakuten API calls — a structural gap worth being aware of, though the absolute amounts are small enough (a fraction of a cent per zero-match run) that it isn't worth added pricing complexity to close. - Purpose tagging itself adds no measurable infrastructure cost — official (
onsen) tagging is a literal object construction and heuristic tagging is a regex scan overhotelSpecialtext already fetched for the base search, with zero additional HTTP calls (same finding as the PR TIMES Actor'ssalesTimingSignals).purpose-match-detected's price is therefore a value-based decision, not something a cost measurement can inform — see below.
Following the same methodology as the PR TIMES Actor's README (price against the higher of the measured
per-item costs, not the optimistic blended average): hotel-recommended was set at $0.035 — a
placeholder $0.02 would only be 65.8x the worst-case $0.0003041/item figure (short of a 100x target),
while $0.035 clears it at 115x (and ~1150x against the typical/blended figure).
Why official and estimated purpose tags are priced the same
A case could be made for pricing business/family/solo higher than onsen: they're exactly the
categories Rakuten's own API can't filter by at all, so they're where this Actor's differentiation
actually lives, while onsen is close to a pass-through of a filter Rakuten already provides for free.
(The PR TIMES Actor prices its own analogous estimated signal, sales-timing-signal-detected, 4x higher
than its base event on this logic.)
This Actor instead uses one flat purpose-match-detected price regardless of source, because purpose
is the core promise of the product itself here, not a bonus layered on top of an already-complete result
the way PR TIMES's signal is — a caller asking for purpose: "family" and one asking for
purpose: "onsen"purposeTagConfidence already communicates reliability
directly, rather than folding it into a price split. A confidence-tiered price schedule is a reasonable
direction for a future version if usage data justifies the added billing complexity, but isn't
implemented here.
Rate limit
Rakuten enforces 1 request/second per applicationId (exceeding it returns HTTP 429). This Actor:
- Never sets
requestDelayMsbelow 1100ms (1000ms + safety margin) between paginated calls. - Auto-retries once, after a 3-second wait, if a 429 is received (
src/rakutenClient.js).
FAQ
How accurate are the business/family/solo purpose tags?
They're keyword-dictionary estimates only (src/purposeClassifier.js), not an official Rakuten
classification. Wording not covered by the dictionary won't be detected, and matches can be spurious
(e.g. a hotel merely mentioning "family restaurant nearby" could false-positive on family).
PURPOSE_DEFINITIONS can be tuned as real output is reviewed.
Do I need to know Rakuten's area codes myself?
Yes, for now — all 4 levels (largeClassCode/middleClassCode/smallClassCode/detailClassCode) are
required, so you need to look them up via Rakuten's Area Class API (GetAreaClass). A natural future
addition would be accepting a free-text area name and resolving it to the full code chain automatically,
or offering latitude/longitude + searchRadius as a simpler alternative input mode.
Does the recommendation score include per-aspect review ratings (cleanliness, service, etc.)?
Not currently. VacantHotelSearch only returns reviewAverage/reviewCount/userReview — the
per-aspect breakdown (service/location/room/equipment/bath/breakfast/dinner/cleanliness) would require an
additional call to SimpleHotelSearch or HotelDetailSearch per hotelNo, which this v0.1 skips to keep
the request count (and rate-limit exposure) minimal. Relatedly, hotelClassCode's exact value meanings
(hotel/ryokan/pension/etc.) aren't documented by Rakuten and haven't been fully confirmed against real
data yet.
Can I search for premium/luxury hotels specifically?
Not as a dedicated option — HotelRanking's genre=premium is a separate, area-less ranking endpoint
that isn't wired into this Actor, since the current purpose list (onsen/business/family/solo) didn't call
for it.
Is the hotel description translated to English?
No — hotelName/hotelSpecial/addresses etc. are Rakuten's raw Japanese text, since Rakuten's API has
no English fields to fall back to. Only this Actor's own boilerplate disclaimer text is in English.
Generating English summaries of the actual property descriptions would need an additional translation/LLM
step on top of hotelSpecial, not implemented here.