Third Party Web Development using the Homhero API

Brad Vickers
Brad Vickers
  • Updated

This article is intended to be shared with third-party web developers who wish to use the Homhero API to connect their website with Homhero to act as a booking engine.

To create this type of connection a developer will need advanced knowledge of server-side scripting and experience integrating with APIs. If this type of resource is not available to you, contact the Homhero web team via websites@homhero.com.au to discuss in-house integration options.

The API

Homhero provides an API (Application Programming Interface) which enables the fast and efficient transmission of data between the Homhero system and your website. Please refer to our API DOCUMENTATION for a detailed breakdown of available endpoints and accepted parameters.

For a booking-engine website, you will primarily be concerned with only these 3 endpoints:

  • https://api.homhero.com.au/listings
  • https://api.homhero.com.au/listing/{slug}
  • https://api.homhero.com.au/pricing

 

Listings API

https://api.homhero.com.au/listings

This endpoint will return an ARRAY of all active listings, including the listing name, slug and ID. This information is useful to then loop through and perform a full-import using the https://api.homhero.com.au/listing/{slug} endpoint, using the slug info provided by this endpoint.

 

Single Listing API

https://api.homhero.com.au/listing/{slug}

Having generated an iterable array of listings with the above endpoint, or if your goal is to import/update the data for only a single listing to your website, use this endpoint. This will provide all the available non-pricing info that is available from the homhero system, including max guests, bedrooms, bathrooms, car spaces, features, categories, suburbs, regions, address, room configuration and images. These can then be imported as necessary to your website.

Please note - this endpoint is NOT designed to be called on page load, please do not attempt to dynamically load listing info of your site. Rather this info should be imported and recalled from your website database. Rate limiting may be applied to high-use cases of this endpoint. 

Locally saved listing info is best kept up-to-date by also employing WEBHOOKS, which will be covered further down.


Having employed these two listing endpoints, you should have all the listing data saved locally to your website available to be used how/where necessary. To perform an availability search, you will need to use the pricing endpoint.

Pricing Endpoint

https://api.homhero.com.au/pricing

The pricing endpoint in its simplest form accepts a checkin and checkout date (YYYY-MM-DD format) as parameters and returns and array of listings. 

eg. https://api.homhero.com.au/pricing?checkin=2026-12-21&checkout=2026-12-29

The array contains both AVAILABILITY information - the BOOL value "bookable", and PRICING information - an INT named "total"


Example output:

{   
    "success": true,
    "count": 50,
    "pricing": [
        {
            "bookable": false,
            "total": 0,
            "name": "1 Hope Island - no linen",
            "id": 68,
            "slug": "1-hope-island",
            "message": "N/A"
        },
        {
            "bookable": true,
            "total": 2760,
            "name": "2 Bedroom Hotel Rooms",
            "id": 120,
            "slug": "2-bedroom-hotel-rooms",
            "message": "$2760"
        }
    ]
}

There is also a value called "message" which will have various outputs based on the conditions

  • Listing is AVAILABLE with NO CONDITIONS ("bookable" = true) during the selected date range - the "message" will be the price, with the currency symbol prepended eg. "$1234"
  • Listing has AVAILABILITY during the selected date range, but is UNAVAILABLE ("bookable" = false) due to an imposed condition such as Min Nights / Max Nights / Closed to Arrivals / Closed to Departures - the "message" will be a simplified explanation of the reason that the listing eg. "Min 7" or "CTA". It may be helpful to pass these messages on to guests as often changing their search terms slightly can fix these.
  • Listing as NO AVAILABILITY (existing booking or stop-sold) on one or more date in the selected date range - the "message" will simply display "N/A". 

Pricing API modifiers

The above endpoint returns pricing info for ALL active listings in your homhero account. This may seem excessive if the search being performed has already been filtered by location or another factor, but the pricing endpoint is well optimised for speed so usually this does not matter. Some additional parameters can be applied though

&guests=INT - specify number of guests - some pricing may change based on the number of guests staying

&only_bookable=1 - only return results where "bookable" = true

&showPrices=true - shows prices even for results where "bookable" = false (default behaviour is to show 0 for price in these cases)

&ignore_booking_lead_time=true - Listings that don't meet the "Booking Lead Time" criteria are not included in search results by default (they are omitted altogether). Applying this setting will SHOW the listing in the API results. The "bookable" value will still be false though. If this setting is not included, and the Booking Lead Time criteria is not met, the API will show the "Booking Lead Time Message" rather than an array of results.

Terms Explained

"Closed to Arrivals" or "CTA" means that check-ins are not allowed on the selected check-in date.

"Closed to Departures" or "CTD" means that check-outs are not allowed on the selected check-out date.

"Booking Lead Time" + "Cut-off Time" are Homhero settings that can affect availability via the API. THink of htis as "last minute booking cut off". This is an account setting in Homhero that applies to all listings, but can be overridden per-listing. A booking lead time of 1 day + a cut-off time of 2:00pm means that a listing will show as available only up until 2:00pm, 1 day BEFORE the booking. After that, the listing is considered unavilable.


The above gives you the ability to import listing data and perform a multi-listing pricing and availability search on the website. Once a guest has been presented with a list of available properties, we can use some widgets to complete the online booking process.

Making a Booking + Helpful Widgets

Our typical presentation to guests, once they are looking at a single listing page, is to present them with a calendar widget that shows their selected dates and prices (or lets them select/modify these details). This has been designed to display best as a sidebar.

 

Single Listing Calendar Widget

This widget should be embedded as an iframe, and some parameters can be added to the iframe src to pre-fill the info that you may have already collected eg. checkin, checkout, guests

Example Embed script below - you will need to dynamically replace {{LISTING_SLUG}} and {{API_KEY}} with values relevant to the listing being viewed. Pre-selected dates should be passed through as &checkin=YYYY-MM-DD&checkout=YYYY-MM-DD.

Guest info can be passed through as &adult=X &child=Y &infant=Z  (these are optional, 2 adults is the default value)

Colours - the widget colours can be modified slightly with these three values

&pc=AAA123 - Primary Colour, value should be a hex colour code WITHOUT the # symbol - eg. &pc=0f6663

&btnclr=BBB456 - Button Colour - specificy a different button colour - if omitted, the button colour will inherit from the Primary colour

&bc=CCC789 - Background Colour - this is the background of the iframe, as opposed to the widget background, which is always white. Default widget background is #FFFFFF

&booking_path= controls the booking URL that is created and returned when the guest clicks "Book Now"

 

<iframe id="booking-frame" frameborder="0" height="1050" src="https://bookings.homhero.com.au/calendar.html?listing={{LISTING_SLUG}}&key={{API_KEY}}&booking_path=/booking" scrolling="yes"></iframe>
<script>
// addEventListener support for IE8
function bindEvent(element, eventName, eventHandler) {
    if (element.addEventListener) {
        element.addEventListener(eventName, eventHandler, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + eventName, eventHandler);
    }
}
// Listen to message from child window
bindEvent(window, 'message', function(e) {
    var args;
    if (typeof e.data == 'string' && e.data != "" && e.data.charAt(0) == '{') {
        args = JSON.parse(e.data);
    }
    if (args && args.vars) {
        switch(args.method){
            case "updateIframeHeight":
                // update height of iframe to accommodate change in content size.
                eval(args.method + "(args.vars.newHeight)");
                break;

            case "calendarPricingEvent":
                // This event fires each time the price is recalculated - Could be used for analytics. args.vars will contain relevant information
                break;
            default: 
                if(args.vars.url){
                  // Default behaviour if a URL is passed through is to redirect to that URL
                  var urlParams = args.vars.url;
                  if(urlParams.substring(0,5) == '/http'){
                    var local = urlParams.substring(1);
                  } else {
                    var local = location.protocol + "//" + location.hostname + urlParams;
                  }
                  top.location.href = local;
                }
                break;
        }
    }
});
function updateIframeHeight(newHeight) {
  console.log('updateIframeHeight: ' + newHeight + 'px');
  document.getElementById('booking-frame').style.height = newHeight + 'px';      
}

</script>

 

Booking Page

When the guest clicks "Book Now" on the calendar icon, they should be redirected to the booking path specified, with the relevant parameters such as checkin, checkout, adult, child etc passed through from the Calendar Widget.

Typically this will create a Booking Page URL that looks something like this

https://example.com/booking/?listing=demo-gs-park-royale&checkin=2026-03-10&checkout=2026-03-14&adult=2&child=0&infant=0

The recommended usage here is to embed our booking widget on a single page and pass through these URL parameters into the Booking Widget iframe SRC.

The below code is for embedding the widget, just add the query parameters such as the listing, checkin, checkout where it says {{DYNAMICALLY_ADD_QUERY_PARAMETERS}}

<iframe id="booking-frame" frameborder="0" width="100%" height="1080" src="https://bookings.homhero.com.au/?key=2265ea2c-fb4d-4946-9ac6-2db3dd68a66f&pc=0f6663&btnclr=5bbdbb&bc=ffffff{{DYNAMICALLY_ADD_QUERY_PARAMETERS}}" scrolling="yes" style="width:100%, height:100vh;margin:0;padding:0;display:block;"></iframe>
<script type="text/javascript">
  function bindEvent(element, eventName, eventHandler) {
    if (element.addEventListener) {
      element.addEventListener(eventName, eventHandler, false);
    } else if (element.attachEvent) {
      element.attachEvent('on' + eventName, eventHandler);
    }
  }
  
  console.log('Load event');
  var iframeURL = URL.parse('<?php echo $iframe_url; ?>');
  var childObj = {};
  var parentObj = {};
  var receiver = document.getElementById('booking-frame').contentWindow;
  function sendMessage() {
    if (receiver) {
      receiver.postMessage(JSON.stringify(parentObj), '*');
    }
  }

  function onScroll() {
    parentObj.method = 'parentScroll';
    parentObj.vars = {};
    parentObj.vars.scrollHeight = document.body.scrollHeight;
    parentObj.vars.scrollTop = window.scrollY;
    parentObj.vars.documentHeight = document.documentElement.scrollHeight;
    parentObj.vars.windowHeight = window.innerHeight;
    sendMessage();
  }

  function receiveMessage(e) {

    if (iframeURL.origin && iframeURL.origin == e.origin && e.data != '') {
      // Only messages from the booking iframe should meet the above condition.
      try {
        var args = JSON.parse(e.data);
        if (!args.__postRobot__ && args.vars && args.method) {
          childObj = eval(args.vars);
          eval(args.method + "(childObj)");
        }
      } catch (e) {}
    }
  }

  function errorScroll() {
    var cal = childObj.bodyScrollHeight / window.document.body.scrollHeight;
    var pos = (childObj.scrollTop * cal) - 30;
    window.scrollTo({
      top: pos,
      behavior: 'instant'
    });
  }
  function updateIframeHeight(childObj) {
    console.log('updateIframeHeight: ' + childObj.newHeight + 'px');
    document.getElementById('booking-frame').style.height = childObj.newHeight + 'px';      
  }

  bindEvent(window, 'scroll', onScroll);
  bindEvent(window, 'message', receiveMessage);

  function bookingResponse(e){
    if(e.bookingResponse.success){
      // This Event fires upon successful purchase
      // Add Analytics Tracking Javascript below
      // ---- Useful Variables ----
      // e.bookingResponse.booking.id #Unique Booking ID (int)
      // e.bookingResponse.booking.source #Booking Source eg. "Website Direct" (string)
      // e.bookingResponse.booking.actual_rate #Price of tariff only (int)
      // e.bookingResponse.booking.surcharge #Booking Fees included in total (int)
      // e.bookingResponse.booking.total #Total price including fees/extras (int)
      // e.bookingResponse.booking.discount #Discount Amount (int)
      // e.bookingResponse.booking.deposit #Initial Deposit Payable (int)
      // e.bookingResponse.booking.deposit_paid #Initial Amount Paid (int)
      // e.bookingResponse.booking.bond_amount #Security Bond Required - payable prior to check-in, not included in total (int)
      // e.bookingResponse.booking.listing_name #Name of Listing (string)
      // e.bookingResponse.booking.listing_id #Unique ID of Listing (int)
      // e.bookingResponse.booking.nights #Number of Nights booked (int)
             
    }
  }

  function gtagEvent(e){
    // This Event fires upon widget load, and between booking steps. This could be used to send analytics events.
    if(e.event && e.data){
      switch(e.event){
        case 'purchase':
          // Usually we handle purchase tracking with the bookingResponse method which has more available data 
          break;
        case 'begin_checkout':
          setTimeout(function(){
            // Add To Cart tracking could go here
          }, 2000);
          setTimeout(function(){
              // Begin Checkout tracking could go here
          }, 3000);
        break;
        case 'add_shipping_info':
          // Add Shipping Info tracking could go here
          break;
        case 'add_payment_info':
          // Add Payment Info tracking could go here
          break;
      }

    }
  }

        
</script>

 

Webhooks

Pricing and availability data should be retrieved via the API whenever needed - this ensures this data will always be up-to-date. 

As mentioned previously though, listing data should be stored locally and the https://api.homhero.com.au/listing/{slug} endpoint should only be called when this data needs to be updated. We can use WEBHOOKS to send a message that a listing needs to be updated. Once received you can use the content of the webhook to update, but it may be easier to perform a full API sync for the given listing.

The webhook will send out a packet of JSON info any time one of the selected items is changed in Homhero

Recommended webhooks are LISTING (fires any time any listing details are updated) and LISTING IMAGES (every time image order is updated).

Webhooks are configured in the Homhero Web interface under Settings > Third Party Services > Webhooks

Click ADD WEBHOOK to create a new Webhook

Enter a URL for the webhook - you will need to configure a listener on your website and trigger further action once the webhook is received.

Type - recommend you make one for LISTING and another webhook for LISTING IMAGES

API Key - set your API key, or leave blank.

 

Example Webhook message output

{
	"body": {
		"account_listing_slug": "abcd",
		"apiKey": "********",
		"data": {
			"insert": {
				"active": 1,
				"description": "",
				"guests_included": 2,
				"guests_max": 6,
				"lock_record": 1,
				"name": "One Bedroom Villas",
				"rms_id": 1155,
				"short_description": "",
				"slug": "one-bedroom-villas"
			}
		},
		"listing_id": 157,
		"listing_slug": "one-bedroom-villas",
		"type": "listing"
	},
	"headers": {
		…},
	"method": "POST",
	"uri": "https://example.com/wp-admin/admin-ajax.php?action=homhero_sync"
}

Related to