1Stay API
Reference

1Stay gives AI agents and applications access to real hotel inventory — 300,000+ properties across 140+ countries — through a simple MCP server or REST API. Reservations use the hotel's own confirmation number. Loyalty points are eligible. You're never the merchant of record.

The MCP server name is 1stay at endpoint mcp.stayker.com. There are 7 tools covering the complete booking lifecycle.

Early Access. 1Stay is onboarding developers by application. Sandbox access is free for 90 days — production requires approval and a Stripe account. Apply for access →

MCP Server Details

MCP Configuration
// Claude Desktop / claude_desktop_config.json
{
  "mcpServers": {
    "1stay": {
      "url": "https://mcp.stayker.com/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
REST Base URL
https://mcp.stayker.com/v1

What Makes This Different

Most hotel APIs are OTA-style: you buy inventory, mark it up, you're the merchant. 1Stay is infrastructure: your agent connects guests to hotels directly. The hotel charges the guest. You build the experience, your users pay for your product — 1Stay is the booking layer that makes it possible. No inventory risk, no float, no chargebacks.

Three things that matter to your users: Loyalty points are eligible (guest books direct-equivalent). The hotel's own confirmation number appears in the guest email. And if the guest has a problem, they call the hotel — not you.

Quick
Start

From zero to a real hotel booking in three tool calls. The canonical flow for any AI agent.

1

Search for hotels

Call search_hotels with city, dates, and guest count. Returns a list of available properties with rates and a search_id you'll need for the next step.

Example
// Tool: search_hotels
{
  "city": "Austin, TX",
  "check_in": "2026-05-01",
  "check_out": "2026-05-04",
  "guests": 2
}

// Returns: search_id, list of hotels with rate_plan_id per rate
2

Get rates for a specific hotel

Call get_rates with your search_id and the hotel's hotel_id. Returns room types, nightly rates, totals, and a 15-minute rate hold.

Example
// Tool: get_rates
{
  "search_id": "srch_a1b2c3d4",
  "hotel_id": "htl_x9y8z7"
}

// Returns: rates[], expires_at, rate_plan_id per rate
3

Book it

Call book_hotel with the guest's info and the rate_plan_id from step 2. Returns the hotel's own confirmation number.

Example
// Tool: book_hotel
{
  "rate_plan_id": "rp_f8e2a1b3",
  "guest": {
    "first_name": "Jane",
    "last_name": "Smith",
    "email": "[email protected]"
  },
  "idempotency_key": "idem_unique_per_attempt"
}

// Returns: confirmation_number (hotel's own), booking_id, total

API Keys &
Auth

All requests require a Bearer token in the Authorization header. API keys are issued per application and scoped to either sandbox or production mode.

Request Header
Authorization: Bearer 1stay_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Sandbox keys begin with 1stay_test_. Production keys begin with 1stay_live_. You'll receive your key after approval and Stripe onboarding.

Never expose your API key in client-side code. All 1Stay API calls should be made from your server. API keys can make real bookings that incur real charges.

Key Scopes

Production plans include 50,000 searches per month with overage billed at $0.002/search. Sandbox keys use test properties and never charge guests. Production keys access live inventory and create real reservations.

search_hotels

Search for available hotels by location and dates. Returns a ranked list of properties with rates and a search_id valid for 15 minutes. The canonical entry point for any booking flow.

🔍
search_hotels
Find available hotels with live rates for a given location and date range
▲ collapse
Parameters
ParameterTypeRequiredDescription
citystringrequiredCity and state/country. e.g. "Austin, TX" or "London, UK"
check_instringrequiredCheck-in date. ISO 8601 format: YYYY-MM-DD
check_outstringrequiredCheck-out date. ISO 8601 format: YYYY-MM-DD. Must be after check_in.
guestsintegerrequiredNumber of adult guests. Min 1, max 8.
radius_kmintegeroptionalSearch radius in kilometers. Default: 10. Max: 50.
max_ratenumberoptionalMaximum nightly rate in USD. No default (returns all rates).
limitintegeroptionalMax results to return. Default: 20. Max: 100.
Example Request
{
  "city": "Nashville, TN",
  "check_in": "2026-06-12",
  "check_out": "2026-06-15",
  "guests": 1,
  "max_rate": 250
}
200 OK Success Response
{
  "search_id": "srch_a1b2c3d4e5f6",
  "expires_at": "2026-06-12T14:45:00Z",
  "count": 34,
  "hotels": [
    {
      "hotel_id": "htl_x9y8z7w6",
      "name": "The Hermitage Hotel",
      "address": "231 6th Ave N, Nashville, TN",
      "stars": 5,
      "from_rate": 189.00,
      "currency": "USD",
      "loyalty_eligible": true,
      "distance_km": 0.4
    }
    // ... more hotels
  ]
}

get_hotel_details

Retrieve full property information for a specific hotel: amenities, photos, policies, and proximity. Use this when your agent needs to describe a hotel to a user before booking.

🏨
get_hotel_details
Full property details including amenities, policies, and description
▼ expand
Parameters
ParameterTypeRequiredDescription
hotel_idstringrequiredHotel ID from a search_hotels response
search_idstringoptionalPass this to include rate context in the response
Example Request
{
  "hotel_id": "htl_x9y8z7w6",
  "search_id": "srch_a1b2c3d4e5f6"
}
200 OK
{
  "hotel_id": "htl_x9y8z7w6",
  "name": "The Hermitage Hotel",
  "chain": "Independent",
  "stars": 5,
  "description": "Historic landmark hotel in downtown Nashville...",
  "amenities": ["Restaurant", "Bar", "Fitness Center", "Valet Parking"],
  "check_in_time": "15:00",
  "check_out_time": "12:00",
  "cancellation_policy": "Free cancellation until 48hrs before arrival",
  "loyalty_eligible": true,
  "coordinates": { "lat": 36.1594, "lng": -86.7798 }
}

get_rates

Get available room types and rates for a specific hotel within a search context. Returns a rate hold valid for 15 minutes. Each rate includes a rate_plan_id — pass this directly to book_hotel.

💰
get_rates
Room types and rates for a hotel. Starts a 15-minute rate hold.
▼ expand
Parameters
ParameterTypeRequiredDescription
search_idstringrequiredSearch ID from a search_hotels response. Must not be expired.
hotel_idstringrequiredHotel ID from the same search_hotels response
Rate holds are 15 minutes. The expires_at timestamp in the response is when this rate will no longer be bookable. Show users a countdown. If a rate expires, call get_rates again.
200 OK
{
  "search_id": "srch_a1b2c3d4e5f6",
  "hotel_id": "htl_x9y8z7w6",
  "expires_at": "2026-06-12T15:00:00Z",
  "ttl_seconds": 900,
  "cached_at": "2026-06-12T14:45:00Z",
  "rates": [
    {
      "rate_plan_id": "rp_f8e2a1b3",
      "room_name": "King Room - City View",
      "nightly_rate": 189.00,
      "total": 567.00,
      "taxes_estimated": 78.54,
      "currency": "USD",
      "cancellation_policy": "Free until 48hrs prior",
      "loyalty_eligible": true
    }
  ]
}

book_hotel

Create a reservation using a rate_plan_id from get_rates. Returns the hotel's own confirmation number. Rate is live-verified at booking time — if the price changed, you'll receive a rate_changed response (see Rate Changes).

book_hotel
Create a reservation. Returns hotel's confirmation number.
▼ expand
Parameters
ParameterTypeRequiredDescription
rate_plan_idstringrequiredRate plan ID from a get_rates response. Must not be expired.
guest.first_namestringrequiredGuest's first name as it appears on their ID
guest.last_namestringrequiredGuest's last name
guest.emailstringrequiredGuest email — confirmation is sent here by the hotel
idempotency_keystringrequiredUnique string per booking attempt. Prevents duplicate bookings on retry. Generate a UUID per attempt.
accept_rate_changebooleanoptionalSet true on resubmit after receiving a rate_changed response. Requires a new idempotency_key.
Idempotency is your friend. If your request times out or errors, retry with the same idempotency_key. If the booking succeeded, you'll get the original booking back. If it didn't, a new attempt is made. Never retry with a different key for the same booking attempt — you may create a duplicate reservation.
Example Request
{
  "rate_plan_id": "rp_f8e2a1b3",
  "guest": {
    "first_name": "Jane",
    "last_name": "Smith",
    "email": "[email protected]"
  },
  "idempotency_key": "550e8400-e29b-41d4-a716-446655440000"
}
201 Created
{
  "booking_id": "bk_9f8e7d6c",
  "confirmation_number": "HLC-98234",
  // ^ The hotel's own confirmation number, not ours
  "hotel": "The Hermitage Hotel",
  "check_in": "2026-06-12",
  "check_out": "2026-06-15",
  "total": 567.00,
  "currency": "USD",
  "guest_email_sent": true,
  "loyalty_eligible": true
}

get_booking

Retrieve the details of an existing booking by booking_id. Scoped to your API key — you can only retrieve bookings created with your key.

📋
get_booking
Retrieve booking details by ID. Scoped to your API key.
▼ expand
Parameters
ParameterTypeRequiredDescription
booking_idstringrequiredBooking ID returned from a book_hotel response
200 OK
{
  "booking_id": "bk_9f8e7d6c",
  "status": "confirmed",
  "confirmation_number": "HLC-98234",
  "hotel": "The Hermitage Hotel",
  "check_in": "2026-06-12",
  "check_out": "2026-06-15",
  "guest_name": "Jane Smith",
  "total": 567.00,
  "created_at": "2026-06-10T14:52:33Z"
}

cancel_booking

Cancel an existing reservation. Cancellation is subject to the hotel's cancellation policy returned at time of booking. Always surface the policy to the guest before confirming cancellation.

cancel_booking
Cancel a reservation. Subject to hotel's cancellation policy.
▼ expand
Parameters
ParameterTypeRequiredDescription
booking_idstringrequiredBooking ID to cancel
reasonstringoptionalCancellation reason. Stored for your records.
Always confirm with the guest first. Show them the cancellation policy and penalty (if any) before calling this tool. Cancellation cannot be undone.
200 OK
{
  "booking_id": "bk_9f8e7d6c",
  "status": "cancelled",
  "cancellation_number": "CXL-4421",
  "penalty": 0,
  "refund_amount": 567.00,
  "cancelled_at": "2026-06-11T09:14:22Z"
}

search_tools

Discover available MCP tools and their capabilities at runtime. Useful for agents that want to dynamically understand what operations are supported without relying on static documentation.

🛠
search_tools
List and describe available MCP tools at runtime. No parameters required.
▼ expand

No parameters required. Returns the complete list of tools with descriptions and parameter schemas.

200 OK
{
  "server": "1stay",
  "version": "1.0.0",
  "tools": [
    {
      "name": "search_hotels",
      "description": "Search available hotels by location and dates",
      "parameters": { /* schema */ }
    }
    // ... all 7 tools
  ]
}

Error
Codes

All errors return a JSON body with an error field (machine-readable code) and a message field (human-readable). Design your agent to handle these gracefully.

HTTPError CodeWhen / What to Do
200Idempotency hit — same key, booking already created. Returns original booking.
201Booking created successfully.
400invalid_requestMissing or invalid fields. Check guest data and required params.
400cache_id_invalidrate_plan_id doesn't exist. Did you use an ID from a different search?
401unauthorizedAPI key missing, invalid, or revoked.
409rate_changedRate changed between search and booking. See Rate Change Handling.
410rate_unavailableRoom/rate no longer available. Search again.
410cache_expiredRate hold expired (>15 min). Call get_rates again.
429budget_exceededDaily search or booking budget exhausted. Resets midnight UTC.
500booking_failedUpstream error. No booking was created. Safe to retry with same idempotency key.

Rate Change
Handling

Hotel rates are live inventory. Any price difference between when you called get_rates and when you call book_hotel triggers a 409 rate_changed response — even a difference of $0.01. No exceptions. No silent overrides. The guest always sees every change.

Zero tolerance policy. Rate changes up, down, or sideways all require guest confirmation. This protects your users and keeps you off the hook.
409 rate_changed Response
{
  "error": "rate_changed",
  "original_rate": { "nightly": 189.00, "total": 567.00 },
  "new_rate": { "nightly": 209.00, "total": 627.00 },
  "difference": { "nightly": 20.00, "total": 60.00, "direction": "increase" },
  "action_required": "Resubmit with accept_rate_change: true, or start a new search.",
  "accept_rate_change_expires_at": "2026-06-12T15:15:00Z"
}

To proceed at the new rate, resubmit the book_hotel call with accept_rate_change: true and a new idempotency_key. The original key is tied to the original price and is now expired.

What your agent should say

ScenarioSuggested Agent Response
Rate increased"The hotel just updated this room from $189 to $209/night — that's $60 more for your stay. Want to book at the new rate or look at other options?"
Rate decreased"Good news — the rate dropped from $209 to $189/night, saving you $60. Want me to lock it in?"
Small change"The rate changed slightly from $189.00 to $189.47/night — likely a tax adjustment. That's $1.41 more total. Shall I proceed?"

Access
Tiers

API capabilities and limits vary by access tier. All tiers use the same endpoints, response formats, and error codes — only the data volume and booking behavior differ.

Capability Sandbox Production Enterprise
Key prefix 1stay_test_ 1stay_live_ 1stay_live_
Hotels per search 5 15 Custom
Rate plans per hotel 2 All available All available
Bookings Simulated only Live reservations Live reservations
Searches / month 100 / day 50,000 included Custom
Search overage $0.002 / search Custom
Monthly fee Free (90 days) $99 / mo Custom
Platform fee $3.00 / booking Custom
Booking service fee Set your own via Stripe Connect Custom structure
Cancellations Simulated Live Live
Duration 90 days Ongoing Contract term
Same code, different key. Your integration code is identical between sandbox and production. Swap 1stay_test_ for 1stay_live_ and you're live. Response formats, error codes, and tool interfaces don't change.

Sandbox
Mode

Sandbox keys (1stay_test_...) return realistic test data without making real reservations. Use sandbox for development, integration testing, and demos.

Sandbox keys cannot create real bookings. Any call to book_hotel with a 1stay_test_ key returns a simulated confirmation. No hotel is contacted, no guest is charged, no reservation is created. This is enforced server-side and cannot be bypassed.
90-day trial. Sandbox access is valid for 90 days from approval. A reminder goes out at day 75. If you need more time, a self-serve extension is available once from your dashboard. After that, the path forward is production — or a conversation.
Sandbox is production-identical. Same response format, same error codes, same rate change handling — just with test properties and no real bookings. Your integration code doesn't change between sandbox and production.

Switch from sandbox to production by swapping your key from 1stay_test_ to 1stay_live_. No other code changes required.

Questions? Email hello@1stay.ai.