🏨 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

🔍 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"
      }
    }
  }
}
📡 AI & Agentic Discovery Endpoints

Public Access: No API Key needed

🚀 Grok Optimized MCP
💙 Gemini Optimized MCP
🧡 ChatGPT Optimized MCP
🟣 Claude Optimized MCP
🟢 Perplexity Optimized MCP
🌐 Universal MCP Server
⚡ OpenAPI Spec (Rest)
📄 MCP Spec (JSON)
Full API Documentation ↗

Quick Connect Guide (No Code Required)

🚀 Grok / xAI

  1. Go to grok.com
  2. Open Connectors (sidebar or settings)
  3. Click Add Custom / Remote MCP
  4. Paste the Grok Optimized MCP URL

💙 Google Gemini & Vertex AI

  1. Open Google AI Studio / Vertex AI
  2. Go to Tools → Add Extension
  3. Select MCP and paste the Gemini Optimized URL

🟣 Claude & Perplexity

  1. Go to Settings → Connectors
  2. Add Custom Connector (Remote/SSE)
  3. Paste the Claude Optimized URL

🧡 ChatGPT (Connectors Beta)

  1. Go to Settings → Apps → Connectors
  2. Click Add MCP Server
  3. Use the ChatGPT Optimized URL
Open in ChatGPT ↗

⚫ Cursor / Windsurf / Cline

  1. Open settings → MCP Servers
  2. Add a new Remote/SSE entry
  3. Use the Universal MCP Server URL

🤖 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"

⚡ 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 = "San Francisco Marriott Marquis, San Francisco, CA, US"
    checkinDate = "2026-07-15"
    checkoutDate = "2026-07-16"
    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\":\"San Francisco Marriott Marquis, San Francisco, CA, US\",\"checkinDate\":\"2026-07-15\",\"checkoutDate\":\"2026-07-16\",\"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": "San Francisco Marriott Marquis, San Francisco, CA, US",
    "checkinDate": "2026-07-15",
    "checkoutDate": "2026-07-16",
    "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: "San Francisco Marriott Marquis, San Francisco, CA, US",
    checkinDate: "2026-07-15",
    checkoutDate: "2026-07-16",
    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, San Francisco, CA, US",
  "address": "780 Mission St, San Francisco, CA 94103",
  "rates": {
    "display_all_in_total": 710.94,
    "currency": "USD",
    "price_info": "Price includes all tax and fees",
    "ppn_bundle": "HEA_..."
  },
  "booking_page_live_rates": "https://www.businesshotels.com/....",
  "latitude": 37.7842,
  "longitude": -122.4016,
  "best_match_score": 0.97,
  "response_time_ms": 550
}

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.

🔄 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 required.

Discovery Phase: No API Key needed for tool registration & manifest fetching.

The Discovery Workflow

  1. Fetch: Agent calls GET /mcp-server.php?route=tools.
  2. Parse: Agent reads input_schema for required search parameters.
  3. Register: The tool get_live_hotel_rates is instantly activated.
  4. Execute: Agent calls the tool automatically when user intent matches.

🌐 Platform-Specific Behaviors

Platform Handshake Type Agent Behavior
♊ Gemini Enterprise Gateway Uses Model Armor to sanitize and index tools as "Skills."
𝕏 Grok Remote MCP Performs a rigid schema check; ideal for real-time search & code execution.
🟣 Claude SSE Connector Pins the tool version to the project for persistent multi-step workflows.
🟢 Perplexity Dynamic Code Writes on-the-fly Python logic to query the endpoint with minimal latency.
💡 Enterprise Discovery: This server is natively compatible with Gemini Enterprise Agent Gateway and xAI Remote MCP, allowing for autonomous discovery without SDK wrappers.

Python Implementation (Dynamic Discovery)

Standard MCP Discovery Script
import requests

# 1. Fetch the manifest from the Universal Discovery endpoint
ENDPOINT = "https://www.businesshotels.com/mcp-server.php?route=tools"
manifest = requests.get(ENDPOINT).json()

# 2. Extract tools for LLM registration
tools = manifest.get("tools", [])
print(f"Registered {len(tools)} Agentic Tool(s):")

for tool in tools:
    print(f" • {tool['name']} — {tool['description'][:60]}...")

# 3. Ready to pass to Gemini, Grok, or OpenAI SDKs
# e.g., client.chat.completions.create(tools=tools, ...)

🛠️ Function Calling Schema

This universal schema is optimized for Claude 3.5+, ChatGPT (o1/o3), Gemini 2.0, Grok, and Perplexity. It utilizes Strict Mode to ensure 100% reliable parameter passing.

Universal JSON Schema — Multi-Model Compatible
{
  "type": "function",
  "name": "get_live_hotel_rates",
  "description": "Trigger this tool for real-time pricing, total cost (inclusive of taxes/fees), availability, or direct booking links for a specific hotel. Returns price-locked URLs and verified inventory.",
  "parameters": {
    "type": "object",
    "required": ["hotelName", "checkinDate", "checkoutDate"],
    "properties": {
      "hotelName": {
        "type": "string",
        "description": "REQUIRED. Full hotel name + City + 2-letter Country Code. Example: 'The Ritz-Carlton, Tokyo, JP'. Infer location from context if omitted."
      },
      "checkinDate": { 
        "type": "string", 
        "format": "date", 
        "description": "Check-in date (YYYY-MM-DD). Convert natural language like 'next Tuesday' to this format." 
      },
      "checkoutDate": { 
        "type": "string", 
        "format": "date", 
        "description": "Check-out date (YYYY-MM-DD). Must be at least 1 day after checkinDate." 
      },
      "adults": { 
        "type": "integer", 
        "minimum": 1, 
        "maximum": 4, 
        "default": 2, 
        "description": "Number of adult guests. Default to 2." 
      },
      "currency": { 
        "type": "string", 
        "pattern": "^[A-Z]{3}$", 
        "default": "USD", 
        "description": "3-letter ISO code (e.g., USD, EUR). Must be UPPERCASE." 
      }
    },
    "additionalProperties": false
  },
  "strict": true
}

Parameter Reference

ParameterType/FormatRequiredLogic Notes
hotelNamestring✅ YesName + City + Country (e.g., "Hilton, Seattle, US")
checkinDateYYYY-MM-DD✅ YesMust be converted from natural language by the LLM.
checkoutDateYYYY-MM-DD✅ YesMust be after check-in date.
adultsintegerNo (2)Accepted range: 1 to 4.
currencyISO 4217No (USD)Must be 3 uppercase letters.

🧩 MCP tools/list Response

The GET /mcp-server.php?route=tools endpoint returns this standardized envelope for auto-registration in Gemini, Grok, and Claude.

MCP tools/list — JSON Response
{
  "protocol": "mcp",
  "version": "1.0",
  "server_name": "BusinessHotels Agentic Hub",
  "tools": [
    {
      "name": "get_live_hotel_rates",
      "description": "Fetches real-time pricing and booking links for a specific hotel and date range.",
      "input_schema": {
        "type": "object",
        "required": ["hotelName", "checkinDate", "checkoutDate"],
        "properties": {
          "hotelName": { "type": "string" },
          "checkinDate": { "type": "string", "format": "date" },
          "checkoutDate": { "type": "string", "format": "date" },
          "adults": { "type": "integer", "default": 2 },
          "currency": { "type": "string", "default": "USD" }
        },
        "additionalProperties": false
      }
    }
  ]
}

📦 Response Format & Field Mapping

JSON Response (200 OK)
{
  "hotel_id": "700076955",
  "hotel_name": "Luxor Las Vegas",
  "hotel_address": "3900 Las Vegas Boulevard South",
  "city_name": "Las Vegas",
  "country_code": "US",
  "latitude": "36.096806",
  "longitude": "-115.173001",
  "booking_page_live_rates": "https://www.businesshotels.com/...",
  "rates": {
    "display_all_in_total": "356.06",
    "currency": "USD",
    "price_info": "Price includes all taxes and fees"
  },
  "best_match_score": 0.98
}

Agent Response Mapping

FieldAgent Instruction
hotel_nameConfirm this name with the user if best_match_score < 0.85.
latitude / longitudeTrigger: Use to generate map pins or neighborhood context.
rates.display_all_in_totalQuote as the Final Total Price for the entire duration of the stay.
booking_page_live_ratesDisplay as the primary call-to-action for the user to finish booking.
best_match_scoreValues > 0.90 indicate high confidence in the hotel match.

🧠 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-07-20",
    "checkoutDate": "2026-07-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-07-20",
    "checkoutDate": "2026-07-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-07-20",
            "checkoutDate": "2026-07-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-07-12", "checkoutDate": "2026-07-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.

⚡ Sub-Second Agentic Execution

Our Universal Agentic API is optimized for the "Loop, Collect, Then Respond" pattern, allowing AI agents to rank multiple properties in real-time without timeouts.

🔄 Multi-Hotel Ranking

Agent verifies 5+ luxury properties in under 1 second.

🔍 Live Rate Verification

Final price validation and booking hand-off returned in ~531ms.

🚧 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.