Get Property Availability
Get unified availability for a single property from multiple sources
/public/properties/{propertyId}/availabilitiesOverview
Returns availability data from the highest priority source (LiteAPI > Strapi) for a single property. Includes booking conflicts from pending payment intents and confirmed bookings to provide accurate real-time availability.
Authentication
No authentication required. This is a public endpoint.
Path Parameters
propertyIdpathstringrequiredProperty ID (internal numeric ID or document ID)
Query Parameters
dateFromquerystringStart date filter (YYYY-MM-DD format)
dateToquerystringEnd date filter (YYYY-MM-DD format)
checkBookingConflictsquerybooleandefault: trueWhether to check for booking conflicts (pending payment intents and confirmed bookings)
Request
curl -X GET "https://api.journey.com/v1/public/properties/abc123/availabilities?dateFrom=2025-01-15&dateTo=2025-03-15&checkBookingConflicts=true"Response
propertyIdstringrequiredProperty ID that was queried
sourcestringrequiredSource of the availability data
Options: liteapi, strapi, none
availabilitiesarrayrequiredAvailability data for each day
availability items
datestringrequiredDate in YYYY-MM-DD format
pricenumberrequiredNightly rate in USD
isAvailablebooleanrequiredWhether the date is available for booking
restrictionsobjectStay restrictions for this date
restrictions object
minNightsnumberrequiredMinimum nights required for stay starting on this date
maxNightsnumberMaximum nights allowed for stay starting on this date (null if unlimited)
closedForArrivalbooleanWhether arrivals are closed for this date
closedForDeparturebooleanWhether departures are closed for this date
lastSyncAtstringWhen the data was last synced (ISO timestamp, null if never synced)
bookingConflictsarrayrequiredBooking conflicts affecting availability
conflict items
checkInDatestringrequiredCheck-in date of the conflicting booking
checkOutDatestringrequiredCheck-out date of the conflicting booking
typestringrequiredType of booking conflict
Options: pending_payment_intent, confirmed_booking
expiresAtstringWhen the hold expires (only for pending payment intents)
metaobjectrequiredResponse metadata
meta object
resolvedIdsobjectrequiredResolved property IDs across all sources
resolved IDs
documentIdstringStrapi document ID (null if not found)
liteapiIdstringLiteAPI property ID (null if not found)
pmsIdstringPMS system ID (null if not found)
hostNamestringHost name identifier (null if not found)
sourcesCheckedstring[]requiredSources that were checked for availability data
Response
{
"propertyId": "abc123",
"source": "liteapi",
"availabilities": [
{
"date": "2025-01-15",
"price": 250.00,
"isAvailable": true,
"restrictions": {
"minNights": 2,
"maxNights": 14,
"closedForArrival": false,
"closedForDeparture": false
}
},
{
"date": "2025-01-16",
"price": 275.00,
"isAvailable": false,
"restrictions": {
"minNights": 2,
"maxNights": 14,
"closedForArrival": false,
"closedForDeparture": false
}
},
{
"date": "2025-01-17",
"price": 275.00,
"isAvailable": true,
"restrictions": {
"minNights": 1,
"maxNights": 14,
"closedForArrival": false,
"closedForDeparture": false
}
}
],
"lastSyncAt": "2025-01-14T08:30:00Z",
"bookingConflicts": [
{
"checkInDate": "2025-01-20",
"checkOutDate": "2025-01-25",
"type": "pending_payment_intent",
"expiresAt": "2025-01-15T10:30:00Z"
},
{
"checkInDate": "2025-02-01",
"checkOutDate": "2025-02-05",
"type": "confirmed_booking"
}
],
"meta": {
"resolvedIds": {
"documentId": "abc123def456ghi789jkl012",
"liteapiId": "HTL_12345",
"pmsId": "prop_001",
"hostName": "boutique-resort"
},
"sourcesChecked": ["liteapi", "strapi"]
}
}Use Cases
Availability Calendar
const AvailabilityCalendar = ({ propertyId, month, year }) => {
const [availability, setAvailability] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchAvailability = async () => {
const startDate = `${year}-${month.toString().padStart(2, '0')}-01`;
const endDate = new Date(year, month, 0).toISOString().split('T')[0];
try {
const data = await getPropertyAvailability(propertyId, {
dateFrom: startDate,
dateTo: endDate,
checkBookingConflicts: true
});
setAvailability(data);
} catch (error) {
console.error('Failed to fetch availability:', error);
} finally {
setLoading(false);
}
};
fetchAvailability();
}, [propertyId, month, year]);
if (loading) return <div>Loading availability...</div>;
if (!availability) return <div>No availability data</div>;
const renderDay = (date) => {
const dayData = availability.availabilities.find(a => a.date === date);
if (!dayData) return null;
const hasConflict = availability.bookingConflicts.some(conflict =>
date >= conflict.checkInDate && date < conflict.checkOutDate
);
return (
<div
className={`calendar-day ${dayData.isAvailable ? 'available' : 'unavailable'} ${hasConflict ? 'conflict' : ''}`}
title={`$${dayData.price} per night`}
>
<div className="date">{new Date(date).getDate()}</div>
<div className="price">${dayData.price}</div>
{dayData.restrictions.minNights > 1 && (
<div className="min-nights">{dayData.restrictions.minNights}n min</div>
)}
</div>
);
};
return (
<div className="availability-calendar">
<div className="source-info">
Data from: {availability.source}
{availability.lastSyncAt && (
<span>Last updated: {new Date(availability.lastSyncAt).toLocaleString()}</span>
)}
</div>
{/* Calendar grid rendering logic */}
<div className="calendar-grid">
{availability.availabilities.map(day => renderDay(day.date))}
</div>
{availability.bookingConflicts.length > 0 && (
<div className="conflicts-notice">
<h4>Booking Conflicts:</h4>
{availability.bookingConflicts.map((conflict, index) => (
<div key={index} className="conflict-item">
{conflict.checkInDate} to {conflict.checkOutDate}
({conflict.type})
{conflict.expiresAt && (
<span> - expires {new Date(conflict.expiresAt).toLocaleString()}</span>
)}
</div>
))}
</div>
)}
</div>
);
};Booking Validation
const validateBookingDates = (availability, checkIn, checkOut) => {
const checkInData = availability.availabilities.find(a => a.date === checkIn);
const stayDates = availability.availabilities.filter(a =>
a.date >= checkIn && a.date < checkOut
);
const validation = {
valid: true,
errors: [],
warnings: [],
totalCost: 0
};
// Check if check-in date exists and is available
if (!checkInData) {
validation.valid = false;
validation.errors.push('Check-in date not found in availability data');
return validation;
}
if (!checkInData.isAvailable) {
validation.valid = false;
validation.errors.push('Check-in date is not available');
}
if (checkInData.restrictions?.closedForArrival) {
validation.valid = false;
validation.errors.push('Check-in date is closed for arrivals');
}
// Check minimum nights requirement
const nights = stayDates.length;
if (checkInData.restrictions?.minNights > nights) {
validation.valid = false;
validation.errors.push(`Minimum ${checkInData.restrictions.minNights} nights required`);
}
// Check maximum nights requirement
if (checkInData.restrictions?.maxNights && nights > checkInData.restrictions.maxNights) {
validation.valid = false;
validation.errors.push(`Maximum ${checkInData.restrictions.maxNights} nights allowed`);
}
// Check all stay dates are available
const unavailableDates = stayDates.filter(day => !day.isAvailable);
if (unavailableDates.length > 0) {
validation.valid = false;
validation.errors.push(`Unavailable dates: ${unavailableDates.map(d => d.date).join(', ')}`);
}
// Check for booking conflicts
const conflicts = availability.bookingConflicts.filter(conflict => {
const conflictStart = new Date(conflict.checkInDate);
const conflictEnd = new Date(conflict.checkOutDate);
const stayStart = new Date(checkIn);
const stayEnd = new Date(checkOut);
return stayStart < conflictEnd && stayEnd > conflictStart;
});
if (conflicts.length > 0) {
const pendingConflicts = conflicts.filter(c => c.type === 'pending_payment_intent');
const confirmedConflicts = conflicts.filter(c => c.type === 'confirmed_booking');
if (confirmedConflicts.length > 0) {
validation.valid = false;
validation.errors.push('Dates conflict with confirmed booking');
}
if (pendingConflicts.length > 0) {
validation.warnings.push('Dates have pending payment intent (may expire soon)');
}
}
// Calculate total cost
validation.totalCost = stayDates.reduce((sum, day) => sum + day.price, 0);
return validation;
};Price Trend Analysis
const analyzePriceTrends = (availability) => {
const prices = availability.availabilities
.filter(day => day.isAvailable)
.map(day => ({ date: day.date, price: day.price }));
if (prices.length === 0) return null;
const minPrice = Math.min(...prices.map(p => p.price));
const maxPrice = Math.max(...prices.map(p => p.price));
const avgPrice = prices.reduce((sum, p) => sum + p.price, 0) / prices.length;
const bestValueDates = prices
.filter(p => p.price === minPrice)
.map(p => p.date);
const premiumDates = prices
.filter(p => p.price === maxPrice)
.map(p => p.date);
return {
minPrice,
maxPrice,
avgPrice: Math.round(avgPrice * 100) / 100,
bestValueDates,
premiumDates,
priceSpread: maxPrice - minPrice,
availability: {
total: availability.availabilities.length,
available: prices.length,
percentage: Math.round((prices.length / availability.availabilities.length) * 100)
}
};
};Data Sources
Source Priority
- LiteAPI: Real-time hotel inventory systems
- Strapi: Property management system data
- None: No availability data found
Source Characteristics
- LiteAPI: Real-time rates and availability, industry standard
- Strapi: Direct property data, may include unique inventory
- Conflict Checking: Cross-source booking conflict detection
Data Freshness
- Real-time: LiteAPI provides live data
- Batch Sync: Strapi data synced periodically
- Conflict Detection: Real-time checking against active bookings
Booking Conflicts
Conflict Types
- Pending Payment Intent: Temporary hold on inventory, expires automatically
- Confirmed Booking: Definite reservation, blocks availability
Conflict Resolution
const resolveConflicts = (conflicts) => {
const now = new Date();
return conflicts.map(conflict => {
if (conflict.type === 'pending_payment_intent' && conflict.expiresAt) {
const expiresAt = new Date(conflict.expiresAt);
const isExpired = now > expiresAt;
return {
...conflict,
isExpired,
remainingTime: isExpired ? 0 : Math.max(0, expiresAt - now)
};
}
return conflict;
});
};Related Endpoints
- Get Batch Availability - Multiple properties at once
- Get Property Rates - Detailed pricing information
- Property Search - Search with availability filtering