Skip to main content

Search for Hotels

The search endpoint allows you to find available hotels matching your criteria. It queries multiple suppliers in parallel and returns normalized results.

Endpoint

POST /hotels/v1/search

Request

Required Parameters

ParameterTypeDescription
stayobjectCheck-in and check-out dates
destinationobjectDestination (code or coordinates)
occupanciesarrayGuest configuration per room

Optional Parameters

ParameterTypeDescription
filtersobjectSearch filters (currency, price range, etc.)
settingsobjectRequest settings (accessIds, timeout, etc.)

Example Request

{
"stay": {
"checkIn": "2025-06-15",
"checkOut": "2025-06-17"
},
"destination": {
"code": "BCN"
},
"occupancies": [
{
"adults": 2,
"children": 1,
"childrenAges": [8]
}
],
"filters": {
"currency": "EUR",
"nationality": "ES",
"minPrice": 50,
"maxPrice": 300,
"boardTypes": ["BB", "HB"]
},
"settings": {
"accessIds": ["ACCESS_1", "ACCESS_2"],
"timeout": 30000
}
}

Response

Success Response

{
"options": [
{
"optionRefId": "OPT-123456789",
"hotel": {
"code": "12345",
"name": "Example Hotel Barcelona",
"location": {
"city": "Barcelona",
"country": "ES"
}
},
"rooms": [
{
"description": "Standard Double Room",
"boardCode": "BB",
"price": {
"currency": "EUR",
"net": 150.00,
"suggested": 180.00
},
"cancelPolicy": {
"refundable": true,
"cancelPenalties": []
}
}
],
"price": {
"currency": "EUR",
"net": 150.00,
"suggested": 180.00,
"binding": false
}
}
],
"tracing": {
"status": "OK",
"accessSpans": [
{
"access": "ACCESS_1",
"status": "OK",
"hotelsRequested": 10,
"hotelsReturned": 8
}
]
},
"warnings": []
}

Key Fields

optionRefId

Unique identifier for this option. Use it to:

  • Quote the option (recheck pricing)
  • Book the option
  • Reference in subsequent operations

Important: optionRefId is only valid for a limited time (typically 4-5 minutes). Always quote immediately before booking.

Destination

You can specify destination in multiple ways:

// By code (IATA, city code, etc.)
{ "destination": { "code": "BCN" } }

// By coordinates
{ "destination": { "latitude": 41.3851, "longitude": 2.1734 } }

// By geohash
{ "destination": { "geohash": "sp3e3" } }

Occupancies

Define guest configuration:

{
"occupancies": [
{
"adults": 2,
"children": 0
},
{
"adults": 1,
"children": 2,
"childrenAges": [8, 10]
}
]
}

This example searches for:

  • Room 1: 2 adults
  • Room 2: 1 adult + 2 children (ages 8 and 10)

Filters

Common filters:

{
"filters": {
"currency": "EUR", // Response currency
"nationality": "ES", // Guest nationality
"minPrice": 50, // Minimum price
"maxPrice": 300, // Maximum price
"boardTypes": ["BB", "HB"], // Filter by board types
"hotelCodes": ["12345"], // Specific hotels
"starRating": [4, 5], // Star rating filter
"amenities": ["WIFI", "POOL"] // Required amenities
}
}

Settings

Request configuration:

{
"settings": {
"accessIds": ["ACCESS_1"], // Which suppliers to query
"timeout": 30000, // Request timeout (ms)
"requestId": "search-001", // Your request ID for tracing
"auditTransactions": false // Include raw supplier responses
}
}

Multi-Supplier Behavior

When multiple suppliers are configured:

  1. Parallel Queries - All suppliers queried simultaneously
  2. Deduplication - Same hotel from different suppliers is consolidated
  3. Ranking - Business rules determine best options
  4. Partial Results - If one supplier fails, others continue

Partial Failure Example

{
"options": [...], // Results from successful suppliers
"tracing": {
"status": "PARTIAL",
"accessSpans": [
{ "access": "ACCESS_1", "status": "OK", "hotelsReturned": 45 },
{ "access": "ACCESS_2", "status": "OK", "hotelsReturned": 32 },
{ "access": "ACCESS_3", "status": "ERROR", "errorCode": "TIMEOUT" }
]
},
"warnings": [
{ "code": "PARTIAL_RESPONSE", "description": "ACCESS_3 timeout" }
]
}

Best Practices

1. Always Quote Before Booking

optionRefId expires quickly. Always quote immediately before booking:

// ✅ Good
const search = await searchHotels(criteria);
const option = search.options[0];
const quote = await quoteOption(option.optionRefId);
const booking = await bookOption(option.optionRefId, travellerData);

// ❌ Bad - optionRefId may expire
const search = await searchHotels(criteria);
// ... user browses for 10 minutes ...
const booking = await bookOption(option.optionRefId, travellerData); // May fail

2. Use Appropriate Filters

Narrow down results to improve performance:

// ✅ Good - Specific filters
{
"filters": {
"currency": "EUR",
"minPrice": 100,
"maxPrice": 200,
"boardTypes": ["BB"]
}
}

// ❌ Bad - Too broad
{
"filters": {
"currency": "EUR"
// No price or board filters = too many results
}
}

3. Monitor Tracing Data

Use tracing to optimize supplier selection:

const result = await searchHotels(criteria);

result.tracing.accessSpans.forEach(span => {
console.log(`${span.access}: ${span.status}, ${span.hotelsReturned} hotels, ${span.processTime}ms`);

if (span.status === 'ERROR') {
// Log supplier issues
console.error(`Supplier ${span.access} failed: ${span.errorCode}`);
}
});

4. Handle Warnings

Warnings indicate data quality issues but don't prevent success:

if (result.warnings.length > 0) {
result.warnings.forEach(warning => {
if (warning.code === 'UNMAPPED_HOTEL') {
// Hotel exists but content not fully mapped
// Still usable, but may have limited information
}
});
}

Code Examples

curl -X POST https://api.bundleport.com/hotels/v1/search \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"stay": {
"checkIn": "2025-06-15",
"checkOut": "2025-06-17"
},
"destination": { "code": "BCN" },
"occupancies": [{ "adults": 2 }],
"filters": { "currency": "EUR" }
}'

Next Steps