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

How a search runs

Each connectionCode in settings is queried in parallel. You still get a single response with merged options; use tracing.accessSpans (and optional auditData when debugging) to see per-supplier outcomes.

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). One entry per room; each lists all guests with ages. See Multi-room occupancies
hotelsarrayOptional: restrict search to specific hotel codes (often from Content API)
destinationobjectOptional: narrow by destination when supported (e.g. code, or latitude/longitude, or geohash). Exact shape may vary by deployment—confirm against API Reference
currencystringCurrency code (ISO 4217)
languagestringResponse language code (ISO 639-1)
nationalitystringGuest nationality code
exclusionsarrayExclusion filters
businessRulesobjectBusiness rules for search
additionalParamsobjectOptional flags such as skipMarkup, skipHotelCodesMapping (see below)

Request settings

ParameterTypeDescription
connectionCodesarrayRequired. Which configured connections to query in parallel
timeoutnumberRequest timeout hint in milliseconds; some suppliers require higher minimums for search/book
requestIdstringYour correlation id for logs and support
auditTransactionsbooleanWhen true, includes supplier-level trace data in auditData (use for debugging; redact in production)
testModebooleanWhen true, non-production handling where supported

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.

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 (search-focused)

optionRefId expires quickly—quoting and booking are covered in Quote and Book. On this page, focus on returning a manageable set of options: use hotels, destination (when available), and date ranges that match your UX.

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