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.
MCP Server Details
// Claude Desktop / claude_desktop_config.json { "mcpServers": { "1stay": { "url": "https://mcp.stayker.com/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } }
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.
Quick
Start
From zero to a real hotel booking in three tool calls. The canonical flow for any AI agent.
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.
// 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
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.
// Tool: get_rates { "search_id": "srch_a1b2c3d4", "hotel_id": "htl_x9y8z7" } // Returns: rates[], expires_at, rate_plan_id per rate
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.
// 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.
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.
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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | required | City and state/country. e.g. "Austin, TX" or "London, UK" |
| check_in | string | required | Check-in date. ISO 8601 format: YYYY-MM-DD |
| check_out | string | required | Check-out date. ISO 8601 format: YYYY-MM-DD. Must be after check_in. |
| guests | integer | required | Number of adult guests. Min 1, max 8. |
| radius_km | integer | optional | Search radius in kilometers. Default: 10. Max: 50. |
| max_rate | number | optional | Maximum nightly rate in USD. No default (returns all rates). |
| limit | integer | optional | Max results to return. Default: 20. Max: 100. |
{
"city": "Nashville, TN",
"check_in": "2026-06-12",
"check_out": "2026-06-15",
"guests": 1,
"max_rate": 250
}
{
"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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| hotel_id | string | required | Hotel ID from a search_hotels response |
| search_id | string | optional | Pass this to include rate context in the response |
{
"hotel_id": "htl_x9y8z7w6",
"search_id": "srch_a1b2c3d4e5f6"
}
{
"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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| search_id | string | required | Search ID from a search_hotels response. Must not be expired. |
| hotel_id | string | required | Hotel ID from the same search_hotels response |
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.
{
"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).
| Parameter | Type | Required | Description |
|---|---|---|---|
| rate_plan_id | string | required | Rate plan ID from a get_rates response. Must not be expired. |
| guest.first_name | string | required | Guest's first name as it appears on their ID |
| guest.last_name | string | required | Guest's last name |
| guest.email | string | required | Guest email — confirmation is sent here by the hotel |
| idempotency_key | string | required | Unique string per booking attempt. Prevents duplicate bookings on retry. Generate a UUID per attempt. |
| accept_rate_change | boolean | optional | Set true on resubmit after receiving a rate_changed response. Requires a new idempotency_key. |
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.
{
"rate_plan_id": "rp_f8e2a1b3",
"guest": {
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]"
},
"idempotency_key": "550e8400-e29b-41d4-a716-446655440000"
}
{
"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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| booking_id | string | required | Booking ID returned from a book_hotel response |
{
"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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| booking_id | string | required | Booking ID to cancel |
| reason | string | optional | Cancellation reason. Stored for your records. |
{
"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.
No parameters required. Returns the complete list of tools with descriptions and parameter schemas.
{
"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.
| HTTP | Error Code | When / What to Do |
|---|---|---|
| 200 | — | Idempotency hit — same key, booking already created. Returns original booking. |
| 201 | — | Booking created successfully. |
| 400 | invalid_request | Missing or invalid fields. Check guest data and required params. |
| 400 | cache_id_invalid | rate_plan_id doesn't exist. Did you use an ID from a different search? |
| 401 | unauthorized | API key missing, invalid, or revoked. |
| 409 | rate_changed | Rate changed between search and booking. See Rate Change Handling. |
| 410 | rate_unavailable | Room/rate no longer available. Search again. |
| 410 | cache_expired | Rate hold expired (>15 min). Call get_rates again. |
| 429 | budget_exceeded | Daily search or booking budget exhausted. Resets midnight UTC. |
| 500 | booking_failed | Upstream 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.
{
"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
| Scenario | Suggested 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 |
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.
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.
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.