directrate

Brand-direct hotel pricing API

Hotel rates and award points from the chains themselves — and the same hotel's price on the OTAs, side by side.

11 chains and 5 OTAs, read from each chain's own booking system at the moment you ask: every rate plan, the member rate, the night in points, and Booking.com, Expedia, Hotels.com, Trip.com and Agoda for the same property — one JSON response, priced per shop.

X-Credits-Charged on every response · errors are free · no key needed to read the docs

Hyatt Place Chicago/Downtown-The Loop

check-in 2026-11-10 · 1 night · 2 adults · after tax

Public
$621.97

lowest public rate

Member
$609.53

Hyatt member rate, no login

Points
15,000 pts

4.06 ¢ per point

OTA · Expedia
$522.00

cheapest of 5 channels · -16.07 % vs direct

X-Credits-Charged: 11 credit = $0.0097 on Startershopped 2026-10-11 09:26Z
Run it live in the playground →

11 hotel chains, brand-direct

5 OTAs beside them

8 of 11 chains with award points

98.4 % mean 30-day uptime, 16 sources status

Listed on Apify Store

Status snapshot generated 2026-10-11T09:30:32.663Z

Same room, four prices

The two prices an OTA scraper never sees are the ones your users ask about.

A real night at a Choice hotel in Chicago, read from Choice's own booking system and from the five OTAs at the same moment.

  1. 01
    Public rate (BAR)$118.70

    what the chain's site quotes anyone · OTA scrapers: no · directrate: yes

  2. 02
    Member rate$106.83

    the loyalty price, quoted to an anonymous visitor · OTA scrapers: no · directrate: yes

  3. 03
    Award night20,000 pts · 0.53 ¢/pt

    cents-per-point against the after-tax cash rate · OTA scrapers: no · directrate: yes

  4. 04
    Expedia (cheapest OTA)$97.00 · -18.28 %

    the reseller's public price for the same night · OTA scrapers: yes · directrate: yes

Clarion Inn Elmhurst - Oak Brook near I-88, I-290, I-294 · check-in 2026-11-10 · 1 night · after tax · shopped 2026-10-11 09:27Z · verdict ota_undercuts, max undercut 18.28 %

A hotel sells the same night at four prices. An OTA scraper sees the top and the bottom of the stack: the public rate the chain's site shows anyone, and the reseller's price. It never sees the two in the middle — the member rate the chain quotes a logged-out visitor, and the award night in points — because they do not exist on an OTA page.

directrate reads the chain's own booking system, so the whole stack comes back in one response: lowest.bar, lowest.member, lowest_points with cpp, and on /parity each OTA's public price beside them with the gap stated per channel. Every row says which tax basis it is in, so the comparison is apples to apples.

How /parity compares them →

Six modes, one key

Every mode is a POST with a hotel code and a date. Here is what each one answered.

Real responses, trimmed. The billing line under each tab is the endpoint's own, as the OpenAPI description states it.

Request

curl -s https://api.directrate.dev/v1/shop \
  -H "x-api-key: $DIRECTRATE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"chain":"hyatt","hotel_code":"CHIZP","checkin":"2026-11-10","los":1,"occupancy":{"adults":2}}'

Response · X-Credits-Charged: 1

{
  "chain": "hyatt",
  "hotel": {
    "code": "CHIZP",
    "name": "Hyatt Place Chicago/Downtown-The Loop",
    "currency": "USD"
  },
  "checkin": "2026-11-10",
  "los": 1,
  "status": "available",
  "rates": [
    {
      "room_name": "View 2 Queens  (with sofa bed)",
      "rate_name": "Standard Rate",
      "rate_type": "bar",
      "member_only": false,
      "refundable": null,
      "price_per_night": 666.06,
      "price_per_night_after_tax": 666.06,
      "price_total": 666.06,
      "tax_included": true,
  … 54 more lines
}

Billing. Billed the chain's shop weight per live shop; cache hits 0.25×; errors free. shopped 2026-10-11 09:26Z · hyatt CHIZP

Request

curl -s https://api.directrate.dev/v1/award \
  -H "x-api-key: $DIRECTRATE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"chain":"marriott","hotel_code":"CHIRL","checkin":"2026-11-10","los":1,"occupancy":{"adults":2}}'

Response · X-Credits-Charged: 1

{
  "chain": "marriott",
  "hotel": {
    "code": "CHIRL",
    "currency": "USD",
    "name": "Residence Inn by Marriott Chicago Downtown/Loop",
    "brand": "Residence Inn"
  },
  "checkin": "2026-11-10",
  "los": 1,
  "status": "available",
  "award_rates": [
    {
      "room_name": "Superior",
      "rate_name": "Redemption with Points Upgrade",
      "award_type": "standard",
      "points_per_night": 60000,
      "points_total": 60000,
      "cash_copay": null,
      "taxes_fees": 3.78,
      "currency": "USD"
  … 49 more lines
}

Billing. Billed like /shop (shop weight live, 0.25× cached). shopped 2026-10-11 09:28Z · marriott CHIRL

Request

curl -s https://api.directrate.dev/v1/parity \
  -H "x-api-key: $DIRECTRATE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"chain":"choice","hotel_code":"IL263","checkin":"2026-11-10","los":1,"occupancy":{"adults":2}}'

Response · X-Credits-Charged: 5.25

{
  "chain": "choice",
  "hotel": {
    "code": "IL263",
    "currency": "USD",
    "name": "Clarion Inn Elmhurst - Oak Brook near I-88, I-290, I-294",
    "brand": "Clarion"
  },
  "checkin": "2026-11-10",
  "los": 1,
  "currency": "USD",
  "direct": {
    "status": "available",
    "public": {
      "per_night": 118.7,
      "total": 118.7,
      "currency": "USD",
      "basis": "after_tax",
      "rate_name": "SAPR2",
      "refundable": false
    },
  … 114 more lines
}

Billing. Billed as the shops it made (each chain's weight, cached 0.25×) plus 1 credit when ids were resolved live. shopped 2026-10-11 09:27Z · choice IL263

Request

curl -s https://api.directrate.dev/v1/calendar \
  -H "x-api-key: $DIRECTRATE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"chain":"choice","hotel_code":"IL263","start":"2026-11-10","days":7,"los":1,"occupancy":{"adults":2}}'

Response · X-Credits-Charged: 0.7

{
  "chain": "choice",
  "hotel": {
    "code": "IL263",
    "currency": "USD",
    "name": "Clarion Inn Elmhurst - Oak Brook near I-88, I-290, I-294",
    "brand": "Clarion"
  },
  "start": "2026-11-10",
  "days": 7,
  "los": 1,
  "currency": "USD",
  "nights": [
    {
      "date": "2026-11-10",
      "status": "available",
      "min_los": null,
      "lowest": {
        "bar": 126.95,
        "bar_after_tax": 139.65,
        "member": 120.6
  … 80 more lines
}

Billing. Billed per night: 0.1 credit on native-calendar chains; the shop weight per live night elsewhere; cached nights 0.25×. shopped 2026-10-11 09:27Z · choice IL263

Request

curl -s https://api.directrate.dev/v1/compset \
  -H "x-api-key: $DIRECTRATE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"chain":"choice","hotel_codes":["IL263"],"checkin":"2026-11-10","los":1,"occupancy":{"adults":2}}'

Response · X-Credits-Charged: 1

{
  "chain": "choice",
  "checkin": "2026-11-10",
  "los": 1,
  "hotels": [
    {
      "code": "IL263",
      "name": "Clarion Inn Elmhurst - Oak Brook near I-88, I-290, I-294",
      "status": "available",
      "lowest": {
        "bar": 107.91,
        "member": 97.12,
        "bar_after_tax": 118.7,
        "member_after_tax": 106.83
      },
      "lowest_points": 20000,
      "currency": "USD"
    }
  ],
  "shopped_at": "2026-10-11T09:27:53.075Z",
  "source": "live",
  … 2 more lines
}

Billing. Billed per hotel returned at the chain's compset rate (hotels served from cache 0.25×; error/not_found free), minimum 1 credit per call. shopped 2026-10-11 09:27Z · choice IL263

Request

curl -s https://api.directrate.dev/v1/stay \
  -H "x-api-key: $DIRECTRATE_API_KEY" \
  -H "content-type: application/json" \
  -d '{"chain":"choice","hotel_code":"IL263","checkin":"2026-11-10","los":3,"occupancy":{"adults":2}}'

Response · X-Credits-Charged: 1.3

{
  "chain": "choice",
  "hotel": {
    "code": "IL263",
    "currency": "USD",
    "name": "Clarion Inn Elmhurst - Oak Brook near I-88, I-290, I-294",
    "brand": "Clarion"
  },
  "checkin": "2026-11-10",
  "checkout": "2026-11-13",
  "los": 3,
  "currency": "USD",
  "stay": {
    "status": "available",
    "lowest": {
      "bar": 105.68,
      "member": 95.11,
      "bar_after_tax": 116.25,
      "member_after_tax": 104.62
    },
    "lowest_points": 17333.33,
  … 59 more lines
}

Billing. Billed as the two calls would be: the shop weight (cached 0.25×) plus the calendar nights, under one usage line `stay`. shopped 2026-10-11 09:27Z · choice IL263

Coverage

11 chains, 5 OTAs, and exactly what each one returns.

Capability flags and credit weights come from the same table the gateway bills with, so this matrix cannot drift from the invoice.

SourceProgramPublicMemberPointsNative calendarComp set batchCredits / shop
MarriottMarriott Bonvoy✓✓✓✓✓1
HiltonHilton Honors✓✓✓✓–1
HyattWorld of Hyatt✓✓✓✓2–1
IHGIHG One Rewards✓✓✓✓–1
ChoiceChoice Privileges✓✓✓✓✓1
RadissonRadisson Rewards✓✓✓✓✓3
I PreferI Prefer (Preferred Hotels)✓✓✓✓–1
WyndhamWyndham Rewards✓✓–1✓✓6
MeliáMeliá Rewards✓✓–1✓2✓1
Best WesternBest Western Rewards✓✓✓✓✓5
Premier Inn—✓n/an/a✓–1
Booking.com OTAGenius (reported apart)✓✓–––1
Expedia OTAMember Price (reported apart)✓✓–––1
Hotels.com OTAMember Price (reported apart)✓✓–––1
Trip.com OTA—✓––––1
Agoda OTAMember deal (reported apart)✓✓–––1

1 Points are login-gated at the source on Meliá and Wyndham; the column comes back empty rather than estimated. Premier Inn is cash-only. 2 Hyatt's calendar carries availability and points, Meliá's availability only — /calendar shops the open nights on those two.

Credits per shop are the default weights; a /compset hotel row is 0.25 (0.5 on Radisson, Best Western, Wyndham) and a native-calendar night 0.1 (0.25 on Wyndham). Full weight table.

Six of these chains are available self-serve nowhere else: Hyatt, Choice, Best Western, Meliá, Premier Inn, I Prefer.

Build, buy, or directrate

What it costs to have this data on Monday.

Build it yourselfBuy enterprise datadirectrate
Time to first callweeks — one scraper per chain, then the OTAsa contract, a sales cycle~5 minutes: sign in, create a key, curl
Cost to evaluateengineering time$$ before you see a response$0 — 250 credits a month, no card
Maintenance11 chain surfaces + 5 OTAs that change without noticethe vendor'snone — the status page shows what broke and when
Member rates & pointsif you reverse-engineer each apprarely, and never cents-per-pointmember rate on 10 chains, points on 8, cpp and certificates
Who maintains ityou, forevera vendor on their roadmapus, measured every 15 minutes

What people build

Four things that are one call each.

Each mockup is drawn from the snapshot on this page, not from a design file.

Clarion Inn Elmhurst · direct public per night vs cheapest OTA

2026-11-10Expedia 97 · 18.28 % under2026-11-16

Parity monitor

One /parity call per hotel per date; filter cheaper = channel for every undercut, rank by max_undercut_pct.

Parity docs →

Hyatt CHIZP · points per night, 7 nights · lowest 12,000

2026-11-102 nights at 12,000 pts2026-11-16

Award sweet-spot finder

Points per night across a window with cpp against the cash rate — the nights worth redeeming on, in gold.

Award docs →

Marriott CHIRL · award calendar, 7 nights · 47,000–56,000 pts

56k56k52k49k52k47k56k

darker = cheaper · one row per hotel in a comp set

Comp set calendar

Up to 25 hotels per call, one row per hotel, native batch where the chain has one; a heatmap by night from /calendar.

Comp set docs →

Choice IL263 · 3 nights from 2026-11-10 · split bookable

stay as one$317.04

nights alone$373

cheaper: stay · saving -17.65 % on the public rate

Stay-split checker

A 3-night stay as one against each night alone, and whether a split booking is even allowed.

Stay docs →

Billing honesty

You pay for results. The rules fit on one hand.

Illustration · a Free month

182 / 250 credits

  • 120 × /shop, 1-credit chains120
  • 6 × /parity, five channels37.5
  • 2 × /calendar, 30 nights, IHG6
  • 1 × /compset, 25 hotels, Choice6.25
  • 49 × cache hits (0.25×)12.25
  • 9 × errors / not_found0

Illustrative mix; every line is priced by the rules on the right. 429 quota_exceeded at 250.

  1. 01

    Eight of the eleven chains always cost 1 credit a shop — Marriott, Hilton, Hyatt, IHG, Choice, Meliá, Premier Inn, I Prefer — and so do the five OTAs. Radisson is 3, Best Western 5, Wyndham 6, and those four values are the only ones there are.

  2. 02

    A result served from a recent cache bills 0.25× the live price; max_age: 0 forces a live shop at full price.

  3. 03

    Errors, a hotel code the chain does not know (not_found), upstream timeouts and an OTA with no listing for the hotel cost nothing.

  4. 04

    The quota is checked before a call is made, so a request that would exceed it is refused (429 quota_exceeded) rather than half-billed.

  5. 05

    Rate limits are an abuse guard per key, not a sold feature: 429 rate_limited comes with Retry-After.

  6. 06

    Every response states its own price in X-Credits-Charged — the number written to your usage.

  7. Full pricing, weights and the credits→calls table →

Pricing

Free to try, priced per credit, weights fixed per chain.

Free

$0 /month

Try every endpoint on every chain. No card.

  • 250 credits a month
  • = 250 shops on a 1-credit chain
  • = 1,000 comp-set hotel rows
  • 2 requests/s
Start free

GrowthMost picked

$99 /month

A product with users: a parity monitor, an award tool, an agent.

  • 15,000 credits a month · $6.6 per 1,000
  • = 15,000 shops on a 1-credit chain
  • = 60,000 comp-set hotel rows
  • 20 requests/s
Upgrade: email us

Scale

$349 /month

Daily comp sets across a portfolio, or a calendar store of your own.

  • 75,000 credits a month · $4.65 per 1,000
  • = 75,000 shops on a 1-credit chain
  • = 300,000 comp-set hotel rows
  • 50 requests/s · 1-month rollover
Upgrade: email us

See all plans — Starter $29, Enterprise from $1,000, annual = two months free. Paid tiers are switched by hand today: email us and we flip the plan.

Operational honesty

Measured every 15 minutes. No SLA yet.

Each source is probed with a real shop on a schedule; the ratio below is what the probes got back over the window. Rates are read from each chain's own booking system; we are not affiliated with any chain or OTA.

SourceUptime (30 days)Avg latency
Agoda100 %1,408 ms
Best Western90.31 %3,316 ms
Booking.com100 %1,539 ms
Choice100 %2,027 ms
Trip.com100 %1,844 ms
Expedia100 %1,390 ms
Hilton99.83 %1,281 ms
Hotels.com100 %1,372 ms
Hyatt97.35 %9,908 ms
IHG98.29 %2,676 ms
I Prefer100 %751 ms
Marriott97.60 %1,363 ms
Meliá99.83 %1,036 ms
Premier Inn99.06 %2,942 ms
Radisson97.69 %3,764 ms
Wyndham94.52 %4,365 ms

generated 2026-10-11 09:30Z · build-time snapshot; refreshes from the API when the page can read it · uptime = the share of 15-minute probes the source answered coherently; our own probe errors are left out.

FAQ

Questions people ask before the first call.

Is this official hotel chain API data?

No. There is no partnership with any chain or OTA. The data is read from each chain's and each OTA's own publicly reachable pricing surfaces — the same numbers their app and website quote an anonymous visitor. No account credentials are used and no bookings are placed.

Can I get member rates without a loyalty account?

Yes, where the chain quotes them to an anonymous visitor — which most do. They arrive as their own rows with member_only: true and under lowest.member, never folded into the public rate. An OTA's Genius or Member Price is reported the same way and never enters the parity gap.

Does it return award / points pricing?

Yes, on 8 of the 11 chains, with points_per_night, any cash_copay, cpp — cents per point — computed per night against the after-tax cash rate for the same stay, and the free-night certificates that would cover the night on Marriott, Hilton and IHG. Points are login-gated at the source on Meliá and Wyndham, and Premier Inn is cash-only; those columns come back empty rather than estimated.

How fresh are the rates?

Live at request time, or from a cache a few minutes old — every response says which, in source and cache_age_sec. There is no nightly crawl behind this: the numbers are fetched when you call, so a points price that moved an hour ago has already moved here. max_age: 0 forces a live shop.

Where do I get the hotel codes?

GET /hotels?chain=…&q=<city or address> returns the chain's own property codes around a place, nearest first, for one credit (Best Western 5, Wyndham 6). The codes are the chain's internal identifiers (Marriott calls them MARSHA codes), so they are not guessable and not shared between chains. An OTA's code is its own hotel id, which a /parity call returns in channel id.

Why did a hotel come back as not_found?

The chain does not recognise that property code. Those responses are never charged. sold_out, closed and restricted_los are real answers — the hotel exists, it just isn't selling that stay. In /parity, a channel with skipped set had no listing matched for the hotel (or answered with an error) and is never charged either.

How is this different from a Booking.com or Expedia scraper?

Those return OTA inventory and OTA prices, and nothing else. This returns what the chain itself quotes — every rate plan, the loyalty member rate, the award night in points, cents-per-point and certificates — with the OTA price beside it when you ask for it, and the tax basis stated per row.

What is rate parity, and why would an OTA be cheaper than the hotel?

Rate parity is the hotel's promise — to its guests and in its OTA contracts — that its own site is never undercut. It breaks all the time: wholesale rates resold through an OTA, an OTA funding a discount from its own commission, a member price the hotel forgot to match. /parity finds those cases in one call per hotel per date.

Is this legal to use?

Not legal advice, just the facts about where the data comes from: no account credentials, no logins, no bookings, nothing behind a paywall. The rates are what each chain and each OTA quotes an anonymous visitor on its own public booking surface. No chain or OTA trademark, logo or creative asset is used or resold — their names appear here because they are what the data is about. What you do with the data is yours to square with your own obligations.

Is there an uptime SLA?

Not yet. Chains and OTAs change their systems without notice and a source can degrade for hours. The status page shows the measured 30-day uptime and latency per source, and source and cache_age_sec on every response say what you got. A live single-hotel lookup typically takes 1–10 seconds; a 25-hotel comp set or a five-channel parity call is paced on purpose and can take tens of seconds.

Two ways to start

Start with 250 credits, or start with a conversation.

Start free

  1. 01Sign in with a magic link or Google — no password, no card.
  2. 02Create a key in the dashboard. It is shown once; the Free plan gives it 250 credits a month.
  3. 03curl one of the quickstart requests and read X-Credits-Charged on the response.
Start free

Email us

A paid plan, an Enterprise contract, a chain you need that is not listed, or a question before you commit. No demo calendar yet — a reply within a working day.

hello@directrate.dev
Open in your mail app →