API Overview
ScrapeGuys exposes real-time travel pricing through a small set of REST endpoints. This page covers the concepts shared by every scraper — authentication, base URLs, the async job workflow, rate limits and errors. Each scraper then has its own reference page.
Flights
Google Flights + MakeMyTrip, each its own standalone guide. Structured, priced offers. Async jobs.
Hotels
Google Hotels, Agoda, Booking.com, MakeMyTrip, Expedia, Goibibo, Destination Search and Rate Shop — eight standalone guides. Async jobs.
Base URLs
The scrapers are served by two independent API surfaces. Each has its own API keys (see Authentication).
| Surface | Base URL | Covers |
|---|---|---|
| Hotel API | https://gumo.co.in/api/v1/scraper | Meta (Google Hotels) + OTA (Agoda, Booking.com, MakeMyTrip, Expedia, Goibibo) |
| Flight API | https://gumo.co.in/api/v1/flight-scraper | Meta (Google Flights) + OTA (MakeMyTrip) |
gumo.co.in with the host provided to you during onboarding if it differs. All examples on these docs use the host above.Authentication
Every request must include your API key in the X-API-Key header. Keys are 68-character strings; the Hotel API issues gsk_… keys and the Flight API issues fsk_… keys. The two are independent — a Hotel key does not authenticate the Flight API and vice-versa.
Scopes. Both key types carry an explicit scope list controlling which sources they may call. A gsk_… key can hold: gmaps (Google Hotels), ota:agoda (Agoda), ota:booking (Booking.com), ota:makemytrip (MakeMyTrip), ota:expedia (Expedia), ota:goibibo (Goibibo), and destination:booking (Destination Search). A fsk_… key can hold: google_flights (Google Flights) and/or makemytrip (MakeMyTrip Flights, both its app and desktop platforms), per source. Which of these a given key actually carries depends on when it was issued and what was requested for it — see below. Calling an endpoint or source outside your key's scopes returns 403 with {"detail": "This API key does not have the '<scope>' scope."}. There is no wildcard scope — a source added later is never automatically granted to existing keys.
destination:booking is not on any key yet, including full-access ones. It's a separate scope from ota:booking, not implied by it — pricing one hotel and enumerating an entire city are different products with unit costs orders of magnitude apart. Since it post-dates every key issued so far, it is never granted implicitly by the "existing keys keep working" migration above; ask your Gumo contact to add it explicitly before your first call to Destination Search.ota:goibibo is our newest scope and ships gated per environment. The same "existing keys keep working" migration does not reach it — it post-dates every key issued so far, same as destination:booking above. Ask your Gumo contact to add it, and see the Goibibo guide for the separate platform-wide gate that can still return 400 even once your key holds the scope.X-API-Key: gsk_a1b2c3d4e5f6... # Hotel API (Google Hotels + OTA rates)
X-API-Key: fsk_a1b2c3d4e5f6... # Flight API (Google Flights)
- 401 Unauthorized — missing, invalid, or deactivated key.
- 403 Forbidden — key is valid but lacks the required scope.
Request a free developer trial to get your keys.
The job workflow
Every scraping endpoint is asynchronous. You submit a job, receive a job_id and a poll_url, then poll until the job reaches a terminal state:
- Submit —
POST …/scrape/(orPOST …/ota-price/) returns202with{ job_id, status: "pending", poll_url }. - Poll —
GETthepoll_urlevery ~2 seconds.statusmovespending → processing → completed(orfailed). - Read — when
statusiscompleted, theresultobject holds the data.
/scrape/{job_id}/, Hotels → OTA jobs at /ota-price/{job_id}/, Hotels → Destination Search jobs at /destination-search/{job_id}/, Flights jobs (either source) at /flight-scraper/scrape/{job_id}/. Polling a job on the wrong endpoint returns 404.Rate limits & quotas
Limits are enforced per API client and are configurable per plan. Defaults:
| Limit | Default | Window |
|---|---|---|
| Rate limit | 10 requests | per minute |
| Daily quota | 1,000 requests | per day (UTC) |
| Monthly quota | 20,000 requests | per billing cycle |
"Per billing cycle" is not always the calendar month: each client has a billing_cycle_day (1–28) the monthly quota resets on. New clients are anchored to their sign-up day; clients provisioned before billing cycles existed run on day 1 — the calendar month, unchanged. GET /usage/'s billing_period block reports your account's actual window.
Exceeding any limit returns HTTP 429 with a descriptive detail, e.g. "Rate limit exceeded (10/min). Try again shortly."
hotels × otas × dates. A 10-hotel, 3-OTA, 30-date shop is 900 units. The whole matrix is priced against your quota before any work starts and refused as a unit if it won't fit, with the arithmetic in the error — never billed for a partial matrix./usage/ are never counted.Errors
| Status | Meaning |
|---|---|
400 | Invalid input — validation error, with a per-field detail. |
401 | Missing, invalid, or deactivated API key. |
403 | Key is valid but lacks the scope for the requested source/scraper. |
404 | Job not found (or not owned by your key). |
429 | Rate limit or quota exceeded. |
500 | Unexpected server error. |
200: a job ends completed with an empty result (e.g. no_results, sold_out: true) or failed with a human-readable error_message. Read each scraper's page for specifics.Usage endpoint
Check your consumption against your quotas at any time. Available on both surfaces:
# Hotel API (Google Hotels + OTA rates)
curl "https://gumo.co.in/api/v1/scraper/usage/?days=30" \
-H "X-API-Key: gsk_your_api_key"
# Flight API (Google Flights)
curl "https://gumo.co.in/api/v1/flight-scraper/usage/?days=30" \
-H "X-API-Key: fsk_your_api_key"
{
"client": "Your Company Name",
"billing_period": { "cycle_day": 1, "start": "2026-04-01", "end": "2026-04-30", "resets_on": "2026-05-01" },
"lifetime": { "total_requests": 12450, "total_bandwidth_kb": 3245678.5 },
"limits": { "rate_limit_per_minute": 10, "daily_quota": 1000, "monthly_quota": 20000 },
"usage": [
{ "date": "2026-04-08", "request_count": 142, "success_count": 138,
"failure_count": 4, "total_duration_seconds": 856.3, "total_bandwidth_kb": 36890.2 }
]
}
days is clamped into the 1–90 range (out-of-range values are adjusted, not rejected); a non-integer returns 400 (days must be an integer (1–90))
billing_period is the window "monthly" means for your account — the monthly quota is summed over your billing cycle, which starts on cycle_day of each month and resets on resets_on, the date your monthly quota springs back to the full amount. start and end are both inclusive. Identical shape on both APIs, so a customer of both reads one contract.
lifetime.total_bandwidth_kb, per-day total_bandwidth_kb) exist on the Hotel API only. The Flight API usage response reports requests, successes, failures and durations — no bandwidth accounting. Bandwidth is the measured, compressed traffic the scrape moved through our proxies (response headers excluded); failed scrapes count what they spent before failing.Client dashboard
A browser dashboard of your own scraping activity — submitted / succeeded / failed / in-flight counts as KPI cards, a time-series chart, and a PDF export. Sign in with the portal username/email + password you were provisioned (ask your Gumo contact), or with any of your active Hotel or Flight API keys. Either way the dashboard is scoped server-side to your data only, and access ends the moment your credentials are revoked on our side.
GET /usage/ above, so the two can disagree if you use Destination Search.For programmatic access, the same stats are available as JSON or PDF using just your API key — no sign-in needed:
curl "https://gumo.co.in/client/scrape-dashboard/data/?range=7d" \
-H "X-API-Key: gsk_your_api_key" # or an fsk_ key
| Param | Optional | Description |
|---|---|---|
range | yes | Quick window ending now, e.g. 10m, 30m, 7d. Wins over start/end when present. |
start, end | yes | YYYY-MM-DD or full ISO 8601. Ignored if range is set. |
granularity | yes | minute | hour | day | month. Auto-picked from the window size when omitted (minute ≤3h, hour ≤7d, day ≤90d, month beyond). |
With nothing supplied the window defaults to the last 7 days. Unrecognized values are ignored (not rejected) and reported back in a warnings array; the request still succeeds with the default window applied.
{
"series": [
{ "bucket": "2026-08-06T00:00:00+05:30", "label": "2026-08-06 00:00",
"submitted": 42, "succeeded": 39, "failed": 2, "in_flight": 1 }
],
"per_client": [
{ "client_id": 123, "client_name": "Your Company Name",
"submitted": 312, "succeeded": 298, "failed": 14,
"in_flight": 0, "success_rate": 95.5 }
],
"totals": { "submitted": 312, "succeeded": 298, "failed": 14, "in_flight": 0, "success_rate": 95.5 },
"meta": { "granularity": "hour", "start": "2026-08-06T00:00:00+05:30",
"end": "2026-08-13T00:00:00+05:30", "client_id": 123, "range_key": "7d" }
}
per_client contains at most one row — your own — the dashboard endpoints never return another client's name, id or volume. bucket timestamps are UTC-offset ISO 8601; label is the same instant formatted for display in Asia/Kolkata.
/client/scrape-dashboard/pdf/ takes the same query params and streams a PDF (Content-Type: application/pdf) instead of JSON.
/usage/, the dashboard page, JSON and PDF endpoints are never counted against your rate limit or quota.Ready to dive in? Pick a guide: Google Flights · MakeMyTrip Flights · Google Hotels · Agoda · Booking.com · MakeMyTrip Hotels · Expedia · Goibibo · Destination Search · Rate Shop.