Goibibo
Return the exact per-room-type price a real guest sees in Goibibo's own app — not a metasearch or commission-inflated affiliate price. Goibibo runs on MakeMyTrip's own backend under a different brand, so this source is resolved and fetched the same way the official Goibibo Android app does. Submit a job, receive a job_id, poll for the result.
/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:goibibo scope · See the Overview.
/api/v1/scraper/ota-price/ also prices Agoda, Booking.com, MakeMyTrip and Expedia — set "ota": "goibibo" (alias goibibo.com) to get this vendor; the endpoint defaults to Agoda when ota is omitted. The envelope shape is the same across all five; this page documents everything Goibibo-specific (one room per request, no children yet, Android-only). Not what you're after? Rate Shop does not offer Goibibo — see the note below.ota:goibibo scope you get 403 {"detail": "This API key does not have the 'ota:goibibo' scope."}; on an environment where the Goibibo scraper is switched off you get 400 {"error": "ota 'goibibo' is not currently enabled"} before any job is created. Contact us to enable it.Submit a job
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
ota | string | Required | — | Set to "goibibo" explicitly (alias goibibo.com) — this endpoint defaults to Agoda when ota is omitted. A numeric channel code ("5" or bare 5) may be sent instead — added 21 September 2026, see Addressing an OTA by number. The code is accepted on this endpoint only — Rate Shop holds Goibibo out under its number exactly as under its name. |
hotel_name | string | Conditional | — | ≤ 300 chars. Resolved via Goibibo's own app autosuggest, into the same hotelId space MakeMyTrip uses (one backend, two brands). 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 also works across Agoda, Booking.com and MakeMyTrip. Bridged to Goibibo's own id on first use, then pinned — when Goibibo 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 | "" | Goibibo's own numeric hotelId — the SAME id as MakeMyTrip's (e.g. "201709071828297454"), because Goibibo prices on MakeMyTrip's backend. Skips resolution entirely. The id is validated against "ota": "goibibo" at submit. See Identifying the hotel. |
check_in | date | Required | — | YYYY-MM-DD; not past, ≤ 1 year. |
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 is accepted at submit like every other OTA here, but Goibibo only prices one room per request today — see the callout below for what actually happens when you ask for more. |
currency | string | Optional | INR | INR only — same constraint as MakeMyTrip, the same backend. Other codes are rejected with 400 ("Unsupported for goibibo. Supported: INR.") before any job is created. |
device_type | string | Optional | desktop | desktop or mobile: accepted and echoed, but has no effect on Goibibo — every scrape is the Android app route regardless of what you send. app_android is rejected with a 400 (there is no separate route to select). See Device pricing below. |
rooms itself is validated the same 1–8 range as every other OTA here, so a value > 1 is not rejected at submit — the job is created, then completes as status: "failed" with result.error: "goibibo prices one room per request today (asked for N): a multi-room party comes back as a re-split combo, not the requested rooms". Keep rooms: 1 until multi-room pricing is measured and lifted.children_ages with "ota": "goibibo" is rejected with 400 before any job is created — for the same reason as the multi-room case above: a childful party doesn't come back in the normal room grid either, and one measured run even had the app rewrite the requested ages on the combo it substituted. Supported today: Agoda, Booking.com, Expedia and MakeMyTrip.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, exactly as on MakeMyTrip — unsurprising, since the two share a backend.
By name (hotel_name + city) is the default — resolved through Goibibo's own app autosuggest and picked by us. It works without you knowing anything about Goibibo, and is what every existing integration uses.
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. The first request for a place still goes through Goibibo'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 Goibibo 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 Goibibo's own id (ota_hotel_id, its numeric hotelId, e.g. "201709071828297454") is the fastest and most direct route once you hold it. The whole resolution step disappears, and the answer cannot drift to a different hotel between runs. It is the same id MakeMyTrip uses — if you already have a portfolio of MakeMyTrip hotelIds, they work here unchanged. 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 Goibibo'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 — the origin's own name for the property it priced, sourced from the same deeplink field MakeMyTrip's parser always reads. That makes it a genuine statement from the origin, not an echo of your text, even though (see Response schema below) the URL it's read from is never published as source_url on a Goibibo result. result.hotel_name cannot answer this question at all — it is your own search text echoed back, not a confirmation signal.
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.Device pricing — device_type
device_type is accepted and validated on Goibibo — desktop or mobile are accepted; app_android, the app route Agoda and Booking.com offer, and anything else 400 — and echoed back on the job envelope, but it has no effect on which prices you get.
device_type; there is no separate desktop or iOS code path to switch to. (Contrast with Expedia, which is the same situation but on iOS/Android-flavored APP_PHONE rather than a device-specific build — and with MakeMyTrip, which is genuinely device-flat rather than device-locked.)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 — the same shape as every other OTA on this endpoint (children_ages is always [] here, since Goibibo rejects a non-empty one at submit; tax_required / tax_scope echo what you submitted). On completion, result holds the pricing object, which nests HotelRoomPricing → room_types[] → offers[].
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; 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) — 5 here, present on failed jobs too; added 21 September 2026, before which Goibibo had none and this was always null. children_ages is always [] and children_per_room always 0 — Goibibo does not price children yet (see the callout under Submit a job). |
RoomType | name, offers[], sold_out, room_size, room_size_sqm, max_guests, bed, view, description, amenities[], images[], tags[], room_id — tags[] is always empty (Agoda-only). bed is the OTA's own wording and case; 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. Same precedence as MakeMyTrip (shares this parser): the origin's own structured view field wins over the room name when both are present. |
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; those are Agoda-only. max_children is a Booking.com-only field, and rooms_booked, pay_later and payment_text are Agoda- or Booking.com-only; all always null here. price_before_discount carries the pre-discount list price when Goibibo states a discount, as on MakeMyTrip, and is null otherwise. tax_percentage is always null on Goibibo, itemized or not — this backend states amounts, never a rate. tax_basis, fee_amount and tax_and_fees are populated free — see Taxes and fees below. |
source_url and source_url_scope are always null on Goibibo — deliberately, not for lack of a URL. The underlying response carries the same deeplink field MakeMyTrip's parser reads, but on Goibibo that URL points at makemytrip.com — Goibibo runs on MakeMyTrip's own backend — so publishing it under a Goibibo result would send you to a different OTA's page. result.origin_hotel_name is still read from that same field and is still genuine (see Identifying the hotel); only the URL itself is withheld.| Field | Meaning on Goibibo |
|---|---|
tax_included | Whether price already contains taxes and fees — restates price_basis as a boolean. |
tax_status | tax_included as a word: included, excluded or unknown — never null. Always excluded on Goibibo, whose basis is fixed. Added 13 October 2026. |
tax_amount | Real — read the same way as on MakeMyTrip (shared parser): the response states the tax figure directly, per night. On an ordinary job it's taxes and fees combined, same as MakeMyTrip and Booking.com. Unlike either of those, Goibibo's tax line states its own components, so a tax_required job splits it into tax alone here — at no extra request. See Taxes and fees below. |
meal_plan_code | Read from Goibibo's own meal-plan field (the same MP_EP / MP_CP / MP_MAP contract MakeMyTrip publishes), not derived from board. null only when the origin states no meal plan at all. |
meal_plan_code_raw | The untouched upstream token behind the normalized meal_plan_code. Shared with MakeMyTrip; null on Agoda, Booking.com and Expedia. |
price_basis is a per-offer field. Goibibo is always per_night_before_taxes — the same fixed basis MakeMyTrip uses, and unlike 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 across OTAs rather than assuming.Taxes and fees — the split costs nothing here
Goibibo's room grid states its own tax line's components, so tax_amount — taxes and fees combined, the same shared contract every OTA here publishes — can be split into tax alone and the fee beside it without a second request. Agoda pays one booking-form request per rate for the same answer and Expedia two (a prepare-checkout call, then the checkout page); an itemized Goibibo job issues exactly as many requests as an ordinary one, and is one unit of quota like any other job.
| Field | Question it answers | Cost |
|---|---|---|
tax_and_fees | What does this rate cost on top of price? | free, on every offer |
tax_amount + fee_amount | How much of that is tax, and how much is a fee? | free — opt in with tax_required, no extra request |
Ask for the split the same way as on Agoda:
tax_scope | What gets itemized | Extra requests |
|---|---|---|
one_fare_per_room | The cheapest fare of each room type that can seat the party | None |
all_fares_all_rooms | Every fare of every room type | None |
Measured 20 Sep 2026 over 306 live rates across 10 properties x 2 stay windows: 82 (27%) carry a fee inside tax_amount — a median 46% of the figure, up to 58% — e.g. a 2,333.88 combined figure that was 1,034.00 Hotel GST beside 765.00 hotelier service charge and 534.88 Goibibo service fee. Those are exactly the rows where tax_amount / price reads noticeably higher than the rest, which is why publishing the combined figure with nothing saying so was a real gap.
| Field | Meaning |
|---|---|
tax_amount | Combined tax + fee on an ordinary job — unchanged. With tax_required, the tax alone, with the fee moved to fee_amount. |
fee_amount | With tax_required only. The hotelier's service charge plus Goibibo's own service fee, read straight from the tax line's own components. 0.0 where the whole figure is tax; null on every non-itemized rate. |
tax_and_fees | Free, on every offer. The combined figure, held steady even after tax_required splits tax_amount — so price + tax_and_fees is the all-in whether or not a rate was itemized. |
tax_basis | Free, on every offer. tax_and_fees — the figure has a fee inside it. tax_only — it does not. unknown — Goibibo itemized a tax but stated components we couldn't read. null — no tax stated at all. |
tax_amount and reports tax_basis: "unknown" — it never goes null the way an unresolved Agoda itemization does, because the combined figure was already there before you asked.tax_percentage stays null on Goibibo, itemized or not — this backend states amounts, never a rate. tax_scope sent without tax_required: true is still a 400, same rule as every other OTA on this endpoint.
MakeMyTrip shares this parser and now publishes the same free tax_basis — but its tax_amount stays combined, and tax_required is still a 400 there. Booking.com has no components to read at all on its default route, so tax_required is a 400 there too — but its app_android route does itemize, free, from the app's own room list.
Result outcomes
Submitting can 4xx (validation 400, 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. |
| Failure | status: "failed" with a human-readable error_message; result.error carries the same string, with result.room_types: []. Goibibo-specific strings include "unresolved (no Goibibo match for name+city)", "proxy quota (407): ...", "goibibo property unavailable (code=...): ..." (a delisted or invalid hotel id), and "goibibo prices one room per request today (asked for N): ..." (see the rooms callout under Submit a job). |
A scrape failure is never a 4xx/5xx on the poll — it is a failed job with an explanation.
Goibibo-specific notes
- Gated while it soaks: it needs the
ota:goibiboscope and an environment where the scraper is switched on — contact us to enable it on your key. - One room per request. A value
> 1is accepted at submit but fails the job, not rejected outright — see the callout under Submit a job. - No children yet.
children_agesis rejected with400at submit. - Currency:
INRonly. - Device: ignored — every scrape is the Android app route, regardless of
device_type. - Price basis: always
per_night_before_taxes, shared with MakeMyTrip. - Tax split is free.
tax_requiredsplitstax_amountinto tax + fee at no extra request — see Taxes and fees. - Identity: shares MakeMyTrip's numeric
hotelIdspace — an id you already hold for MakeMyTrip works here unchanged. - Numeric channel code
5, added 21 September 2026 — see Addressing an OTA by number. Accepted on/ota-price/only; a code does not imply Rate Shop offers this OTA. - Not offered on Rate Shop.
/ota-price/only, until it has proven itself on single-property jobs, where one failure costs one job rather than a whole shop — held out under its name and its numeric code alike. source_url/source_url_scopeare alwaysnull— see the callout under Response schema.
Next: Destination Search →