Getting Started with the Xeni Activities APIHow to Book an ActivityHow to Browse Activity Tags and CategoriesHow to Cancel an Activity BookingHow to Check Activity AvailabilityHow to Get Activity DetailsHow to Retrieve Activity Booking DetailsHow to Search for Activities with FiltersHow to Search for Activity DestinationsCar Rental API - Getting StartedCar Rental API - Understanding Response FieldsHow to Book a Car RentalHow to Get Rental Car Details and Equipment Add-OnsHow to Retrieve or Cancel a Car Rental BookingHow to Search for Available Rental CarsHow to Search for Pickup LocationsHow to Use Car Rental Search FiltersDeals API Best Practices for IntegrationDeals API Frequently Asked QuestionsGetting Started with the Xeni Deals APIDeals API Request Parameters and Headers ReferenceDeals API Supported Currencies and LocalizationHow to Display Deals in Your ApplicationHow to Fetch Hotel Deals by LocationFlights API Error Codes and TroubleshootingGetting Started with the Xeni Flights APIHow to Book a FlightHow to Check Flight Availability and PricingHow to Confirm or Cancel a Flight BookingHow to Retrieve Fare Rules for a FlightHow to Retrieve Flight Booking DetailsHow to Search for Airports Using AutocompleteHow to Search for FlightsHow to Use Flight Search Filters, Sorting, and PaginationHow to Check Room Availability and PricingHow to Filter Vacation Rental ResultsHow to Get Resort Property Details, Amenities, and AccessibilityHow to Hold and Confirm a Resort BookingHow to Release a Resort HoldHow to Retrieve Resort Booking DetailsHow to Search for Available ResortsHow to Search for Resort DestinationsHow to Search for Vacation Rental LocationsHow to Search for Vacation RentalsHow to Use Resort Search Filters and SortingGetting Started with the Xeni Resorts APIResorts API: Understanding Booking Statuses and PoliciesGetting Started with the Vacation Rentals APIVacation Rentals Frequently Asked QuestionsVacation Rentals Supported Property TypesUnderstanding Async Search for Vacation RentalsAuthentication & API SignaturesBooking Hotels — Direct API & SSO CheckoutError Handling, Rate Limits & Best PracticesGetting Started with the Xeni Hotels APIManaging Bookings: Status, Retrieval & CancellationPricing Confirmation & Token LifecycleRetrieving Hotel Details & Room AvailabilitySearching for Hotels: Locations, Filters & PaginationSearching for HotelsSession Management & Correlation IDsAPI authentication and getting your API keys

How to Display Deals in Your Application

Last updated: 2026-03-03

How to Display Deals in Your Application

Once you are successfully fetching deals from the API, the next step is presenting them effectively in your application. This article covers practical strategies for geocoding, displaying deal cards, sorting results, and keeping deals fresh.

Step 1: Obtain the User's Location

The Deals API requires latitude and longitude coordinates. There are several ways to obtain these depending on your platform and use case.

Browser Geolocation API

The most direct approach for web applications. This uses the device's GPS or network-based location:

JAVASCRIPT
function getUserLocation() {
  return new Promise((resolve, reject) => {
    if (!navigator.geolocation) {
      reject(new Error('Geolocation is not supported'));
      return;
    }

navigator.geolocation.getCurrentPosition(
(position) => {
resolve({
lat: position.coords.latitude,
long: position.coords.longitude
});
},
(error) => {
// Fall back to IP-based geolocation or a default location
reject(error);
},
{ timeout: 10000, maximumAge: 300000 }
);
});
}

> Important: Geolocation requires user permission. Always have a fallback strategy for users who decline or are on browsers that do not support it.

IP-Based Geolocation Fallback

When GPS is unavailable, IP-based services provide approximate coordinates:

JAVASCRIPT
async function getLocationByIP() {
  const response = await fetch('https://ipapi.co/json/');
  const data = await response.json();
  return {
    lat: data.latitude,
    long: data.longitude,
    city: data.city
  };
}

City or Destination Search

For applications where users select a destination, use a geocoding service to convert the location name to coordinates:

JAVASCRIPT
async function geocodeCity(cityName) {
  // Use your preferred geocoding provider (Google Maps, Mapbox, etc.)
  const response = await fetch(
    https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(cityName)}.json?accesstoken=YOURTOKEN
  );
  const data = await response.json();
  const [lng, lat] = data.features[0].center;
  return { lat, long: lng };
}

Step 2: Build a Deal Card Component

A well-designed deal card should communicate value quickly. Here are the key elements to include:

Essential Card Elements

  • Property image — A large, high-quality hero image draws the eye and makes deals visually appealing.
  • Hotel name and star rating — Display prominently so users can quickly assess quality.
  • Location — City name or neighborhood, plus distance from the searched coordinates if available.
  • Original price — Show the reference price with a strikethrough to highlight the discount.
  • Deal price — The discounted price, displayed larger and in a contrasting color.
  • Discount badge — A percentage or "Save $X" badge in the corner of the card.
  • Deal validity — If the response includes date ranges, show when the deal expires or the travel window.

Example HTML Structure

HTML
<div class="deal-card">
  <div class="deal-image">
    <img src="{deal.imageurl}" alt="{deal.propertyname}" loading="lazy" />
    <span class="discount-badge">-{deal.discount_percentage}%</span>
  </div>
  <div class="deal-content">
    <div class="star-rating">{'★'.repeat(deal.star_rating)}</div>
    <h3 class="property-name">{deal.property_name}</h3>
    <p class="location">{deal.address}</p>
    <div class="pricing">
      <span class="original-price">${deal.original_price}</span>
      <span class="deal-price">${deal.deal_price}</span>
      <span class="per-night">per night</span>
    </div>
  </div>
</div>

Styling Tips

  • Use a grid layout (2-3 columns on desktop, single column on mobile) for deal cards.
  • Make the discount badge visually prominent — a colored pill or ribbon works well.
  • Use the strikethrough style on the original price to emphasize the savings.
  • Ensure images have a consistent aspect ratio (16:9 or 3:2) to keep the grid clean.

Step 3: Sort and Filter Deals

The API returns deals in its default order, but you may want to sort or filter them on the client side to match user preferences.

Sorting Options

JAVASCRIPT
function sortDeals(deals, sortBy) {
  const sorted = [...deals];

switch (sortBy) {
case 'discount':
// Highest discount first
return sorted.sort((a, b) => b.discountpercentage - a.discountpercentage);
case 'price-low':
// Lowest deal price first
return sorted.sort((a, b) => a.dealprice - b.dealprice);
case 'price-high':
// Highest deal price first
return sorted.sort((a, b) => b.dealprice - a.dealprice);
case 'rating':
// Highest star rating first
return sorted.sort((a, b) => b.starrating - a.starrating);
default:
return sorted;
}
}

Client-Side Filtering

You can also offer filters so users can narrow results:

  • Star rating — Filter by minimum star rating (e.g., 3+ stars, 4+ stars).
  • Price range — Slider or min/max inputs to filter by deal price.
  • Minimum discount — Only show deals above a certain percentage off.

Step 4: Handle Deal Expiry and Refresh

Deals are time-sensitive. Stale deals lead to a poor user experience when a user clicks through to book and finds the deal is no longer available.

Refresh Strategy

  • On page load — Always fetch fresh deals when the user navigates to a deals page.
  • Periodic refresh — If the user stays on the deals page, refresh every 5 to 10 minutes to pick up new deals and drop expired ones.
  • On user action — Refresh when the user changes location, currency, or sort preferences.
JAVASCRIPT
class DealRefresher {
  constructor(fetchFn, intervalMs = 5  60  1000) {
    this.fetchFn = fetchFn;
    this.intervalMs = intervalMs;
    this.timer = null;
  }

start() {
this.fetchFn(); // Initial fetch
this.timer = setInterval(() => this.fetchFn(), this.intervalMs);
}

stop() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}

refresh() {
this.stop();
this.start();
}
}

Handling Expired Deals

If deals include an expiry timestamp, you can proactively remove or dim expired deals in the UI without waiting for the next API call:

JAVASCRIPT
function isExpired(deal) {
  if (!deal.expires_at) return false;
  return new Date(deal.expires_at) < new Date();
}

function filterActiveDeals(deals) {
return deals.filter(deal => !isExpired(deal));
}

Step 5: Empty State and Loading

Loading State

Show skeleton cards or a spinner while deals are loading. Avoid showing an empty page that might be mistaken for "no deals available."

No Deals Available

Some locations may have no current deals. Handle this gracefully:

HTML
<div class="no-deals">
  <h3>No deals available near this location right now</h3>
  <p>Deals change frequently. Check back soon or try a different location.</p>
  <button onclick="showTopDestinations()">Browse Top Destinations</button>
</div>

Consider falling back to a top_destination=true query when a proximity-based search returns no results, so users always see something useful.

Putting It All Together

A typical integration flow looks like this:

  1. Detect or request the user's location.
  2. Call the Deals API with the coordinates and preferred currency.
  3. Render deal cards in a responsive grid layout.
  4. Provide sorting and filtering controls.
  5. Set up a periodic refresh to keep deals current.
  6. Handle empty states and loading indicators gracefully.

For guidance on caching and performance, see Best Practices for Integration.

Was this article helpful?