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.
Base URL: https://gumo.co.in · 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."}; if the scraper is switched off platform-wide — the default today — 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. Goibibo has no numeric channel code — see Addressing an OTA by number — and stays addressable by name only. |
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, so the same place_id returns the same property on every later request. 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 | Accepted and echoed, but has no effect on Goibibo — every scrape is the Android app route regardless of what you send. 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.curl -X POST https://gumo.co.in/api/v1/scraper/ota-price/ \
-H "X-API-Key: gsk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"ota": "goibibo",
"hotel_name": "The Taj Mahal Palace, Mumbai",
"city": "Mumbai",
"check_in": "2026-09-15",
"check_out": "2026-09-17",
"adults": 2,
"rooms": 1
}'
{
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "pending",
"ota": "goibibo",
"ota_hotel_id": "", "place_id": "",
"poll_url": "/api/v1/scraper/ota-price/a1b2c3d4-e5f6-7890-abcd-ef1234567890/"
}
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. 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 is validated against "ota": "goibibo" at submit, so an id from a different OTA is rejected with 400 before any job is created or billed.
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 — only desktop or mobile are accepted, anything else 400s — 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.
curl https://gumo.co.in/api/v1/scraper/ota-price/YOUR_JOB_ID/ \
-H "X-API-Key: gsk_your_api_key"
# Repeat every ~2 s until status is "completed" or "failed"
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, 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). On completion, result holds the pricing object, which nests HotelRoomPricing → room_types[] → offers[].
{
"id": "a1b2c3d4-...", "status": "completed", "error_message": "",
"result": {
"ota": "goibibo",
"hotel_name": "The Taj Mahal Palace, Mumbai",
"origin_hotel_name": "The Taj Mahal Palace, Mumbai",
"hotel_id": "200601010111111111",
"currency": "INR",
"check_in": "2026-09-15", "check_out": "2026-09-17",
"adults": 2, "rooms": 1,
"nights": 2, "adults_per_room": 2, "price_scope": "per_room",
"occupancy_honored": true,
"cheapest_fitting_price": 21800.0, "cheapest_fitting_basis": "per_night_before_taxes",
"source": "http", "scraped_at": "2026-09-14T12:00:00+00:00",
"sold_out": false, "error": null, "truncated": false,
"suppliers": [], "rate_channels": [],
"room_types": [
{
"name": "Luxury Room Palace Wing", "sold_out": false,
"room_size": "45 m²", "bed": "1 king bed", "view": "City view",
"amenities": ["Free WiFi", "Parking"], "images": ["https://..."],
"tags": [], "room_id": "991243155",
"room_size_sqm": 45.0, "max_guests": 3,
"offers": [
{
"price": 21800.0, "currency": "INR", "price_text": "₹ 21,800",
"price_basis": "per_night_before_taxes", "price_before_discount": null,
"board": "Breakfast included", "free_cancellation": true,
"cancellation_text": "Free cancellation until Sep 13",
"refundable": true, "occupancy": 2,
"occupancy_source": "priced", "fits_occupancy": true,
"badges": [], "rate_token": null,
"supplier_id": null, "rate_channel_id": null, "device_identity": null,
"tax_included": true, "tax_amount": 3924.0,
"meal_plan_code": "CP", "meal_plan_code_raw": "MP_CP",
"source_url": null, "source_url_scope": null
}
]
}
]
}
}
Object fields
| Object | Fields |
|---|---|
HotelRoomPricing | ota, 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, source ("http"), scraped_at, sold_out, error, truncated, suppliers[], rate_channels[] — suppliers[]/rate_channels[] are always empty; those are Agoda-only channel fields. children_ages / children_per_room are always [] — 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). |
RoomOffer | price, currency, price_text, price_basis, price_before_discount, board, free_cancellation, cancellation_text, refundable, occupancy, occupancy_source ("priced"), max_children, fits_occupancy, badges[], rate_token, raw_label, supplier_id, rate_channel_id, device_identity, tax_included, tax_amount, 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, always null here. price_before_discount is always null — no usable strikethrough on this route. |
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_amount | Real — read the same way as on MakeMyTrip (shared parser): the response states the tax figure directly, per night. There is no itemized split to ask for — tax_required is rejected with 400 on Goibibo, same as on MakeMyTrip and Booking.com. |
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 to normalize across OTAs rather than assuming.Result outcomes
Submitting can 4xx (validation 400, auth 401, missing scope 403, throttling 429). 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
- Ships disabled by default while it completes its first production soak — contact us to enable it on your environment and 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. - Identity: shares MakeMyTrip's numeric
hotelIdspace — an id you already hold for MakeMyTrip works here unchanged. - 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. source_url/source_url_scopeare alwaysnull— see the callout under Response schema.
Next: Destination Search →