Skip to main content

Search for Hotels

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

Endpoint

POST /connect/hotels/v1/availability

Request

Request Body Structure

The request body contains criteria and settings:

ParameterTypeDescription
criteriaobjectSearch criteria (required)
settingsobjectRequest settings (required)

Search Criteria

ParameterTypeDescription
checkInstringCheck-in date and time (ISO 8601, required)
checkOutstringCheck-out date and time (ISO 8601, required)
occupanciesarrayRoom occupancies with paxes (required)
hotelsarrayOptional: Specific hotel codes to search
currencystringCurrency code (ISO 4217)
languagestringResponse language code (ISO 639-1)
nationalitystringGuest nationality code
exclusionsarrayExclusion filters
businessRulesobjectBusiness rules for search

Example Request

{
"criteria": {
"checkIn": "2025-06-15T00:00:00Z",
"checkOut": "2025-06-17T00:00:00Z",
"occupancies": [
{
"paxes": [
{ "name": "John", "surname": "Doe", "age": 35 },
{ "name": "Jane", "surname": "Doe", "age": 33 },
{ "name": "Child", "surname": "Doe", "age": 8 }
]
}
],
"hotels": ["12345", "67890"],
"currency": "EUR",
"language": "en",
"nationality": "US"
},
"settings": {
"connectionCodes": ["testb-hbds-1876", "testb-hbds-1877"],
"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": "testb-hbds-1876",
"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 using paxes array:

{
"criteria": {
"occupancies": [
{
"paxes": [
{ "name": "John", "surname": "Doe", "age": 35 },
{ "name": "Jane", "surname": "Doe", "age": 33 }
]
},
{
"paxes": [
{ "name": "Adult", "surname": "Doe", "age": 40 },
{ "name": "Child1", "surname": "Doe", "age": 8 },
{ "name": "Child2", "surname": "Doe", "age": 10 }
]
}
]
}
}

This example searches for:

  • Room 1: 2 adults (ages 35 and 33)
  • Room 2: 1 adult (age 40) + 2 children (ages 8 and 10)

Search Criteria Options

Common criteria fields:

{
"criteria": {
"checkIn": "2025-06-15T00:00:00Z",
"checkOut": "2025-06-17T00:00:00Z",
"occupancies": [...],
"hotels": ["12345", "67890"], // Optional: specific hotel codes
"currency": "EUR", // Response currency
"language": "en", // Response language
"nationality": "US", // Guest nationality
"exclusions": [...], // Exclusion filters
"businessRules": {...}, // Business rules
"additionalParams": {...} // Additional parameters (see below)
}
}

Additional Parameters

The additionalParams object allows you to pass extra configuration:

ParameterTypeDescription
skipMarkupstringWhen "true", skip margin calculation. Suggested price equals net price.
skipHotelCodesMappingstringWhen "true", hotel codes are sent directly to providers without mapping.
{
"criteria": {
"checkIn": "2025-06-15T00:00:00Z",
"checkOut": "2025-06-17T00:00:00Z",
"occupancies": [...],
"additionalParams": {
"skipMarkup": "true"
}
}
}
Markup Behavior
  • If a connection has no markup configured, markupGross equals net and a NO_MARKUP_CONFIGURED warning is returned.
  • Use skipMarkup: "true" when markup is handled externally or for testing purposes.
  • The markupGross field contains the final selling price with your markup applied.
  • Use marginAmount and marginPercent to analyze applied margins.
  • Markup Priority: When multiple markups exist, only the highest priority one is applied (based on priority field, then creation date).
  • Configure one global markup per connection for consistent pricing.

Settings

Request configuration:

{
"settings": {
"connectionCodes": ["testb-hbds-1876"], // Which providers to query
"timeout": 30000, // Request timeout (ms)
"requestId": "search-001", // Your request ID for tracing
"auditTransactions": false // Include raw provider responses
}
}

Multi-Provider Behavior

When multiple providers are configured:

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

Partial Failure Example

{
"options": [...], // Results from successful providers
"tracing": {
"status": "PARTIAL",
"accessSpans": [
{ "access": "testb-hbds-1876", "status": "OK", "hotelsReturned": 45 },
{ "access": "testb-hbds-1877", "status": "OK", "hotelsReturned": 32 },
{ "access": "testb-hbds-1878", "status": "ERROR", "errorCode": "TIMEOUT" }
]
},
"warnings": [
{ "code": "PARTIAL_RESPONSE", "description": "testb-hbds-1878 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 criteria
{
"criteria": {
"checkIn": "2025-06-15T00:00:00Z",
"checkOut": "2025-06-17T00:00:00Z",
"occupancies": [...],
"hotels": ["12345", "67890"], // Specific hotels
"currency": "EUR",
"language": "en"
},
"settings": {
"connectionCodes": ["testb-hbds-1876"]
}
}

// ❌ Bad - Too broad
{
"criteria": {
"checkIn": "2025-06-15T00:00:00Z",
"checkOut": "2025-06-17T00:00:00Z",
"occupancies": [...],
"currency": "EUR"
// No hotel filter = searches all hotels = too many results
}
}

3. Monitor Tracing Data

Use tracing to optimize provider 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 provider issues
console.error(`Provider ${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
}

if (warning.additionalData?.warning_type === 'NO_MARKUP_CONFIGURED') {
// Connection has no markup configured
// Suggested price equals net price
console.log(`No markup for connection: ${warning.connectionCode}`);
}
});
}

Common Warning Types

Warning TypeDescription
PARTIAL_RESPONSEOne or more providers failed or timed out
UNMAPPED_HOTELHotel content not fully mapped
NO_MARKUP_CONFIGUREDConnection has no markup; markupGross = net price

Code Examples

curl -X POST https://api.bundleport.com/connect/hotels/v1/availability \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"criteria": {
"checkIn": "2025-06-15T00:00:00Z",
"checkOut": "2025-06-17T00:00:00Z",
"occupancies": [
{
"paxes": [
{"name": "John", "surname": "Doe", "age": 35},
{"name": "Jane", "surname": "Doe", "age": 33}
]
}
],
"currency": "EUR",
"language": "en",
"nationality": "US"
},
"settings": {
"connectionCodes": ["testb-hbds-1876"]
}
}'

Next Steps