MakeMyTrip (Hotels)
Return the exact per-room-type price a real guest sees on MakeMyTrip's own site — not a metasearch or commission-inflated affiliate price. Submit a job, receive a job_id, poll for the result.
For India-specific pitfalls — GST threshold, number formatting, point of sale — see India hotel price data.
/api/v1/scraper/ota-price/— submit/api/v1/scraper/ota-price/{job_id}/— pollBase URL: https://api.scrapeguys.com · Auth: X-API-Key: gsk_… with the ota:makemytrip scope · See the Overview.
/api/v1/scraper/ota-price/ also prices Agoda, Booking.com, Expedia and Goibibo — set "ota": "makemytrip" (aliases mmt, makemytrip.com) to get this vendor. The request/response shape is the same across all five; this page documents it fully for MakeMyTrip Hotels. Looking for flights instead? See the separate MakeMyTrip FlightsCleartrip Flights guide — different endpoint, different scope, different response shape.Submit a job
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
ota | string | Required | — | Set to "makemytrip" explicitly (aliases mmt, makemytrip.com) — this endpoint defaults to Agoda when ota is omitted. A numeric channel code ("2" or bare 2) may be sent instead — see Addressing an OTA by number. |
hotel_name | string | Conditional | — | ≤ 300 chars. Resolved to MakeMyTrip's own property id via MakeMyTrip's own autosuggest. Required unless place_id or ota_hotel_id is given — see Identifying the hotel. |
city | string | Conditional | — | ≤ 200 chars. Required unless place_id or ota_hotel_id is given. |
place_id | string | Optional | "" | Google Place ID. Identifies the property itself — the same id works across Agoda, Booking.com, MakeMyTrip and Goibibo. Bridged to MakeMyTrip's own id on first use, then pinned — when MakeMyTrip names the property and that name agrees with the Place ID's own — so the same place_id returns the same property on every later request. Otherwise each request resolves again. See Identifying the hotel. |
ota_hotel_id | string | Optional | "" | MakeMyTrip's own numeric hotelId (e.g. "201709071828297454"). Skips resolution entirely — the single most expensive step of a job (roughly 2.0s of a ~2.9s MakeMyTrip job). See Identifying the hotel. |
check_in | date | Required | — | YYYY-MM-DD; not past, ≤ 364 days ahead on MakeMyTrip — it refuses the 365th day, so since 16 October 2026 that date is rejected with a 400 before any job is created. |
check_out | date | Required | — | After check_in; ≤ 30 nights. |
adults | int | Optional | 2 | 1–10. Total party size across all rooms, not per room. |
rooms | int | Optional | 1 | 1–8. Must be ≤ adults (at least one adult per room). |
children_ages | int[] | Optional | [] | One age per child, e.g. [4, 7] — not a count; the list length is the child count. Ages 0–17, max 8 children. Also priced on Agoda, Booking.com and Expedia — see Children below. |
currency | string | Optional | INR | INR only on MakeMyTrip (same on Goibibo, the same backend) — other codes are rejected with a 400 rather than mislabeled. |
device_type | string | Optional | desktop | desktop or mobile — accepted and echoed, but see Device pricing: MakeMyTrip is device-flat on price. app_android is rejected with a 400 (MakeMyTrip has no app route to select), as is any other value. |
children_ages (e.g. [4, 7]) rides the same roomStayCandidates[].childAges field MakeMyTrip's own site sends; a bare children count is rejected with 400 on every OTA, MakeMyTrip included — there's no honest way to turn a count into a request. See Children below for the request shape and a measured price delta.400. Fields this endpoint does not know are still ignored, so your own extras (client_ref, request_id, …) are harmless. But a key that is a near miss of a real field you did not send — a case or separator variant (checkIn, check-in) or a letter or two off (adult, room, child_ages) — is refused with a 400 naming the field it probably meant. Before this, "room": 2 priced one room with nothing in the response to say so.Identifying the hotel
A request identifies its hotel one of three ways.
By name (hotel_name + city) is the default — resolved through MakeMyTrip's own autosuggest and picked by us. It works without you knowing anything about MakeMyTrip, and is what every existing integration uses. It is also the only one of the three routes that can return the wrong hotel: MMT's own autosuggest has known failure modes and can match a same-named property in the wrong place. Since 16 October 2026 the matcher cleans spreadsheet-style names (trailing | City tags, descriptive brackets, chain prefixes), retries with the city added to the query when the name alone gives no acceptable pick, and checks every pick against the property's location and the city you sent — a candidate in another country or far from the city is refused, and unconfirmed matches are never cached. This makes a wrong match much rarer, not impossible. A property more than about 50 km from the city you named may be refused as unresolved; send the city the property is actually in. Prefer place_id or ota_hotel_id wherever you can.
By Google Place ID (place_id) is the recommended way to pin a portfolio. One id identifies the property across Agoda, Booking.com, MakeMyTrip and Goibibo, so you keep a single key per hotel instead of four. The first request for a place still goes through MakeMyTrip's own autosuggest — there's no other way in — but the id that actually returned a price is then pinned, so every later request for that place_id addresses the same property directly. A pin is written only when MakeMyTrip names the property and that name agrees with the Place ID's own; otherwise the job still returns prices and the next request resolves again. Send hotel_name alongside it if you have it — it's used as the search text for that one resolution; without it we look the name up from the Place ID itself.
By MakeMyTrip's own id (ota_hotel_id, its numeric hotelId, e.g. "201709071828297454") is the fastest and most direct route, and the one to prefer once you hold it. The whole resolution step disappears — roughly 2.0s of a ~2.9s MakeMyTrip job, the single most expensive step — and, unlike the name route, the answer cannot drift to a different hotel between runs. The id's format is checked at submit (digits only): a malformed id is rejected with 400 before any job is created or billed. A well-formed id from another OTA cannot be told apart and is priced as MakeMyTrip's own id — Agoda, MakeMyTrip and Goibibo ids are all plain numbers.
result.hotel_id, so one name-based run per property gives you the id to send from then on.Checking we priced the hotel you meant. Read result.origin_hotel_name — what MakeMyTrip itself calls the property it priced, sourced from the hotelName parameter of the deeplink MakeMyTrip's own site builds. That makes it a genuine statement from MakeMyTrip, not an echo of your text: when a job is submitted with ota_hotel_id, we send MakeMyTrip nothing but a hotel id, so any name in that deeplink is MMT's own. result.hotel_name cannot answer this question at all — it is your own search text echoed back, not a confirmation signal. An empty origin_hotel_name means either MakeMyTrip named nothing on that job, or you identified the hotel by ota_hotel_id and no name search ran, so there's nothing to echo unless MakeMyTrip's own response states a name — never that it agreed with you.
ota_hotel_id is used exactly as given and changes no state on our side — it's never written into our own name→id mapping. If MakeMyTrip reports the id as unknown (Hotel static info not found), we re-resolve once from hotel_name + city with that id excluded and price the current listing when its name still matches the hotel you asked for; a different hotel is refused. Send hotel_name + city alongside the id to get this recovery — without them the job fails with the id error.Device pricing — device_type
device_type is argument-driven across this API: desktop (default) or mobile. The field is accepted and echoed back on the job envelope.
mobile is accepted and echoed, but the prices are the same as desktop. (Contrast with Agoda and Booking.com, where mobile pricing is real and can differ on a minority of properties.)desktop and mobile are both web identities — no OTA lets you ASK for Android apart from iOS on its website. A separate app_android route exists for Agoda and Booking.com only. Goibibo is the one source that always answers as the Android app internally, regardless of what you send.
Poll for results
Poll the poll_url every ~2 seconds until status is completed or failed. Statuses: pending → processing → completed | failed. Job IDs are scoped to this endpoint — a Google Hotels job ID 404s here, and vice versa.
/api/v1/scraper/ota-price/{job_id}/Response schema
The poll response is a job envelope: id, status, ota, hotel_name, city, ota_hotel_id, place_id, check_in, check_out, adults, children_ages, tax_required, tax_scope, rooms, currency, device_type, result, error_message, created_at, started_at, completed_at, duration_seconds, bandwidth_kb — tax_required / tax_scope echo what you submitted (false / "" when you sent neither; tax_required itself is rejected on this OTA). On completion, result holds the pricing object, which nests HotelRoomPricing → room_types[] → offers[]. (bandwidth_kb is the measured, compressed proxy traffic for the scrape — response headers excluded, and failed scrapes count what they spent.)
hotel_name at the top of the envelope is not stable when you didn't send one. If you identified the hotel by place_id or ota_hotel_id alone, whatever appears here is incidental and can differ between two identical requests. Never join on it — use result.origin_hotel_name for MakeMyTrip's own name and result.hotel_id for its id.Object fields
| Object | Fields |
|---|---|
HotelRoomPricing | ota, ota_code, hotel_name, origin_hotel_name, hotel_id, currency, check_in, check_out, room_types[], adults, children_ages[], rooms, nights, adults_per_room, children_per_room, price_scope ("per_room"), occupancy_honored, cheapest_fitting_price, cheapest_fitting_basis, cheapest_fitting_meal_plan_code, cheapest_fitting_tax_status, cheapest_room, source ("http"), scraped_at, sold_out, error, truncated, suppliers[], rate_channels[] — suppliers[]/rate_channels[] are always empty on MakeMyTrip; those are Agoda-only channel fields. cheapest_fitting_tax_status is the tax_status of the offer cheapest_fitting_price was read from; cheapest_room is the cheapest fitting rate priced all-in for one room for one night — see Agoda's Response schema for both. ota_code is the numeric channel code ota also accepts on submit (see Addressing an OTA by number) — 2 here, present on failed jobs too. origin_hotel_name is what MakeMyTrip itself calls the property, or "" when it named none — see Identifying the hotel. children_ages echoes what you submitted ([] for an adults-only request, including on a failed job); children_per_room is the same list spread across rooms, rounded up. See Children below. |
RoomType | name, offers[], sold_out, room_size, room_size_sqm, max_guests, bed, view, description, amenities[], images[], tags[], room_id — bed is the OTA's own wording and case (e.g. "1 king bed"); only spacing mechanics are cleaned up centrally, so the same bed can't read as two different strings depending on which OTA sent it. view is one of a fixed vocabulary, identical across every OTA: "Sea view", "City view", "Garden view", "Courtyard view", "Pool view", "Lake view", "Valley view", "Bay view", "Jungle view", "Hill view", "Mountain view", matched on whole phrases only — never a bare noun, so a room called "Suite with Plunge Pool" is not a pool view. Synonyms fold onto one token ("Ocean View" → Sea view, "Swimming Pool View" → Pool view); Hill view and Mountain view deliberately do not fold into each other. null means no view in the vocabulary was named — never a raw upstream string. Read from MakeMyTrip's own structured room-view field first when it states one, and mined from the room name otherwise — the structured field wins even when the room's own marketing name suggests a different view. |
RoomOffer | price, currency, price_text, price_basis, price_before_discount, board, free_cancellation, cancellation_text, refundable, occupancy, occupancy_source ("priced"), max_children, rooms_booked, fits_occupancy, badges[], rate_token, raw_label, supplier_id, rate_channel_id, device_identity, tax_included, tax_status, tax_amount, tax_basis, fee_amount, tax_and_fees, pay_later, payment_text, tax_percentage, meal_plan_code, meal_plan_code_raw, source_url, source_url_scope — the channel/device fields are always null on MakeMyTrip; those are Agoda-only. max_children is a Booking.com-only capacity field, always null here — MakeMyTrip folds children straight into occupancy instead, see Children below. price is always the amount actually payable; price_before_discount is populated when MakeMyTrip states a property discount on the tariff — it carries the pre-discount list price, null on an undiscounted rate. fee_amount is always null on MakeMyTrip — itemization isn't offered here, only the free tax_basis and tax_and_fees fields; see below. rooms_booked, pay_later, payment_text and tax_percentage are always null here too. |
| Field | Meaning on MakeMyTrip |
|---|---|
tax_included | Whether price already contains taxes and fees — restates price_basis as a boolean, so the two can never disagree. |
tax_status | tax_included as a word: included, excluded or unknown — never null. Always excluded on MakeMyTrip, whose basis is fixed. Added 13 October 2026. |
tax_amount | Real on MakeMyTrip (and Goibibo, same parser) — the response states the tax figure directly, per night, matching the per_night-family price_basis both always use. On MakeMyTrip itself it's always taxes and fees combined — tax_required is rejected with 400 here, same as on Booking.com, even though the response states the same components Goibibo's does (see tax_basis below): a live OTA's contract isn't changed as a side effect of what shipped for Goibibo. Goibibo does itemize, and at no extra request, because it shares this same tax line. (On Agoda, tax_amount is null unless the job sent tax_required — and when itemized it's the tax alone, with any extra-occupancy charge reported separately as fee_amount; see Agoda's Taxes and fees. Check which OTA a figure came from before comparing them.) Don't multiply or divide it against a whole-stay total from another OTA without converting first. |
tax_basis | Free on MakeMyTrip, on every offer — no tax_required needed (and none is accepted; see above). tax_and_fees: the tax line's own components include something other than tax (a service charge or fee). tax_only: they don't. unknown: MakeMyTrip itemized a tax but stated components we couldn't read. null: no tax stated at all. Read from the same details[].TAXES.breakup Goibibo's split reads from — it tells you which rows carry a fee even though tax_amount itself stays combined. |
tax_and_fees | Free on MakeMyTrip, on every offer. The all-in surcharge — since MakeMyTrip never splits tax_amount, this is the same figure as tax_amount today, published under its own name for consistency with Agoda and Goibibo, where the two fields do diverge once a rate is itemized. |
meal_plan_code | MakeMyTrip and Goibibo are the two OTAs here that publish their own meal-plan code (Goibibo shares this parser — same backend) — read from the own MP_EP / MP_CP / MP_MAP field, not derived from board the way Agoda's and Booking.com's are. That matters: a rate plan named "Room With Free Cancellation" carries no meal word at all, yet the field on it states MP_EP — deriving from board text alone would have missed it entirely. A rate plan with neither its own meal field nor any meal word in board is inferred as room-only (EP) when a sibling plan on the same room type states a meal — a property that codes meals at all almost certainly meant this plan as room-only too. null only when no plan on that room type establishes a meal plan at all. |
meal_plan_code_raw | MakeMyTrip and Goibibo only. The untouched upstream token (e.g. MP_CP) behind the normalized meal_plan_code, kept for reconciling against the origin's own records. Always null on Agoda, Booking.com and Expedia — none of the three publishes a raw code to keep. |
source_url / source_url_scope | MakeMyTrip only links to the property page — source_url_scope is "property", like Booking.com (Agoda is the one rate-scoped OTA — see its guide). null when no URL was available. Always null on Goibibo — its underlying response carries this exact same deeplink, but as a makemytrip.com URL (Goibibo runs on MakeMyTrip's own backend), so it is never published under the Goibibo result. |
price_basis is a per-offer field with values per_night_before_taxes, per_night, total_stay_before_taxes, total_stay or unknown. MakeMyTrip is always per_night_before_taxes — the same fixed basis Goibibo uses (it shares this parser), and the only two of the five OTAs with a fixed basis. If you're comparing against Booking.com (whole-stay) or Agoda (usually per-night but derived per offer), use the pre-computed cheapest_fitting_price + cheapest_fitting_basis (+ cheapest_fitting_meal_plan_code, added 23 September 2026, so you're not comparing a breakfast rate against a room-only one) to normalize rather than assuming.Children
Send ages, not a count: "children_ages": [4, 7]. MakeMyTrip prices each child by age on the same roomStayCandidates[].childAges field its own site sends, and echoes the ages back onto the matching tariff — so a bare children count is rejected with 400, same as on every other OTA.
When MakeMyTrip can match a tariff on adults and children together, occupancy on that offer counts all guests — a match on 2 adults + 2 children reports occupancy: 4 — the same convention Agoda uses, and unlike Booking.com, which keeps a separate max_children field instead (always null here). Measured live, same hotel/room/dates: 2 adults, no children → ₹6,000; 2 adults + children aged [4, 7] → ₹7,000 — a real price difference, not just an accepted request, and it agrees with the rate plan's own FREE_CHILD inclusion line: the 4-year-old adds nothing, the 7-year-old adds exactly ₹1,000.
children_ages results the same way as any other MakeMyTrip result and check fits_occupancy rather than assuming. When MakeMyTrip answers with bundles instead (below), the same warning applies to them.When no single room type matches the party. Most multi-room searches — and a childful one the property re-splits — get no per-room-type answer from MakeMyTrip at all, only recommended bundles: whole bookings, each a set of rooms with one price for all of them. Since 14 October 2026 those are read instead of failing the job with no rooms: each bundle becomes one offer at its per-room share (the bundle price over rooms, through the same discount and tax handling as an ordinary rate), on a room type named for the rooms in it (e.g. "King Smoking + King Non-Smoking"). MakeMyTrip splits a party however it likes — 1 + 3 adults across two rooms — so a bundle is never read room by room.
occupancy_source: "priced" and can be fits_occupancy: true only when the bundle was priced for exactly the adults, rooms and child ages you sent. MakeMyTrip also re-ages and re-splits children (one room with children aged 4 and 10 has come back as a two-room bundle, and two rooms with the same children as a bundle for children aged 12 and 12): such a bundle is still published, with occupancy unknown and fits_occupancy: null, so it is never served as the price of the party you asked for. Where the rooms of a bundle are on different cancellation terms, cancellation_text carries each wording joined by " / " and free_cancellation / refundable are null.Result outcomes
Submitting can 4xx (validation 400 — including a check-in more than 364 days ahead and a misspelt field name, auth 401, missing scope 403, throttling 429) or, rarely, a 5xx if we couldn't accept the job on our side — retry the submit. Once a job is accepted, polling always returns HTTP 200 — you distinguish scrape outcomes by fields, never by status code:
| Outcome | How to detect |
|---|---|
| Priced | status: "completed", result.error: null, result.room_types populated. |
| Sold out | status: "completed", result.sold_out: true, result.room_types: [], result.error: null — MakeMyTrip distinguishes genuine no-availability from property errors, and this is reported as a real sold-out signal rather than folded into a generic failure. When MakeMyTrip states a reason, result.unavailable_reason carries its own wording (e.g. "Property requires a minimum stay of 2 nights", "Property available for 26 OCTOBER or Later"); it is "" when none was given. A minimum-stay or date-window refusal means the same dates will not price, so read it before retrying. Since 27 October 2026 these refusals complete as sold out; before that they were reported as failed. |
| Sold out, not sold on MakeMyTrip | result.not_bookable: true beside sold_out: true — MakeMyTrip does not sell the property at all (“Try exploring other amazing stays & deals for your trip!”). No date will price there and a retry will not help. Distinct from a plain sold_out, which may price on other dates. |
| Failure | status: "failed" with a human-readable error_message; result.error carries the same string (e.g. "unresolved (no MMT match for name+city)", "proxy quota (407): ...") with result.room_types: []. |
A scrape failure is never a 4xx/5xx on the poll — it is a failed job with an explanation.
MakeMyTrip-specific notes
- Currency:
INRonly — other codes are rejected rather than mislabeled. - Price basis: always
per_night_before_taxes— a fixed basis shared with Goibibo only; every other OTA here derives it per offer. - Distinguishes genuine no-availability (returns
sold_out: true) from property errors, unlike a generic failure. - Amenities:
amenitiesreturns MakeMyTrip's own real facility names for the room (e.g."Wi-Fi","Minibar"), in MakeMyTrip's own display order, deduped and capped at 8 — not a fixed set of category headings.
price is now the discounted price. Before this date, price on a MakeMyTrip row reported the list price (BASE_FARE) even when the property discounted the rate, and price_before_discount was always null. price now reports the amount actually payable (PRICE_AFTER_DISCOUNT) when MakeMyTrip states a discount, and price_before_discount carries the list price on those rows. tax_amount is unaffected — it was already computed off the payable figure. If you store a MakeMyTrip price series, expect a one-time step down on discounted rows around this date; one measured property was 45% off across all 23 rates it offered.truncated.
board/meal_plan_code: a meal that follows a discount is not an included meal."10% discount on a la carte for Lunch and Dinner"used to make every rate carrying it"Half board"/MAP, room-only rates included; it now names no meal.meal_plan_code: a perk MakeMyTrip files under its own Breakfast category outranks a room-only code on the same rate. Where a property codes a suite's "Room Only" planMP_EPbut lists "Breakfast included" among its perks, the offer now readsCP, in agreement withboard.meal_plan_code_rawstill shows"MP_EP".bed: read from the bed line MakeMyTrip displays on the room, with its structured bed list as a fallback. The structured list was wrong on exactly the rooms that matter (a triple room read"1 Single Bed", a Double Queen room"1 Queen Bed", and only the first bed type was read).view: a negated phrase names no view —"Standard Non-Lake Facing Room"isnull, where it used to read"Lake view".truncated: alwaysfalseon MakeMyTrip. It used to be set on every hotel with 20 or more room types, but MakeMyTrip ignores the room limit and returns the full list.
amenities used to return the same 8 fixed category headings ("Popular with Guests", "Basic Facilities", …) on every room rather than the room's actual facilities — it now returns MakeMyTrip's real, room-specific facility list. Separately, meal_plan_code now infers EP (room-only) for a rate plan that states no meal itself when a sibling plan on the same room type does state one — previously such a plan always read null.Next: Expedia →