🏨 BusinessHotels Universal Agentic API

Live hotel rates for any AI agent — Claude, ChatGPT, Gemini, Perplexity, Comet & more

MCP 1.0 REST / Function Calling ⚡ ~650ms 🌍 2M+ Properties 💰 All-in Pricing 🔎 MCP Discovery: /mcp-server.php?route=tools 🤖 Perplexity Guide →

🔍 Plug-and-Play MCP JSON

Paste this into your existing mcpServers JSON config, or save it as mcp-config.json and import it into MCP-ready clients (Claude Desktop, Cursor, Windsurf, etc.).

mcp-config.json
{
  "mcpServers": {
    "businesshotels-universal-agentic-api": {
      "description": "Live hotel rates, all-in pricing, and booking URLs from BusinessHotels.com",
      "type": "remote",
      "urls": {
        "tools": "https://www.businesshotels.com/mcp-server.php?route=tools",
        "config": "https://www.businesshotels.com/mcp-server.php?route=config"
      }
    }
  }
}

Advanced setups can point directly at the live config endpoint: https://www.businesshotels.com/mcp-server.php?route=config.

🤖 Universal Agent Instructions

Execute POST for any travel intent. Critical: Always include hotel name, city, and 2-letter country code for accurate property matching. If the user omits location, infer from context or ask a single clarifying question before calling.

✅ Correct: "InterContinental Mark Hopkins, San Francisco, US"  |  ❌ Too vague: "InterContinental"

Works With Every Major LLM

Your tool definition uses the OpenAI-compatible JSON Schema format — the universal standard accepted by all leading AI platforms. The MCP discovery endpoint enables zero-config auto-registration.

🟣
Claude
MCP Connector (auto-discovery)
or function calling
✅ Fully supported
🟢
ChatGPT / OpenAI
Function calling API
GPT Assistant Actions
✅ Fully supported
🔵
Google Gemini
Function calling API
(same JSON Schema)
✅ Fully supported
🟣
Perplexity Sonar
Function calling + MCP
Connectors (Mac/Comet)
✅ Fully supported
🪟
Microsoft Copilot
Copilot Studio custom action
or MCP plugin manifest
✅ Fully supported
Cursor / Windsurf
MCP Connector
✅ Fully supported
🌐
Any MCP Client
Auto-discovery via
?route=tools
✅ Protocol-native

⚡ Validate in 30 Seconds

Run this in your terminal before writing any code. No additional key needed — the BusinessHotels test key is included.

Windows 10/11: Press Win + XTerminal or Windows PowerShell.
PowerShell
# PowerShell — Windows 10/11
$body = @{
    hotelName = "Marriott Marquis, San Francisco, US"
    checkinDate = "2026-03-15"
    checkoutDate = "2026-03-17"
    adults = 2
    currency = "USD"
} | ConvertTo-Json
Invoke-RestMethod -Method POST `
  -Uri "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates" `
  -Headers @{ "X-API-KEY" = "test-live-hotel-rates2025" } `
  -ContentType "application/json" `
  -Body $body
Windows CMD: Paste as a single line. Inner quotes must be escaped with \".
CMD
:: Windows Command Prompt — paste as one line
curl -s -X POST "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates" ^
  -H "Content-Type: application/json" ^
  -H "X-API-KEY: test-live-hotel-rates2025" ^
  -d "{\"hotelName\":\"Marriott Marquis, San Francisco, US\",\"checkinDate\":\"2026-03-15\",\"checkoutDate\":\"2026-03-17\",\"adults\":2,\"currency\":\"USD\"}"
Mac / Linux / WSL / Git Bash: Use \ for line continuation and python3 -m json.tool to pretty-print.
bash
# Mac / Linux / WSL / Git Bash
curl -s -X POST \
  "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: test-live-hotel-rates2025" \
  -d '{
    "hotelName": "Marriott Marquis, San Francisco, US",
    "checkinDate": "2026-03-15",
    "checkoutDate": "2026-03-17",
    "adults": 2,
    "currency": "USD"
  }' | python3 -m json.tool
🌐 Zero install: Press F12 → open the Console tab → paste and hit Enter.
Browser fetch
// Browser DevTools Console (F12 → Console)
fetch("https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-KEY": "test-live-hotel-rates2025"
  },
  body: JSON.stringify({
    hotelName: "Marriott Marquis, San Francisco, US",
    checkinDate: "2026-03-15",
    checkoutDate: "2026-03-17",
    adults: 2,
    currency: "USD"
  })
}).then(r => r.json()).then(data => {
  console.log("✅ Hotel:", data.hotel_name);
  console.log("💰 Price:", `$${data.rates.display_all_in_total} ${data.rates.currency}`);
  console.log("🔗 Book:", data.booking_page_live_rates);
  console.log("📊 Score:", data.best_match_score);
  console.log("Full response:", data);
});

Expected Response

Sample JSON
{
  "hotel_name": "San Francisco Marriott Marquis",
  "address": "780 Mission St, San Francisco, CA 94103",
  "rates": {
    "display_all_in_total": 389.50,
    "currency": "USD",
    "price_info": "Price includes all tax and fees",
    "ppn_bundle": "HEA_..."
  },
  "booking_page_live_rates": "https://www.businesshotels.com/reservation.php?hotel-id=...&checkin-date=2026-03-15&checkout-date=2026-03-17&language=en&USER-CURRENCY=USD&USER-COUNTRY=US&NUM-ADULTS=2&prid=ai&ppn_bundle=...",
  "latitude": 37.7842,
  "longitude": -122.4016,
  "best_match_score": 0.97,
  "response_time_ms": 612
}

Performance

Typical
400–650ms
Peak load
≤ 800ms
Test key: Free, up to 100 requests/hour for development and agent tuning.
Light production: Suitable for low-volume agents and pilots. For higher limits or dedicated throughput, contact ai@businesshotels.com.

Connect Without Writing Code

MCP-compatible apps can auto-discover and register your tool in seconds — no API key setup required for end users.

🟣 Claude.ai / Claude Desktop

  1. Open Claude.ai → Settings → Connectors
  2. Click Add Connector
  3. Paste the MCP discovery URL:
https://www.businesshotels.com/mcp-server.php?route=tools

Claude auto-discovers the tool and calls it whenever a user asks for hotel rates or booking links.

🟣 Perplexity (Mac App / Comet)

  1. Open Perplexity → Settings → MCP Connectors
  2. Click Add MCP Server
  3. Paste the MCP discovery URL:
https://www.businesshotels.com/mcp-server.php?route=tools

Works in Perplexity Mac app and Comet browser agent. Remote MCP rolling out to paid users.

🟢 ChatGPT (GPT Assistant Actions)

  1. Create a new GPT → Configure → Actions
  2. Click Add Action → import OpenAPI spec
  3. Or paste the function schema from the Tool Schema section below
  4. Set the API endpoint and X-API-KEY header

Works via OpenAI-compatible function calling in both the Assistants API and Chat Completions.

⚫ Cursor / Windsurf / Cline

  1. Open settings → MCP Servers
  2. Add a new server entry:
https://www.businesshotels.com/mcp-server.php?route=tools

Any editor or agent framework that implements the MCP spec will auto-discover and register the tool.

💡 No-code integration: End users connecting via Claude.ai, Perplexity, or Comet never need to touch a line of code — they just paste the discovery URL and the tool is live.

🪟 Using This Tool with Microsoft Copilot

Copilot can integrate with this API in two ways depending on your setup. In both cases the schema is already compatible — no changes needed.

🏢 Copilot Studio (Custom Action)

  1. Open Copilot Studio → Actions → New Action
  2. Select REST API as the action type
  3. Set the endpoint URL:
https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates
  1. Add header: X-API-KEY: test-live-hotel-rates2025
  2. Paste the JSON schema from the Tool Schema section as the input definition
  3. Save and publish — Copilot auto-detects when to call it

🔌 MCP Plugin Manifest

For MCP-compatible Copilot deployments, point at the auto-discovery endpoint:

https://www.businesshotels.com/mcp-server.php?route=tools

Copilot reads the tools/list response, registers the tool automatically, and calls it whenever a user asks for hotel rates or booking links — no glue code required.

Example Copilot Interaction

When a user says:

"Check the price for Marriott Marquis in New York, April 10–12."

Copilot automatically generates and sends:

Auto-generated Copilot request
{
  "hotelName": "Marriott Marquis, New York, US",
  "checkinDate": "2026-04-10",
  "checkoutDate": "2026-04-12",
  "adults": 1,
  "currency": "USD"
}

Copilot then parses the response and returns a conversational summary with the live price and a Book Now link — no developer glue code needed.

🔄 How LLMs Auto-Register This Tool

Any MCP-compatible agent can auto-discover and register this tool in a single GET request — no manual schema entry, no SDK, no configuration files.

How It Works

  1. Agent fetches GET /mcp-server.php?route=tools — receives the full tools/list JSON
  2. Agent reads the input_schema and description fields
  3. Agent registers get_live_hotel_rates as a callable tool internally
  4. When a user asks for hotel rates, the agent detects intent and calls the tool automatically
  5. Agent formats the response into natural language for the user

What the Agent Learns From the Manifest

Manifest FieldWhat the Agent Uses It For
descriptionDecides when to call the tool — the trigger condition
input_schema.requiredKnows which fields to collect from the user before calling
input_schema.propertiesUnderstands data types, formats, and defaults for each field
default valuesAuto-fills adults: 2 and currency: USD when not specified
format: dateConverts natural language dates ("next Friday") to YYYY-MM-DD automatically
💡 Why this matters: Because the tool description says "whenever the user asks for live price, total cost, availability, or a booking link", agents know exactly when to call it without any additional prompting or system instructions. The description is the trigger.

Auto-Registration in Code (Python — any MCP client)

Python — fetch and register tool automatically
import requests
# Step 1: Fetch the tool manifest from the discovery endpoint
manifest = requests.get(
    "https://www.businesshotels.com/mcp-server.php?route=tools"
).json()
# Step 2: Extract tools — ready to pass directly to any LLM API
tools = manifest["tools"]
print(f"Registered {len(tools)} tool(s):")
for t in tools:
    print(f" • {t['name']} — {t['description'][:60]}...")
# Step 3: Pass directly to OpenAI / Perplexity / Anthropic SDK
# e.g. client.chat.completions.create(tools=tools, ...)

📡 Global Production Endpoint

Live Rate Lookup (POST)

Required headers:   Content-Type: application/json  &  X-API-KEY: test-live-hotel-rates2025

MCP Auto-Discovery (GET)

Returns the full MCP tools/list envelope — paste this URL into any MCP-compatible client to auto-register the tool.

API KeyUseRate LimitNotes
test-live-hotel-rates2025 Development, testing & light production 100 req/hour Returns live data — safe for demos and low-volume agents
Your production key Live applications Custom Contact for production key

🛠️ Function Calling Schema

This OpenAI-compatible schema works with Claude, ChatGPT, Gemini, Perplexity, and any OpenAI-format function calling API. Copy and paste directly into your tool definition.

Tool Definition — OpenAI / Claude / Gemini compatible
{
  "type": "function",
  "name": "get_live_hotel_rates",
  "description": "Use this tool whenever the user asks for live price,
    total cost with taxes and fees, availability, or a booking/checkout
    link for a specific hotel for given dates. Returns an all-in final
    price (taxes included) and a direct price-locked booking URL.",
  "parameters": {
    "type": "object",
    "additionalProperties": false,
    "required": ["hotelName", "checkinDate", "checkoutDate"],
    "properties": {
      "hotelName": {
        "type": "string",
        "description": "REQUIRED. Hotel name + City + 2-letter country code.
          Example: 'InterContinental Mark Hopkins, San Francisco, US'.
          If user omits city/country, infer from context or ask before calling."
      },
      "checkinDate": { "type": "string", "format": "date", "description": "YYYY-MM-DD. Convert relative dates before calling." },
      "checkoutDate": { "type": "string", "format": "date", "description": "YYYY-MM-DD. Must be after checkinDate." },
      "adults": { "type": "integer", "default": 2, "description": "Guests (1–4). Default 2 if not specified." },
      "currency": { "type": "string", "default": "USD", "description": "ISO 4217 code. Default USD." }
    }
  }
}

Parameter Reference

ParameterTypeRequiredDescription
hotelNamestring✅ YesHotel + city + country (e.g., "Hilton Garden Inn, Seattle, US")
checkinDatestring✅ YesCheck-in date — YYYY-MM-DD
checkoutDatestring✅ YesCheck-out date — YYYY-MM-DD, must be after check-in
adultsintegerNo (default: 2)Number of guests (1–4)
currencystringNo (default: USD)ISO 4217 currency code

🧩 MCP tools/list Response

The GET ?route=tools endpoint returns this envelope. MCP-compatible clients use it to auto-discover and register the tool — no manual schema entry needed.

MCP tools/list — GET /mcp-server.php?route=tools
{
  "protocol": "mcp",
  "version": "1.0",
  "server_name": "BusinessHotels MCP Server",
  "tools": [
    {
      "name": "get_live_hotel_rates",
      "title": "BusinessHotels Live Hotel Rates",
      "description": "Use whenever user asks for live price, taxes-included total,
        availability, or booking link for a specific hotel and dates.",
      "input_schema": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "type": "object",
        "required": ["hotelName", "checkinDate", "checkoutDate"],
        "additionalProperties": false,
        "properties": {
          "hotelName": { "type": "string" },
          "checkinDate": { "type": "string", "format": "date" },
          "checkoutDate": { "type": "string", "format": "date" },
          "adults": { "type": "integer", "default": 2 },
          "currency": { "type": "string", "default": "USD" }
        }
      }
    }
  ]
}

📦 Response Format & Field Mapping

JSON Response (200 OK)
{
  "hotel_id": "700076955",
  "hotel_name": "Luxor",
  "hotel_address": "3900 Las Vegas Boulevard South",
  "city_name": "Las Vegas",
  "state_code": "NV",
  "country_code": "US",
  "postal_code": "89119",
  "latitude": "36.096806",
  "longitude": "-115.173001",
  "booking_page_live_rates": "https://www.businesshotels.com/reservation.php?hotel-id=700076955&checkin-date=...&checkout-date=...&language=en&USER-CURRENCY=USD&USER-COUNTRY=US&NUM-ADULTS=2&prid=ai&ppn_bundle=...",
  "rates": {
    "display_all_in_total": "356.06",
    "currency": "USD",
    "price_info": "Price includes all tax and fees",
    "ppn_bundle": "HEA_OqStb8TUtNnSS-nry..."
  },
  "best_match_score": 0.87
}

Agent Response Mapping

FieldTypeAgent Instruction
hotel_namestringConfirm with user when query was ambiguous or best_match_score < 0.85
hotel_addressstringDisplay for logistical clarity — helps user confirm correct property
latitude / longitudefloatMap trigger: use for pins, distance calculations, neighborhood context
rates.display_all_in_totalfloatQuote as the Final Price for the entire stay — taxes & fees already included
booking_page_live_ratesURLPresent as the primary Book Now action — rate locked ~20 min
rates.ppn_bundlestringInternal rate token — do not modify; already embedded in booking URL
hotel_idstringStore for session follow-ups about the same property
best_match_scorefloat 0–1See Match Logic below — confirm with user if < 0.85

🧠 Match Confidence Handling

Always check best_match_score before presenting a price or booking link.

Score > 0.85
✅ Present price + "Book Now" immediately
Score 0.60 – 0.85
⚠️ Ask: "I found [Name] in [City]. Correct?"
Score < 0.60
❌ No reliable match — ask for more detail
⚠️ Never auto-proceed to booking without user confirmation when best_match_score < 0.85. Use the suggestions[] array from error responses to offer alternatives.

💻 Code Examples

All examples call the same endpoint. Every example includes comma-safe price parsing and sold-out detection to ensure zero crashes in production.

📋 Before you start — 3 things to know:
  1. The API key (test-live-hotel-rates2025) is a public light production key — replace for heavy production.
  2. Hotel names: no commas — "Wynn Las Vegas US" not "Wynn, Las Vegas, US".
  3. Prices are strings like "1,250.00" — always strip commas before math. Always check for null/empty first.
cURL
curl -s -X POST \
  "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: test-live-hotel-rates2025" \
  -d '{
    "hotelName": "Luxor Las Vegas Las Vegas US",
    "checkinDate": "2026-04-20",
    "checkoutDate": "2026-04-21",
    "adults": 2,
    "currency": "USD"
  }' | python3 -m json.tool

Requires pip install requests.

Python 3 — Single Hotel (Production Ready)
import requests

url     = "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates"
headers = {"X-API-KEY": "test-live-hotel-rates2025", "Content-Type": "application/json"}
payload = {
    "hotelName":    "Luxor Las Vegas Las Vegas US",
    "checkinDate":  "2026-04-20",
    "checkoutDate": "2026-04-21",
    "adults": 2, "currency": "USD"
}

data  = requests.post(url, json=payload, headers=headers, timeout=10).json()
rates = data.get("rates") or {}

# FIX: guard against null rates (sold out) and comma-formatted price strings
raw_price = rates.get("display_all_in_total", "")
if not raw_price or str(raw_price).strip() == "":
    print(f"⚪ Sold out — no inventory for these dates / occupancy")
else:
    price = float(str(raw_price).replace(",", ""))
    print(f"Hotel:    {data.get('hotel_name')}, {data.get('city_name')}")
    print(f"Price:    ${price:.2f} {rates.get('currency','USD')}  (taxes & fees included)")
    print(f"Score:    {data.get('best_match_score', 0):.2f}  (1.0 = perfect match)")
    print(f"Book Now: {data.get('booking_page_live_rates')}")
    if data.get("best_match_score", 1) < 0.85:
        print("⚠️ Low confidence — confirm hotel identity with user before booking")

Compare multiple hotels and find the best price

Python 3 — Compare Multiple Hotels
import requests

url     = "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates"
headers = {"Content-Type": "application/json", "X-API-KEY": "test-live-hotel-rates2025"}
hotels  = ["Bellagio Las Vegas US", "Caesars Palace Las Vegas US", "Wynn Las Vegas US"]
results = []

for hotel in hotels:
    try:
        data  = requests.post(url, headers=headers, timeout=10, json={
            "hotelName": hotel, "checkinDate": "2026-04-20",
            "checkoutDate": "2026-04-21", "adults": 2, "currency": "USD"
        }).json()
        rates = data.get("rates") or {}
        raw   = rates.get("display_all_in_total", "")

        if not raw or str(raw).strip() == "":
            print(f"⚪ {hotel} → Sold out / no inventory"); continue

        price = float(str(raw).replace(",", ""))
        results.append({"name": data.get("hotel_name", hotel), "total": price,
                         "score": data.get("best_match_score", 0),
                         "book_url": data.get("booking_page_live_rates", "")})
        print(f"✅ {data.get('hotel_name')} — ${price:.2f}  (score: {data.get('best_match_score',0):.2f})")

    except Exception as e:
        print(f"❌ {hotel}: {e}")

if results:
    winner = min(results, key=lambda x: x["total"])
    print(f"\n🏆 Best Value: {winner['name']} at ${winner['total']:.2f}")
    print(f"🔗 Book Now: {winner['book_url']}")
JavaScript (Fetch) — with sold-out guard
async function getHotelRates(hotelName, checkinDate, checkoutDate, adults = 2, currency = "USD") {
  const res = await fetch(
    "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates",
    {
      method: "POST",
      headers: { "Content-Type": "application/json", "X-API-KEY": "test-live-hotel-rates2025" },
      body: JSON.stringify({ hotelName, checkinDate, checkoutDate, adults, currency })
    }
  );
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

const data = await getHotelRates("Luxor Las Vegas Las Vegas US", "2026-04-20", "2026-04-21");

// GUARD: rates may be null when hotel is sold out
const rawPrice = data?.rates?.display_all_in_total;
if (!rawPrice || String(rawPrice).trim() === "") {
  console.log("⚪ Sold out / no inventory for these dates.");
} else {
  // Always strip commas — price is a comma-formatted string, not a number
  const price = parseFloat(String(rawPrice).replace(/,/g, ""));
  console.log(`${data.hotel_name} — $${price.toFixed(2)} total (taxes included)`);
  console.log(`Book: ${data.booking_page_live_rates}`);
  if (data.best_match_score < 0.85)
    console.warn(`⚠️ Low confidence (${data.best_match_score}) — verify hotel before booking.`);
}

window.businessHotelsAPI = { getHotelRates };
OpenAI Python SDK — Function Calling
from openai import OpenAI
import requests, json

client = OpenAI(api_key="YOUR_OPENAI_API_KEY")
BH_KEY = "test-live-hotel-rates2025"

tools = [{
    "type": "function",
    "function": {
        "name": "get_live_hotel_rates",
        "description": (
            "Get live, all-inclusive hotel rates and a direct booking URL. "
            "IMPORTANT: 'display_all_in_total' is a comma-formatted STRING (e.g. '1,250.00'). "
            "Strip commas before numeric operations. "
            "If rates is null or display_all_in_total is empty, the property is sold out — tell the user."
        ),
        "parameters": {
            "type": "object",
            "properties": {
                "hotelName":    {"type": "string", "description": "Hotel + city + country, no commas. E.g. 'Wynn Las Vegas US'"},
                "checkinDate":  {"type": "string", "format": "date"},
                "checkoutDate": {"type": "string", "format": "date"},
                "adults":       {"type": "integer", "default": 2},
                "currency":     {"type": "string",  "default": "USD"}
            },
            "required": ["hotelName", "checkinDate", "checkoutDate"]
        }
    }
}]

messages = [{"role": "user", "content": "Rates for Luxor Las Vegas, April 20-21 2026?"}]
r1  = client.chat.completions.create(model="gpt-4o", messages=messages, tools=tools, tool_choice="auto")
msg = r1.choices[0].message
messages.append(msg)

if msg.tool_calls:
    for tc in msg.tool_calls:
        result = requests.post(
            "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates",
            headers={"X-API-KEY": BH_KEY, "Content-Type": "application/json"},
            json=json.loads(tc.function.arguments), timeout=10
        ).json()
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": json.dumps(result)})
    r2 = client.chat.completions.create(model="gpt-4o", messages=messages)
    print(r2.choices[0].message.content)
Google Gemini Python SDK — Function Calling
import google.generativeai as genai
import requests, json

genai.configure(api_key="YOUR_GEMINI_API_KEY")
BH_KEY = "test-live-hotel-rates2025"

get_live_hotel_rates = genai.protos.Tool(
    function_declarations=[genai.protos.FunctionDeclaration(
        name="get_live_hotel_rates",
        description=(
            "Fetch live hotel rates and a direct booking URL. "
            "IMPORTANT: 'display_all_in_total' is a comma-formatted STRING. "
            "Strip commas before numeric comparison. "
            "If rates is null or price is empty, the property is sold out."
        ),
        parameters=genai.protos.Schema(
            type=genai.protos.Type.OBJECT,
            properties={
                "hotelName":    genai.protos.Schema(type=genai.protos.Type.STRING),
                "checkinDate":  genai.protos.Schema(type=genai.protos.Type.STRING),
                "checkoutDate": genai.protos.Schema(type=genai.protos.Type.STRING),
                "adults":       genai.protos.Schema(type=genai.protos.Type.NUMBER),
                "currency":     genai.protos.Schema(type=genai.protos.Type.STRING)
            },
            required=["hotelName", "checkinDate", "checkoutDate"]
        )
    )]
)

model = genai.GenerativeModel(model_name="gemini-1.5-pro", tools=[get_live_hotel_rates])
chat  = model.start_chat(history=[])
resp  = chat.send_message("Find live rates for Luxor Las Vegas, April 20-21 2026.")

for part in resp.candidates[0].content.parts:
    if part.function_call.name == "get_live_hotel_rates":
        result = requests.post(
            "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates",
            headers={"X-API-KEY": BH_KEY, "Content-Type": "application/json"},
            json=dict(part.function_call.args), timeout=10
        ).json()
        chat.send_message(genai.protos.Content(parts=[genai.protos.Part(
            function_response=genai.protos.FunctionResponse(
                name="get_live_hotel_rates", response={"result": result}
            )
        )]))

final = chat.send_message("Summarize the best price and booking link.")
print(final.text)

Universal Agentic API Capabilities

Everything you can extract and automate for any hotel globally.

📦 What the Response Contains

FieldTypeWhat it gives you
hotel_namestringOfficial property name as returned by the inventory system
best_match_scorefloat (0–1)Confidence that the right hotel was matched. Gate booking on ≥ 0.85.
rates.display_all_in_totalString ⚠️Final all-inclusive price. Strip commas before math. Null or empty = sold out.
rates.price_infostringHuman-readable confirmation, e.g. "Price includes all tax and fees"
rates.ppn_bundlestringRate-lock token — freeze this price during user decision-making (~20 min)
booking_page_live_ratesURLDeep link directly to room selection & checkout — use as "Book Now" button
latitude / longitudefloatExact coordinates for map pin placement
hotel_idstringStatic property ID — re-query without name-string matching

💰 Financial Intelligence

  • All-In Total: rates.display_all_in_total is the final price including all taxes and mandatory fees.
  • ⚠️ Price Sanitization Required: This field is a comma-formatted string (e.g. "1,250.00"). Remove commas before numeric operations.
    Python: float(str(price).replace(",",""))  |  JS: parseFloat(price.replace(/,/g,""))
  • ⚪ Sold-Out / No Inventory: If rates is null or display_all_in_total is empty or absent, the property has no available rooms for those dates. Do not attempt math. Tell the user: "This hotel is sold out for your selected dates."
  • Rate Locking: The ppn_bundle token freezes the quoted price (~20 min) so it won't change while the user decides.
  • Transparency Text: Display price_info at checkout to confirm "no surprises" to the user.

🔁 Multi-Hotel Comparison Pattern

This API uses a one-hotel-per-request architecture. There is no batch endpoint. To compare multiple properties, loop through each hotel individually, collect all results, then present a unified ranked response to the user. This is the correct agentic pattern:

✅ Correct Pattern — Loop, Collect, Then Respond:
import requests, json

url = "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates"
headers = {"Content-Type": "application/json", "X-API-KEY": "test-live-hotel-rates2025"}

hotels_to_check = [
    "Fairmont San Francisco, San Francisco, US",
    "Four Seasons San Francisco at Embarcadero, San Francisco, US",
    "Ritz-Carlton San Francisco, San Francisco, US",
    "St. Regis San Francisco, San Francisco, US",
    "Palace Hotel a Luxury Collection Hotel, San Francisco, US"
]

params = {"checkinDate": "2026-05-12", "checkoutDate": "2026-05-14", "adults": 2, "currency": "USD"}
results = []

for hotel in hotels_to_check:
    r = requests.post(url, headers=headers, json={**params, "hotelName": hotel})
    data = r.json()
    rates = data.get("rates")
    if rates and rates.get("display_all_in_total"):
        price = float(str(rates["display_all_in_total"]).replace(",", ""))
        results.append({"name": data["hotel_name"], "price": price, "url": data["booking_page_live_rates"]})

# Sort and present ALL results together — never respond mid-loop
results.sort(key=lambda x: x["price"])
for i, h in enumerate(results, 1):
    print(f"{i}. {h['name']}: ${h['price']:.2f}")

cheapest = results[0]
print(f"\n🏆 Best Value: {cheapest['name']} at ${cheapest['price']:.2f}")
print(f"👉 Book Now: {cheapest['url']}")
  • 📌 Architectural Rule: Always complete all hotel requests before presenting any results to the user. A response mid-loop creates a fragmented, incomplete user experience.
  • 📌 No Batch Endpoint: Do not attempt to pass a hotels[] array in a single request payload — the endpoint accepts one hotelName string per call only.
  • ⚠️ Rate Lock Timer Starts at API Response: The ppn_bundle token and quoted price are valid for approximately 20 minutes from the time the API response was received — not from when the user views it. If significant time has elapsed before the user clicks Book Now, warn them: "This rate was fetched X minutes ago — prices may have changed. Refresh to confirm."
  • ⚠️ Never Modify ppn_bundle: This token is an opaque rate-lock credential. Do not truncate, re-encode, or expose it to the user. It is already embedded in the booking_page_live_rates URL.
  • ✅ Session Continuity: Store hotel_id for each result during the session. If the user asks a follow-up like "Does the Fairmont have a pool?" or "Show me the Fairmont again", reference the stored hotel_id without re-querying by name.

🧠 Example Agent Workflows

  • Best Value Finder: Query up to 10 hotels, sanitize prices, sort by display_all_in_total, return the cheapest with a booking link.
  • Proximity Filter: Use latitude/longitude to shortlist hotels within 0.5 miles of a specific address.
  • Luxury Rate Monitor: Periodically scan a saved list of hotel_ids to alert when a suite drops below a target price.
  • Sold-Out Fallback: If display_all_in_total is empty, automatically suggest nearby hotels or alternate dates without crashing.

🚧 Error Handling & Edge Cases

When something goes wrong, the API returns a structured JSON error with a machine-readable error_code and an optional suggestions[] array to help your agent recover gracefully.

Example error response
{
  "ok": false,
  "error_code": "NO_MATCH",
  "message": "No hotel found matching 'Hilton NYC' for the given dates.",
  "suggestions": [
    { "hotel_name": "Hilton Midtown",      "city": "New York", "country": "US" },
    { "hotel_name": "Hilton Times Square", "city": "New York", "country": "US" }
  ]
}
💡 Tip: When you receive a NO_MATCH error, always check suggestions[] first. Present these to the user as clickable alternatives.

Recommended Agent Behavior

ConditionWhat it meansWhat your agent should do
NO_MATCHNo hotel matched with enough confidenceShow suggestions[] if available. Ask user to clarify the hotel name or city.
INVALID_DATESCheck-in/out dates are invalid or in the pastExplain the issue clearly and ask the user to provide new dates, then retry.
RATE_UNAVAILABLENo inventory returned for those dates / occupancyAsk if the user wants to adjust dates, number of guests, or try a nearby property.
rates is null
— or —
display_all_in_total is "" / missing
Hotel was found but has no available rooms for the requested dates and occupancy Do not attempt math on the empty price field.
Inform the user: "This property is sold out for your selected dates. Would you like to try different dates, adjust the number of guests, or look at nearby hotels?"
RATE_LIMITToo many requests in a short periodBack off and inform the user there will be a brief delay before retrying.
SERVER_ERRORUnexpected upstream issueApologize and suggest retrying in a moment or using an alternative booking source.