Skip to content
Cross-source

OTA compare

Fan out across every source given an identifier.

POST/v1/ota/compare
20 credits

Fail-soft per source: this returns 200 with per-source status even when one OTA is blocked, because losing three good answers to one bad one is worse than a partial result the caller can see. comparison.comparable is false for a single priced source or mixed currencies; mixed-currency minima and spreads are not calculated.

Request

POSThttps://api.scrapercompany.com/v1/ota/compare

Authenticate with your API key in the x-api-key header (see Authentication).

Body

JSON object. Unknown fields are rejected with 422.

  • check_instring (date)required

    First night of the stay, YYYY-MM-DD.

  • check_outstring (date) | null

    Departure date. An alternative to nights — if both are given, this wins.

  • nightsintegerdefault 1min 1, max 30

    Length of stay. Ignored when check_out is supplied.

  • adultsintegerdefault 2min 1, max 8

    Adults in the room. Two is the hotel default; pass one for a hostel, which is priced per bed. Any value from 1 to 8 is accepted.

  • currencystringdefault USD

    ISO 4217 code for the returned prices. Major currencies include AED, AUD, BRL, CAD, CHF, CNY, EUR, GBP, HKD, INR, JPY, KRW, MXN, NZD, SGD, USD; all 39 supported codes are listed in the enum.

    39 allowed values

    AED, ARS, AUD, BRL, CAD, CHF, CLP, CNY, COP, CZK, DKK, EGP, EUR, GBP, HKD, HUF, IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, NZD, PEN, PHP, PLN, QAR, RON, SAR, SEK, SGD, THB, TRY, TWD, USD, VND, ZAR

  • booking_pagenamestring | null

    Booking slug. Omit to skip Booking entirely.

  • booking_countrystringdefault us

    Two-letter segment before the slug in the Booking URL.

    251 allowed values

    ad, ae, af, ag, ai, al, am, ao, aq, ar, as, at, au, aw, ax, az, ba, bb, bd, be, bf, bg, bh, bi, bj, bl, bm, bn, bo, bq, br, bs, bt, bv, bw, by, bz, ca, cc, cd, cf, cg, ch, ci, ck, cl, cm, cn, co, cr, cu, cv, cw, cx, cy, cz, de, dj, dk, dm, do, dz, ec, ee, eg, eh, er, es, et, fi, fj, fk, fm, fo, fr, ga, gb, gd, ge, gf, gg, gh, gi, gl, gm, gn, gp, gq, gr, gs, gt, gu, gw, gy, hk, hm, hn, hr, ht, hu, id, ie, il, im, in, io, iq, ir, is, it, je, jm, jo, jp, ke, kg, kh, ki, km, kn, kp, kr, kw, ky, kz, la, lb, lc, li, lk, lr, ls, lt, lu, lv, ly, ma, mc, md, me, mf, mg, mh, mk, ml, mm, mn, mo, mp, mq, mr, ms, mt, mu, mv, mw, mx, my, mz, na, nc, ne, nf, ng, ni, nl, no, np, nr, nu, nz, om, pa, pe, pf, pg, ph, pk, pl, pm, pn, pr, ps, pt, pw, py, qa, re, ro, rs, ru, rw, sa, sb, sc, sd, se, sg, sh, si, sj, sk, sl, sm, sn, so, sr, ss, st, sv, sx, sy, sz, tc, td, tf, tg, th, tj, tk, tl, tm, tn, to, tr, tt, tv, tw, tz, ua, ug, uk, um, us, uy, uz, va, vc, ve, vg, vi, vn, vu, wf, ws, xk, ye, yt, za, zm, zw

  • hotels_property_idstring | null

    Hotels.com numeric id. Omit to skip Hotels.com.

  • hotels_marketstringdefault US

    Hotels.com point-of-sale; also picks its currency.

    One ofAUCADEEUFRGBIEITNLNZUS

  • agoda_property_idstring | null

    Agoda numeric id. Omit to skip Agoda.

  • expedia_property_idstring | null

    Expedia numeric id from POST /v1/ota/expedia/search. Omit to skip Expedia.

  • expedia_marketstringdefault US

    Expedia point-of-sale; also picks its currency.

    One ofCAUS

Example request

curl -X POST "https://api.scrapercompany.com/v1/ota/compare" \
  -H "x-api-key: $SCRAPERCOMPANY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "check_in": "2026-11-30",
    "nights": 1,
    "currency": "USD",
    "booking_pagename": "moxy-boston-downtown",
    "booking_country": "us",
    "hotels_property_id": "38766175",
    "hotels_market": "US",
    "agoda_property_id": "8795952",
    "expedia_property_id": "38766175",
    "expedia_market": "US"
  }'

Response

200 — One night per source. Fail-soft: a blocked OTA is a row with a status. Metered responses carry x-credits-charged and x-credits-remaining headers (see credit headers).

200 response
{
  "search_parameters": {
    "engine": "ota_compare",
    "check_in_date": "2029-02-05",
    "check_out_date": "2029-02-06",
    "adults": 2,
    "currency": "USD",
    "booking_pagename": "moxy-boston-downtown",
    "agoda_property_id": "8795952"
  },
  "comparison": {
    "status": "comparable",
    "comparable": true,
    "identifiers": {
      "booking": "moxy-boston-downtown",
      "agoda": "8795952"
    },
    "lowest_price": 285,
    "lowest_source": "booking",
    "spread": 47,
    "prices": {
      "booking": 285,
      "agoda": 332
    },
    "currencies": {
      "booking": "USD",
      "agoda": "USD"
    },
    "price_provenance": {},
    "reason": "Sources price on different bases (booking_display_average, total_including_taxes_and_fees); the headline minimum mixes them. Compare within `by_price_basis`.",
    "price_basis_consistent": false,
    "by_price_basis": {
      "booking_display_average": {
        "sources": [
          "booking"
        ],
        "currencies": [
          "USD"
        ],
        "lowest_source": "booking",
        "lowest_price_per_night": 285
      },
      "total_including_taxes_and_fees": {
        "sources": [
          "agoda"
        ],
        "currencies": [
          "USD"
        ],
        "lowest_source": "agoda",
        "lowest_price_per_night": 332
      }
    }
  },
  "sources": {
    "booking": {
      "status": "ok",
      "price_per_night": 285,
      "currency": "USD",
      "price_basis": "booking_display_average"
    },
    "agoda": {
      "status": "ok",
      "price_per_night": 332,
      "currency": "USD",
      "price_basis": "total_including_taxes_and_fees"
    }
  },
  "meta": {
    "requested": 2,
    "ok": 2,
    "elapsed_s": 15.89
  }
}

Response fields

Fields marked required are always present; others appear when they apply.

  • search_parametersobjectrequired
    Show 9 child fields
    • search_parameters.enginestringrequired

      One ofota_compare

    • search_parameters.check_in_datestring (date)required
    • search_parameters.check_out_datestring (date)required
    • search_parameters.adultsintegerrequired
    • search_parameters.currencystringrequired

      Requested currency; used by Booking.com and Agoda. Hotels.com and Expedia price in their market's currency.

    • search_parameters.booking_pagenamestring

      Present only when supplied.

    • search_parameters.hotels_property_idstring

      Present only when supplied.

    • search_parameters.agoda_property_idstring

      Present only when supplied.

    • search_parameters.expedia_property_idstring

      Present only when supplied.

  • comparisonobjectrequired
    Show 12 child fields
    • comparison.statusstringrequired

      One ofno_pricessingle_sourcemixed_currencycomparable

    • comparison.comparablebooleanrequired

      True when at least two sources priced in one currency.

    • comparison.reasonstring | nullrequired

      Why the prices are not (fully) comparable. Null only when status is comparable and every price shares one basis; when bases differ it says the headline minimum mixes them.

    • comparison.identifiersobjectrequired

      Echo of the identifiers used, keyed booking / hotels / agoda / expedia (supplied ones only).

    • comparison.lowest_pricenumber | nullrequired

      Null when nothing priced or currencies are mixed. May span different price bases; see price_basis_consistent.

    • comparison.lowest_sourcestring | nullrequired
    • comparison.spreadnumber | nullrequired

      Highest minus lowest price per night; non-null only when comparable is true.

    • comparison.pricesobjectrequired

      Source -> price per night (priced sources only).

    • comparison.currenciesobjectrequired

      Source -> upper-case currency code (priced sources only).

    • comparison.price_basis_consistentbooleanrequired

      True when every priced source shares one price basis (also true when at most one priced).

    • comparison.by_price_basisobjectrequired

      Priced sources grouped by price basis (keys as in sources.<name>.price_basis), so like is compared with like. Empty when nothing priced.

    • comparison.price_provenanceobjectrequired

      Source -> provenance of the compared price (priced sources only).

  • sourcesobjectrequired

    One entry per requested source (booking, hotels, agoda, expedia). Fail-soft: a failed source is an entry with status: error, not a failed request.

  • metaobjectrequired
    Show 3 child fields
    • meta.requestedintegerrequired
    • meta.okintegerrequired

      Sources that returned a price.

    • meta.elapsed_snumberrequired

Errors

Errors return a JSON body with a detail field. Failed requests are not charged. See Errors for the full list and retry advice.

StatusMeaningRetry?
401Unauthorized

Missing or invalid API key.

{"detail":"missing or invalid API key"}
No, fix the request
402Payment Required

Not enough credits for this request. Only returned once credit enforcement is switched on; during the beta metering runs in shadow mode and never blocks.

{"detail":"insufficient credits: this request costs 5, 0 available. Credits renew 2026-11-01."}
No, fix the request
422Unprocessable Content

Request validation failed. detail is a list of problems for schema errors, or a string for semantic checks performed by the endpoint.

{"detail":[{"loc":["body","name"],"msg":"Field required","type":"missing"}]}
No, fix the request
429Too Many Requests

Too many requests: the key's requests-per-minute limit was exceeded, or (once credit enforcement is on) the plan's concurrent-request limit. No rate-limit or Retry-After headers are sent; back off and retry.

{"detail":"rate limit 60/min exceeded"}
Yes, with backoff
502Bad Gateway

The upstream source failed, blocked the request or returned an unusable answer. Safe to retry later; failed requests are not charged.

{"detail":"RuntimeError"}
Yes, with backoff

Try it

  1. Export your key: export SCRAPERCOMPANY_API_KEY=sk_... (no key yet? request access).
  2. Copy the cURL example above and run it in a terminal.
  3. Or open the interactive playground on api.scrapercompany.com, paste your key and pick this endpoint.

Credits

20 credits per successful call. Failed, blocked and empty results are free. Credit metering is in beta and does not block requests yet.