Deals API Best Practices for Integration
This article covers production-ready guidance for integrating the Deals API into your application. Following these practices will help you deliver a fast, reliable deals experience while being a good API consumer.
Caching Strategies
Hotel deals change frequently, but they do not change on every request. Implementing a caching layer reduces latency, lowers API call volume, and provides a better user experience.
Recommended Cache Duration
A cache TTL (time-to-live) of 5 to 10 minutes strikes a good balance between freshness and efficiency. Deals are time-sensitive, but they do not update every second.
- 5 minutes — Suitable for high-traffic pages where users expect up-to-the-minute deals.
- 10 minutes — Appropriate for lower-traffic pages or background preloading.
Cache Key Design
Include all query parameters and relevant headers in your cache key to avoid serving stale or mismatched results:
deals:{lat}:{long}:{currency}:{top_destination}:{language}
Round coordinates to 2-3 decimal places in your cache key to increase cache hit rates. Two decimal places of latitude/longitude precision covers roughly 1.1 km, which is close enough for deal searches:
function buildCacheKey(lat, long, currency, topDestination, language) {
const roundedLat = lat.toFixed(2);
const roundedLong = long.toFixed(2);
return deals:${roundedLat}:${roundedLong}:${currency}:${topDestination}:${language};
}
Server-Side Cache
If your application has a backend, cache API responses there to benefit all users requesting deals for the same area:
const cache = new Map();
async function getDeals(lat, long, currency, options = {}) {
const key = buildCacheKey(lat, long, currency, options.topDestination, options.language);
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < 5 60 1000) {
return cached.data;
}
const data = await fetchDealsFromAPI(lat, long, currency, options);
cache.set(key, { data, timestamp: Date.now() });
return data;
}
For production systems, consider using Redis or Memcached instead of an in-memory cache.
Client-Side Cache
On the frontend, you can use session storage or an in-memory store to avoid redundant calls when users navigate back to the deals page:
function getCachedDeals(key) {
const raw = sessionStorage.getItem(key);
if (!raw) return null;
const { data, timestamp } = JSON.parse(raw);
if (Date.now() - timestamp > 5 60 1000) {
sessionStorage.removeItem(key);
return null;
}
return data;
}
Polling Frequency
If your application displays deals on a long-lived page (such as a dashboard or homepage), periodically refresh the data to keep it current.
Recommended Intervals
| Scenario | Interval |
|---|---|
| Active deals page (user viewing) | 5–10 minutes |
| Background tab | Pause polling |
| Homepage widget | 10–15 minutes |
| Mobile app (foreground) | 5–10 minutes |
| Mobile app (background) | Do not poll |
Pause When Not Visible
Avoid wasting API calls when the user is not looking at the page:
let pollTimer = null;
function startPolling(fetchFn, intervalMs) {
fetchFn();
pollTimer = setInterval(fetchFn, intervalMs);
}
function stopPolling() {
clearInterval(pollTimer);
pollTimer = null;
}
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
stopPolling();
} else {
startPolling(fetchDeals, 5 60 1000);
}
});
Error Handling
Robust error handling ensures your deals feature degrades gracefully rather than breaking the page.
HTTP Status Codes
| Status | Meaning | Action |
|---|---|---|
| 200 | Success | Parse and display the deals. |
| 400 | Bad Request | Check your parameters. Likely an invalid lat, long, or currency value. |
| 401 | Unauthorized | Verify your API credentials and authentication headers. |
| 404 | Not Found | Confirm the endpoint URL is correct. |
| 429 | Too Many Requests | You have exceeded the rate limit. Back off and retry after the indicated period. |
| 500 | Internal Server Error | Retry with exponential backoff. If persistent, contact Xeni support. |
| 503 | Service Unavailable | The API is temporarily down. Retry after a short delay. |
Retry with Exponential Backoff
For transient errors (429, 500, 503), implement exponential backoff:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return response.json();
}
if (response.status === 429 || response.status >= 500) {
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) 1000 + Math.random() 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
throw new Error(API returned ${response.status}: ${response.statusText});
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
Handling No Results
A successful response with an empty deals array is not an error. Handle it as a normal state in your UI:
- Show a friendly "No deals available" message.
- Suggest the user try a different location or check back later.
- Optionally, fall back to a
top_destination=truequery to display popular deals instead.
Rate Limiting
While the exact rate limits depend on your API plan, follow these general guidelines:
- Do not call the API on every keystroke or scroll event. Debounce location changes and user interactions.
- Use caching to serve repeated requests from your cache rather than hitting the API.
- Monitor your usage and set up alerts if you approach your rate limit threshold.
- Respect 429 responses by backing off as described above.
Combining Deals with Hotel Search
The Deals API is a discovery tool — it shows users what discounts are available. To complete a booking, you will typically need to use the Hotels API.
A recommended flow:
- Show deals — Use the Deals API to display attractive offers on your homepage or deals page.
- User selects a deal — When a user clicks on a deal, capture the property ID.
- Check availability — Call the Hotels API availability endpoint with the property ID, check-in/check-out dates, and guest count.
- Confirm pricing — Use the Hotels API price confirmation endpoint to lock in the rate.
- Complete booking — Proceed through the Hotels API booking flow.
This handoff from Deals to Hotels API gives users the best experience: they discover deals easily and then transition into a full booking flow with confirmed, real-time pricing.
Correlation and Session ID Best Practices
- x-correlation-id — Generate a new UUID for every API call. This makes it easy to trace individual requests in logs.
- x-session-id — Reuse the same UUID for all API calls within a single user session. This helps Xeni correlate requests from the same user journey.
- Log both IDs — Store both IDs in your application logs. If a user reports an issue, you can provide these to Xeni support for rapid debugging.
Performance Tips
- Lazy load deal images — Use
loading="lazy"on deal card images to avoid blocking the initial page render. - Preload deals for common locations — If your app serves a specific market, preload deals for major cities in that region.
- Use server-side rendering — For SEO and initial load performance, render deal cards on the server and hydrate on the client.
- Minimize layout shift — Reserve space for deal cards (e.g., using skeleton cards) so the page does not jump when data loads.