Scraper · MakeMyTrip / Booking.com

Hotel Details

Get one hotel's profile as the OTA you choose publishes it: name, property type, stars, address, coordinates, description, check-in/out times, guest rating with sub-scores, amenities, photos, policies, nearby landmarks and the room catalogue. No prices — not a rate, not a fee, not an offer, and the room catalogue carries no availability.

POST/api/v1/scraper/hotel-details/— submit
GET/api/v1/scraper/hotel-details/{job_id}/— poll

Base URL: https://api.scrapeguys.com · Auth: X-API-Key: gsk_… with the details:<slug> scope of the OTA you name · See the Overview.

Its own scope, and one unit per lookup. The scopes are details:makemytrip and details:booking; a price scope (ota:<slug>) does not include them, and they are on no existing key — ask your ScrapeGuys contact to add the ones you need. Each lookup costs 1 unit, room catalogue included, whether or not the hotel is found; polling is free.
Every field is the chosen OTA's own statement. Nothing in the answer comes from Google, even when you identify the hotel by Place ID.

Submit a job

Send the OTA and one way to identify the hotel.

FieldTypeRequiredDefaultNotes
otastringRequired—makemytrip (alias mmt) or booking. An OTA not offered here, or switched off for the environment, is a 400.
ota_hotel_idstringOne identity required—That OTA's own id. MakeMyTrip: the hotelId. Booking.com: the numeric hotel id, or the web page's cc/slug (in/the-taj-mahal-palace-mumbai).
urlstring″—That OTA's hotel page; the id is read off it at submit. A URL for another OTA, or a link without an id, is a 400 that says why. A Goibibo link with mmtId= counts as MakeMyTrip.
place_idstring″—Google Place ID (or a Maps link carrying place_id:). Used only to find the hotel: the OTA id already mapped to that Place ID is used directly; with no mapping, the OTA is searched by the place's name and city. Don't have it? Look it up.
hotel_namestring″ (with city)—Searched on the OTA together with city. Beside one of the other identities it is only a hint.
citystringWith hotel_name alone—Optional when another identity is sent.
include_roomsbooleanOptionaltruefalse leaves result.rooms.items empty (status: "skipped") and the answer smaller.

If you send several identities, the most precise wins: ota_hotel_id / url, then place_id, then hotel_name + city. A stay is refused with 400 — check_in, check_out, adults, rooms, children_ages, currency and similar — because this endpoint describes the hotel, not a night's stay. For rates, use Cheapest Room or an OTA guide such as Booking.com.

POST /api/v1/scraper/hotel-details/
curl -X POST https://api.scrapeguys.com/api/v1/scraper/hotel-details/ \ -H "X-API-Key: gsk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "ota": "booking", "hotel_name": "The Taj Mahal Palace", "city": "Mumbai" }'
202 Accepted
{ "job_id": "3f6c1a2b-...", "status": "pending", "ota": "booking", "poll_url": "/api/v1/scraper/hotel-details/3f6c1a2b-.../" }

A lookup takes about 2–10 seconds: one request to MakeMyTrip, or two to Booking.com (the profile with its photos and rooms, then its surroundings), plus the OTA's own search when you identify the hotel by name.

Poll for results

Poll the poll_url every ~2 seconds until status is completed or failed.

statusfoundMeaning
pending / processingnullNot done yet.
completedtrueThe profile is in result.hotel, the rooms in result.rooms.
completedfalseAn answer, not an error: result.reason is not_found (nothing on the OTA matched what you sent; result.resolution.detail says what was tried) or not_listed (the OTA has no live listing under that id).
failednullThe lookup itself failed; error_message says why. Nothing is known about the hotel either way — retry.
hotel_details.py
import requests, time API = "https://api.scrapeguys.com/api/v1/scraper" H = {"X-API-Key": "gsk_your_api_key"} job = requests.post(f"{API}/hotel-details/", headers=H, json={ "ota": "makemytrip", "url": "https://www.makemytrip.com/hotels/hotel-details/?hotelId=201709071828297454", }).json() while True: res = requests.get(f"{API}/hotel-details/{job['job_id']}/", headers=H).json() if res["status"] in ("completed", "failed"): break time.sleep(2) if res["status"] == "failed": print("lookup failed:", res["error_message"]) elif res["found"]: hotel = res["result"]["hotel"] print(hotel["name"], hotel["star_rating"], hotel["rating"]) print(len(res["result"]["rooms"]["items"]), "room types") else: print("not found:", res["result"]["reason"])

Response schema

200 OK — GET /hotel-details/{job_id}/
{ "id": "3f6c1a2b-...", "status": "completed", "ota": "makemytrip", "ota_hotel_id": "201709071828297454", "include_rooms": true, "found": true, "hotel_id": "201709071828297454", "result": { "ota": "makemytrip", "found": true, "reason": null, "hotel_id": "201709071828297454", "listing_url": "https://www.makemytrip.com/hotels/hotel-details/?hotelId=201709071828297454", "resolution": {"input": "ota_hotel_id", "provenance": "supplied", "match_score": null, "matched_name": null}, "incomplete": [], "hotel": { "name": "The Taj Mahal Palace, Mumbai", "property_type": "Hotel", "star_rating": {"value": 5.0, "type": "official"}, "address": {"line": "Apollo Bunder, Mumbai, Maharashtra 400001, India", "locality": "Colaba", "city": "Mumbai", "state": null, "country": "India", "country_code": "IN", "postal_code": "400001"}, "latitude": 18.92173, "longitude": 72.83303, "description": "Location: The Taj Mahal Palace is situated in Mumbai ...", "check_in_time": "2 PM", "check_out_time": "12 PM", "rating": {"score": 4.7, "scale": 5, "review_count": 1686, "label": "Excellent", "sub_scores": [{"name": "Location", "score": 4.8, "review_count": 163}]}, "amenities": [{"group": "Family and Kids", "items": ["Childcare Services (Paid)"]}], "images": [{"url": "https://r1imghtlak.mmtcdn.com/....jpg", "caption": "355 sq.ft (33 sq.mt), Sea View, King Bed", "category": "Room"}], "policies": {"min_check_in_age": 18, "children": ["1 infant (0-2 yrs) per room included without counting in total room capacity"], "extra_bed": ["..."], "rules": [{"category": "ID Proof Related", "items": ["Passport, Aadhaar, Driving License and Govt. ID are accepted as ID proof(s)"]}]}, "landmarks": [{"name": "Gateway Of India Mumbai", "category": "Tourist Attraction", "distance": "5 min walk", "distance_m": 470.0}] }, "rooms": {"status": "ok", "items": [{"name": "Presidential Suite Sea View King Bed", "room_id": "1126062", "room_size": "467 m² / 5027 ft²", "room_size_sqm": 467.0, "bed": "1 King Bed", "view": "Sea view", "max_guests": 3, "description": null, "amenities": ["Bathtub", "Safe"], "images": ["https://r1imghtlak.mmtcdn.com/....jpg"]}]}, "error": null, "scraped_at": "2026-10-24T14:02:11+00:00" }, "error_message": "", "created_at": "...", "started_at": "...", "completed_at": "...", "duration_seconds": 2.8, "bandwidth_kb": 66.2 }

Result fields

FieldMeaning
hotel_idThe OTA id the profile was read from. For Booking.com, the numeric hotel id even when you sent the web slug.
listing_urlThe OTA's own page for the hotel — a permanent URL, never a dated or tracking link.
resolutionHow the hotel was identified: input (which of your fields decided it), provenance (supplied your id; place_pinned an id we already hold for that Place ID; cache / catalog / live the OTA's search), plus match_score and matched_name when a search picked it. Never carries anything Google said.
incompleteSections the OTA did not deliver on this answer — landmarks, rating, images or rooms — so an empty one is not mistaken for "the OTA lists none". Usually []; a later lookup normally fills the gap.
hotel.star_rating.typeofficial (a classification) or self_rated (Booking.com marks a property's own assessment this way); null when the OTA did not say.
hotel.rating.scaleThe scale the score is on: 5 on MakeMyTrip, 10 on Booking.com. Scores are reported as the OTA states them, not rescaled. A MakeMyTrip score of 0 means unrated, not a 0/5 rating.
hotel.address.stateNot stated by either OTA today; kept for the schema.
hotel.policiesmin_check_in_age, children and extra_bed lines, and every other house rule under the OTA's own category, all verbatim.
hotel.landmarks[].distanceAs the OTA words it ("5 min walk", "2.4 km"); distance_m is the same in metres where it can be read.
rooms.items[]The room catalogue: name, id, size, bed, view, capacity (max_guests, unknown when the OTA's figure is implausible), description, amenities, photos. No offers, no prices, no availability.
rooms.statusok, no_rooms (the OTA listed none) or skipped (include_rooms: false).

What each OTA provides

MakeMyTripBooking.com
PhotosUp to 60 of the property's own photos, by categoryUp to 60 from the property's gallery, uncategorised
RatingScore /5, count, label, sub-scoresScore /10, count, sub-scores; no label
LandmarksUp to 5 of each kind listed (attractions, restaurants, metro, ferry, airport…)Up to 5 attractions, nearby places, beaches and restaurants; up to 2 metro, train and airport
PoliciesMust-read and house rules, infant, child and extra-bed linesMinimum check-in age, whether children are allowed, pets, ID/card at check-in, accepted cards, fine print. No child or extra-bed policy text
RoomsEvery room type, from the same callEvery room type the property lists, from the same call
No contact details. Neither OTA publishes the hotel's own phone number or email, so the profile has no contact block. Extra-bed policy lines can quote the property's own charge ("INR 700 will be charged for an extra mattress"); that is policy text kept verbatim, not a room rate.

Errors

Submit — 400 (no identity, a stay sent, a URL for another OTA, an OTA not offered or switched off), 401 (missing, unknown or deactivated key), 403 (key lacks details:<slug>), 429 (per-minute limit, or daily / monthly quota exhausted). No job is created on any of these. Poll — 404 (unknown id, or another client's job), 401, 403 (key lacks the scope of the job's OTA).