๐ŸŽ‰ Developer Free Trial โ€” 1 week of full API access, no credit card required. Claim your trial โ†’
Scraper

Hotel Rates โ€” Direct OTA

Return the exact per-room-type price a real guest sees on an OTA's own site โ€” for Agoda, Booking.com and MakeMyTrip โ€” normalized into a single schema so you can compare them field-for-field. Like the other scrapers, this is an async job: submit, receive a job_id, poll for the result.

POST /api/v1/scraper/ota-price/ โ€” submit
GET /api/v1/scraper/ota-price/{job_id}/ โ€” poll

Base URL: https://gumo.co.in ยท Auth: X-API-Key: gsk_โ€ฆ (same key as Google Hotels; the key needs the matching ota:<name> scope) ยท See the Overview.

Direct-route rates. These are the prices the OTA shows on its own site for the property โ€” not metasearch or commission-inflated affiliate prices.

Submit a job

FieldTypeRequiredDefaultNotes
otastringOptionalagodaagoda, booking, makemytrip. Aliases: booking.com โ†’ booking, mmt / makemytrip.com โ†’ makemytrip.
hotel_namestringRequiredโ€”โ‰ค 300 chars. Resolved to the OTA's own property id.
citystringRequiredโ€”โ‰ค 200 chars.
check_indateRequiredโ€”YYYY-MM-DD; not past, โ‰ค 1 year.
check_outdateRequiredโ€”After check_in; โ‰ค 30 nights.
adultsintOptional21โ€“10. Total party size across all rooms, not per room.
roomsintOptional11โ€“8. Must be โ‰ค adults (at least one adult per room).
currencystringOptionalINR3-letter ISO. Support varies by OTA โ€” see per-OTA notes. Unsupported codes are rejected with a 400.
Adults only. This endpoint prices adult occupancy only โ€” sending a children field returns a 400 telling you to remove it (use the Google Hotels scraper for child-inclusive pricing).
POST /api/v1/scraper/ota-price/
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": "agoda",
    "hotel_name": "The Taj Mahal Palace, Mumbai",
    "city": "Mumbai",
    "check_in": "2026-09-15",
    "check_out": "2026-09-17",
    "adults": 2,
    "rooms": 1,
    "currency": "INR"
  }'
202 Accepted
{
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "pending",
  "ota": "agoda",
  "poll_url": "/api/v1/scraper/ota-price/a1b2c3d4-e5f6-7890-abcd-ef1234567890/"
}

Poll for results

Poll the poll_url every ~2 seconds until status is completed or failed. Statuses: pending โ†’ processing โ†’ completed | failed. Google Hotels job IDs 404 here (and OTA job IDs 404 on the Google poll endpoint) โ€” each scraper polls its own jobs.

GET /api/v1/scraper/ota-price/{job_id}/

Response schema

The poll response is a job envelope: id, status, ota, hotel_name, city, check_in, check_out, adults, rooms, currency, result, error_message, created_at, started_at, completed_at, duration_seconds, bandwidth_kb. On completion, result holds the pricing object, which nests HotelRoomPricing โ†’ room_types[] โ†’ offers[], identical across all three OTAs.

200 OK โ€” GET /ota-price/{job_id}/ (result, abridged)
{
  "id": "a1b2c3d4-...", "status": "completed", "error_message": "",
  "result": {
  "ota": "agoda",
  "hotel_name": "The Taj Mahal Palace, Mumbai",
  "hotel_id": "16082488",
  "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": 22500.0, "cheapest_fitting_basis": "per_night_before_taxes",
  "source": "http", "scraped_at": "2026-09-01T12:00:00+00:00",
  "sold_out": false, "error": null, "truncated": false,
  "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": ["free-cancellation", "king-bed"], "room_id": "991243155",
      "room_size_sqm": 45.0, "max_guests": 3,
      "offers": [
        {
          "price": 22500.0, "currency": "INR", "price_text": "โ‚น 22,500",
          "price_basis": "per_night_before_taxes", "price_before_discount": null,
          "board": "Breakfast included", "free_cancellation": true,
          "cancellation_text": "Cancel for free before Sep 13, 2026",
          "refundable": true, "occupancy": 2,
          "occupancy_source": "priced", "fits_occupancy": true,
          "badges": ["Lowest price available!"], "rate_token": "8b:0;c:2D1N_100P;..."
        }
      ]
    }
  ]
  }
}

Object fields

ObjectFields
HotelRoomPricingota, hotel_name, hotel_id, currency, check_in, check_out, room_types[], adults, rooms, nights, adults_per_room, price_scope ("per_room"), occupancy_honored, cheapest_fitting_price, cheapest_fitting_basis, source ("http"|"browser"), scraped_at, sold_out, error, truncated
RoomTypename, offers[], sold_out, room_size, room_size_sqm, max_guests, bed, view, description, amenities[], images[], tags[], room_id
RoomOfferprice, currency, price_text, price_basis, price_before_discount, board, free_cancellation, cancellation_text, refundable, occupancy, occupancy_source ("priced"|"capacity"), fits_occupancy, badges[], rate_token, raw_label
Compare on price_basis, not raw price. 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; Agoda is usually per_night_before_taxes (derived from Agoda's own price label); Booking.com returns total_stay or total_stay_before_taxes. Always read price_basis before comparing across OTAs โ€” or use the pre-computed cheapest_fitting_price + cheapest_fitting_basis.

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:

OutcomeHow to detect
Pricedstatus: "completed", result.error: null, result.room_types populated.
Sold outstatus: "completed", result.sold_out: true, result.room_types: [], result.error: null โ€” resolved but no availability.
Failurestatus: "failed" with a human-readable error_message; result.error carries the same string (e.g. "unresolved (no 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.

Agoda

Set "ota": "agoda". Resolved and fetched over pure HTTP. Requires the ota:agoda key scope.

  • Currencies: INR, USD, GBP, EUR, AED.
  • Price basis: usually per_night_before_taxes, derived per offer from Agoda's own price label โ€” always read the field.
  • Full room grid with room size, bed, view, amenities and per-offer cancellation terms.

Booking.com

Set "ota": "booking" (alias booking.com). Requires the ota:booking key scope.

  • Currencies: INR, USD, GBP, EUR, AED.
  • Price basis: total_stay or total_stay_before_taxes (depending on whether Booking shows a taxes-and-charges line) โ€” the total for the whole stay, not per-night. Normalize before comparing with Agoda/MMT.
Availability: Booking.com is gated twice while headless-session provisioning is finalized. If your key lacks the ota:booking scope you get 403 {"detail": "This API key does not have the 'ota:booking' scope."}; if the scraper is switched off platform-wide you get 400 {"error": "ota 'booking' is not currently enabled"}. Contact us to enable it.

MakeMyTrip (MMT)

Set "ota": "makemytrip" (aliases mmt, makemytrip.com). Requires the ota:makemytrip key scope.

  • Currency: INR only โ€” other codes are rejected rather than mislabeled.
  • Price basis: per_night_before_taxes.
  • Distinguishes genuine no-availability (returns sold_out: true) from property errors.

Back to: Overview ยท Google Hotels ยท Google Flights