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:
| Parameter | Type | Description |
|---|---|---|
criteria | object | Search criteria (required) |
settings | object | Request settings (required) |
Search Criteria
| Parameter | Type | Description |
|---|---|---|
checkIn | string | Check-in date and time (ISO 8601, required) |
checkOut | string | Check-out date and time (ISO 8601, required) |
occupancies | array | Room occupancies with paxes (required) |
hotels | array | Optional: Specific hotel codes to search |
currency | string | Currency code (ISO 4217) |
language | string | Response language code (ISO 639-1) |
nationality | string | Guest nationality code |
exclusions | array | Exclusion filters |
businessRules | object | Business 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:
| Parameter | Type | Description |
|---|---|---|
skipMarkup | string | When "true", skip margin calculation. Suggested price equals net price. |
skipHotelCodesMapping | string | When "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"
}
}
}
- If a connection has no markup configured,
markupGrossequalsnetand aNO_MARKUP_CONFIGUREDwarning is returned. - Use
skipMarkup: "true"when markup is handled externally or for testing purposes. - The
markupGrossfield contains the final selling price with your markup applied. - Use
marginAmountandmarginPercentto analyze applied margins. - Markup Priority: When multiple markups exist, only the highest priority one is applied (based on
priorityfield, 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:
- Parallel Queries - All providers queried simultaneously
- Deduplication - Same hotel from different providers is consolidated
- Ranking - Business rules determine best options
- 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 Type | Description |
|---|---|
PARTIAL_RESPONSE | One or more providers failed or timed out |
UNMAPPED_HOTEL | Hotel content not fully mapped |
NO_MARKUP_CONFIGURED | Connection has no markup; markupGross = net price |
Code Examples
- cURL
- JavaScript
- Python
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"]
}
}'
const response = await fetch('https://api.bundleport.com/connect/hotels/v1/availability', {
method: 'POST',
headers: {
'Authorization': 'ApiKey YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
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'],
},
}),
});
const data = await response.json();
console.log(`Found ${data.options.length} options`);
import requests
url = "https://api.bundleport.com/connect/hotels/v1/availability"
headers = {
"Authorization": "ApiKey YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"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"]
}
}
response = requests.post(url, json=payload, headers=headers)
data = response.json()
print(f"Found {len(data['options'])} options")
Next Steps
- Quote an Option - Revalidate pricing before booking
- Create a Booking - Book the selected option
- Content API - Access hotel catalog data