Real-time Listings Service

1. Introduction

Thank you for your interest in the Zoopla Property Group (ZPG) Real-time Listings Service.

ZPG operates a number of property websites; the principal ones being Zoopla and PrimeLocation. Our mission is to provide the most useful online resources to property consumers, and be the most effective partner to property professionals.

To achieve this, data about available properties needs to be transferred from estate agents' software systems to ZPG. Traditionally the data transfer involves exporting large volumes of data from a database, providing a comprehensive picture of the estate agent's properties at that time. This method has the benefit of being very easy to implement but it is also very slow (due to the amount of data involved) and inefficient (most of that data has not changed since the previous export). These limitations mean that it is usually only performed once a day, overnight.

The level of service that users and estate agents now expect means that the daily bulk transfer of data is no longer suitable; the ZPG Real-time Listings Service is its replacement. It allows estate agents to make changes on their system and have those changes reflected on ZPG's websites within minutes. It achieves this by reducing the amount of data that needs to be transferred: only information about listings which have actually changed needs to be transmitted (i.e. updates are incremental).

What follows is the technical specification of the ZPG Real-time Listings Service so that anyone who wishes to integrate it with their systems may do so.

2. Overview

The ZPG Real-time Listings Service is designed to interact with a central data provider. Estate agents manage their properties using either a cloud-based property management solution or locally installed software which logs changes to a central data system. As changes are registered on this central system it sends messages to the ZPG Real-time Listings Service, in order to update ZPG's version of that data, which is displayed on its websites.

2.1 Simple technical overview

The ZPG Real-time Listings Service uses a fairly conventional web service design. Clients communicate with the service using standard HTTP over a secure connection (HTTPS). Each method (action) that the service provides has its own dedicated URL. The data required for the method is sent in a JSON formatted message.

The service runs in two environments: sandbox and live. The sandbox environment is used for testing your system's messaging interactions; whilst the live environment is for data you intend to be published to our websites. Initially we will provide you with access to the sandbox environment and once you are happy with your implementation you will be granted access to the live environment.

There are two principal entities which are tracked: branches and listings. A branch can have many listings associated with it but a listing can only belong to one branch. Whenever a change to a branch or listing is registered by your system, its full details should be sent to us. We will then replace our record entirely with your new version.

The media content for a listing (e.g.: images; brochures) is not transferred directly via the service. Instead, the content should be hosted on a web server and associated URLs included in a listing/update message. We will then download that content from your web server.

2.2 Simple workflow

The ZPG Real-time Listings Service is used in the following way:

When information about a branch changes, you send a branch/update message to replace the data we have.

When information about a listing changes, you send a listing/update message to replace the data we have. When sending this message you also include some metadata (ZPG-Listing-ETag) which will enable you to detect data synchronisation issues at a later date.

In order to check that the listing data for a branch has been synchronised properly, you can send a listing/list message. This will return information about all of the branch's listings, including the ZPG-Listing-ETags that were previously sent. You can then ascertain if there are any data differences between ZPG's system and yours, and correct any issues by sending listing/update and listing/delete messages as appropriate.

3. Security and authentication

There are two aspects to security: privacy and identity. By using HTTPS we can guarantee both: communications are encrypted (privacy) and both parties are assured that they are communicating directly with the person/system they expect (identity). HTTPS uses standard public-private key-pair cryptography (messages that are encrypted with one key can only be decrypted with the other key) which is wrapped up in an identity exchange mechanism called X.509 Certificates. The set-up process is:

You create a public-private key-pair. The private key is integral to proving your identity; it should never leave your corporate network, nor be given to any third-party, including ZPG. The public key, as its name suggests, can be given to anyone.

The public key, plus details about your corporate identity (name) are combined to generate a certificate signing request file. (Since anyone can perform steps 1 and 2, this cannot be used as a certificate on its own; it needs to be verified and signed by a certificate authority. In this case, ZPG will be acting as the certificate authority.)

You send your certificate signing request file (in PEM format) to ZPG via our certificate signing request submission page. This page will check that it is both formatted correctly and does not use encryption settings which are no longer secure. Upon acceptance of your certificate signing request, you will be provided with a verification token (which looks like "XXXX-XXXX-XXXX") that you should write down and keep safe.

ZPG will contact you and verbally confirm that you sent us a certificate signing request and ask for your verification token.

Having confirmed your identity and your request, ZPG will sign the certificate signing request and return the resulting signed certificate to you.

You will need to configure your software so that it can use the signed certificate and private key when contacting our service, so that both systems can authenticate each other's identity and encrypt communications.

How you generate a public-private key-pair and associated certificate signing request will be dependent on your particular software platform. For reference, this is how you might do it on Linux using openssl:

1. Create the public-private key-pair.

openssl genrsa -out private.pem 2048

This generates a key-pair using the RSA algorithm with keys that are 2048 bits long, which is then output to the file private.pem. Note that this file includes everything about the generated key-pair and the public key can be produced from the information it contains. Reminder: private.pem should never leave your corporate network.

We suggest that when you create your key-pair you do not specify an accompanying password. Otherwise you'll have to provide that password (to decrypt the file) every time your system is restarted. The lack of a password will not affect the security of the connection itself. The example given here does not set a password.

If you are interested, you can look at the contents of private.pem:

openssl rsa -in private.pem -text

2. Create a certificate signing request.

openssl req -new -sha256 -key private.pem -out public.csr

This creates a new certificate signing request, using the SHA-256 cryptographic hash algorithm, which includes the public key component contained in private.pem. You will be prompted to enter details about your company:

3.2 Example: testing authentication using Curl on Linux

How you configure your system to use the private key and signed certificate to establish an HTTPS connection will be dependent on your particular software platform. Consequently our ability to advise you on how to troubleshoot authentication issues is limited. However, if you use Linux you can test that the private key and signed certificate work together correctly by:

3.3 Certificate revocation

If at any point you believe that your private key may have been compromised (seen by anyone outside your company) then you should inform ZPG immediately so that we can revoke the associated signed certificate, thus preventing anyone from masquerading as you. Then, repeat the above procedure to generate a new private key and obtain a new signed certificate. You can confirm that your previous signed certificate has been revoked by consulting our Certificate Revocation List.

3.4 Supported TLS versions

The ZPG Real-time Listings Service only accepts secure connections using the TLS 1.2 protocol.

Since security is so important it is worth explaining why this is the case. As previously discussed, security is composed of two parts: identity and privacy. Whilst the data being sent to the Real-time Listings Service is not particularly sensitive (privacy), being assured that the person sending the data is actually who they claim to be (identity) is paramount: no-one should be able to read/change data except its legitimate owner.

(ZPG also expects to launch other services in future. Those services may be dealing with data that is sensitive and should therefore be transmitted in a suitable manner. The Real-time Listings Service is the first of these services, and so we have taken the opportunity to establish best practice standards at the outset, so that partners are familiarised with our security requirements, leading to fewer suprises or problems in future.)

There are a number of industry-standard secure communication protocols - many of which have become obsolete due to flaws in their design:

SSL 1.0, 2.0, 3.0 are all insecure. There are known exploits and no-one should be using them any more.

TLS 1.0 is insecure. There are known exploits and no-one should be using it any more.

TLS 1.1 is believed to be secure but is not the latest version.

TLS 1.2 is believed to be secure and is the latest version, with support for additional, stronger cipher algorithms than TLS 1.1.

After reviewing library support for TLS across programming languages and operating systems, we determined that, in the vast majority of cases, if TLS 1.1 was supported then TLS 1.2 was too. We therefore feel comfortable that by restricting access to TLS 1.2 we are following best practice guidelines, should not cause undue disruption, and is in the interest of all users of our services.

4. Messaging

The ZPG Real-time Listings Service uses JSON as its messaging format. JSON has a simple structural syntax; is easy to read; is familiar to software developers; and has excellent library support across many software platforms. We have no plans to support XML.

(Note that in JSON nomenclature an object's attributes are called "properties", which is somewhat unfortunate given the industry we operate in. In order to remove any possibility of ambiguity, this documentation refers to "attributes" instead and the only use of the word "properties" in our schemas is as required by the JSON standard, not as the name of a data attribute.)

For the API method you wish to call, you should construct a JSON message which conforms to the schema definition for that method. We recommend you perform a schema validation check before sending us the message so that you can detect and respond to any validation errors earlier in your messaging process. Our service will always validate your message against the associated schema and reject those that fail.

The JSON message would then be sent to the method's URL endpoint using an HTTP POST. Having received your message the service will respond with a standard HTTP response status code and provide additional information in the response content, formatted in JSON.

Reminder: your software will need to have been configured so that it can access both the signed certificate and private key, discussed in 3. Security and authentication, when you contact either of these environments. This is so that the secure HTTPS connection can be established.

Please note that the endpoints reference the major component of the API version number you wish to use, whilst the JSON schemas also include minor version components, which must be specified in the Content-Type profile.

(You will initially be given access to the sandbox environment to test your system integration. Data sent here will not affect ZPG's websites. When you are happy that your system integration is complete, you will need to contact us to gain access to the live environment. At that time we will then discuss the best way to perform an initial upload of all your branch and listing data, as well as how to migrate any existing clients who currently use an FTP feed.)

Note that some schema versions may not be available in the live environment; for example, during the development and testing of new versions, or because an old version has been retired. Please refer to our current message schemas to see which environments support the API version documented here.

4.2 HTTP request

The JSON message should be sent to the URL endpoint for the method you wish to use, via a POST.

4.3 HTTP headers

4.3.1 Content-Type

The Content-Type should be set to application/json, along with a profile definition, which is the URL of the specific version of the message schema you are conforming to. e.g.:

4.3.2 ZPG-Listing-ETag (only required for listing/update)

ZPG-Listing-ETag is the mechanism for ensuring that the listings on your system and ours are synchronised. Whenever you send us a listing/update message we will replace the data we have with the new version. At a later date you may wish to check whether the data we have is indeed the same as yours. Rather than sending the entire message back to you - which would then require you to compare all the individual attributes to find differences - we instead ask that you provide us with metadata that will allow your system to quickly determine whether the listing data we have is synchronised with yours. This metadata is sent via the ZPG-Listing-ETag header with each listing/update message, which can later be retrieved via the listing/list method.

The ZPG-Listing-ETag value is a string that you define (up to 255 characters in length) that indicates the overall state of the listing, as represented by the listing/update message. Its value should:

remain constant for a given listing/update message

change when data within the listing/update message is modified

be unlikely to be duplicated in future

i.e. if something changes on your system that would change the listing/update message you would send to us, then the accompanying ZPG-Listing-ETag should change to reflect that.

The ZPG-Listing-ETag you provide will be returned by the listing/list method. By comparing its value with the one your system would produce now, you can determine whether it is necessary to refresh the listing on our system. Please see 10.3 Periodically check synchronisation for additional information about this process.

5. Methods

The service supports the following methods:

branch/update: create or update information about a branch (e.g.: their name; address).

listing/update: create or update information about a listing, associated with a branch (e.g.: description; price).

5.1 branch/update method (optional)

This method allows you to describe a branch, to which listings are then associated. The address and other contact details allow us to identify the equivalent branch on our system and map your branch_reference to ours.

Note that this method is optional. If you do not use it then the branch information will need to be transferred to ZPG in some other way (typically via an emailed CSV spreadsheet), although this will likely delay the integration of branches with our system.

Message attribute

Requirements

Type

Description

Example

branch_reference

Mandatory

string

Your unique identifier for the branch. (We do not generally issue these as it should be the identifier that is native to your system. This makes debugging easier for you and means you do not need to wait for any additional information from us before you can start uploading listing data for a branch.)

"1234"; "kd-789d"

branch_name

Mandatory

string

The name of the branch. This is usually the name of the company and may also include some location information in order to differentiate it from other branches in the company.

The remaining attributes are all optional. However, all the additional information you can provide improves our ability to successfully match the listing to a user's search. If you do not have any data for a particular attribute, then do not include it in your message - do not default it. This will make it less likely for the message to fail validation rules which use the attribute and you won't accidentally introduce meaning (and consequence) where there was none before.

There are three attributes which define how we will interpret the listings's data:

location:country_code: "Where is it?" (This can be further reduced to: "Is this a UK or overseas listing?")

category: "Is this listing for a property which is residential (people will live in it) or commercial (a place of business)?"

Attributes which are nonsensical when considered in the above context will be ignored (for example: council_tax_band will be ignored if the location:country_code indicates an overseas (non-UK) listing).

Message attribute

Requirements

Type

Description

Example

accessibility

boolean

Whether the property includes accessibility features for disabled people. We recommend that the accessibility improvements be described in more detail via feature_list or detailed_description.

The number of vacant bedrooms being offered in shared accommodation. Note that if supplying this attribute, shared_accommodationmust be present and true, although the relationship is asymmetric. See shared_accommodation for more details.

All values

The grade assigned to the property should it be listed as a building of special architectural or historic interest.

"grade_a"

All values

category_a

category_b

category_c

grade_a

grade_b

grade_b_plus

grade_one

grade_two

grade_two_star

locally_listed

listing_reference

Mandatory

string

Your unique identifier for the listing. This should be unique across your entire system, not just for its associated branch (i.e. this is the PRIMARY KEY). If your identifiers are based on those submitted by the agent themselves then you may wish to concatenate, with an unambiguous separator, your branch reference and their listing reference to ensure uniqueness (e.g. "branch_id|agent_listing_id").

Whether the listing is for shared accommodation (i.e. the listing is for a room, or rooms, within an existing house/flatshare). If this attribute is true, we recommend also supplying the available_bedrooms attribute.

All values

withdrawn

offer_accepted

exchanged

completed

let

5.4 listing/list method

Because of the incremental nature of the service it is possible for the data that ZPG has to drift relative to yours (because of network problems or uncaught errors, for example). It is recommended that you periodically check the synchronisation of data between our system and yours. The listing/list method allows you retrieve a summary of the listing inventory for a branch and their state.

This is the string that you specified in the ZPG-Listing-ETag HTTP header when you last called listing/update, referring to listing_reference. If the value is not the same as the one you expect then there is a synchronisation issue and you should call listing/update again to refresh the data that ZPG has.

"18ab1f2e4c8ed62882fa57380c9b7b026413a663"

url

string

A link to the listing's detail page. (When using the sandbox environment this links to a preview page; when using the live environment it is a redirect to the detail page on Zoopla's website.)

6. Objects

If you include an attribute in your method call that uses an object to represent it, a valid object must be supplied. When discussing objects, a constituent attribute described as being mandatory is with reference to how to produce a valid object; it does not imply that the object, nor the attribute that uses that object, is mandatory when calling a particular method. Please refer to the method itself to see whether a particular attribute is mandatory.

6.1 area object

This describes a single area. It is a constituent of a number of other objects. This should not be confused with the areas object.

6.3 content object

A content object specifies a single piece of media content associated with the listing, such as an image or brochure. The listing/update:content attribute is an array of these content objects, which will be displayed in the order provided. Please see 9. Retrieval of media content for more information about how the content will be retrieved from the specified URLs.

All values

A short description of the content. This will be displayed alongside the content or, where appropriate, used as the hyperlink text.

"The entrance hall."

6.4 coordinates object

This object describes the geographic location of a point on the Earth's surface using the latitude/longitude system. We recommend you store latitude/longitude with a precision of at least 5 decimal places, which allows for locating a point to within approximately 1 metre.

6.5 description object

The description is expressed as an array of description objects, each of which represent a paragraph or section. For additional information about how to use this object, including examples, please see section 8. Structuring of detailed descriptions.

6.8 google_street_view object

Google Street View provides users with the ability to virtually explore outdoor areas. This is becoming increasingly valuable to users, who are able to, for example: plan routes to bus stops; see which services are provided by local shops.

The coordinates of the point where StreetView should be initially located.

heading

Mandatory

number

The angle of rotation to the right of the view, measured in degrees, relative to north. Also known as compass heading, or bearing.

0 (north); 74.5; 90 (east); 360 (north)

pitch

Mandatory

number

The angle of rotation up/down of the view, measured in degrees, relative to the horizon.

-90 (straight down); 5.4; 90 (straight up)

6.9 lease_expiry object

The expiry of leasehold tenure can be expressed as the actual expiry date or the number of years remaining on the lease.

Object attribute

Requirements

Type

Description

Example

expiry_date

Mandatory

string

The date when the lease expires. Note that this is a date-like string whose validation is not as restrictive as the datetime used for other attributes. It may be any of the following formats: YYYY-MM-DD; YYYY-MM; YYYY.

"2014-01-31"; "2014-01"; "2014"

or

Object attribute

Requirements

Type

Description

Example

years_remaining

Mandatory

integer

The number of years remaining on the lease.

99

6.10 location object

Providing an accurate address and location for the property is incredibly important because a large number of existing and future features on our websites depend on it. For example: which council is responsible for the maintaining and taxing the local area; or average travel times between the property and other locations of interest to the user.

Object attribute

Requirements

Type

Description

Example

property_number_or_name

Best practice; mandatory when not providing street_name.

string

The public designation of the property.

15; "14A"; "Flat B, 41"; "The Wildings"

street_name

Best practice; mandatory when not providing property_number_or_name.

string

The name of the road on which the property is principally adjacent.

"Barker Road"; "Chestnut Street"

locality

string

The familiar name of the area as it is referred to by local residents. This is usually a traditional, historic name and may refer to an aspect of the area which has ceased to exist.

"Sutton Coldfield"; "North Beach"

town_or_city

Mandatory

string

The nearest large urban area to the property. This is usually included in the property's postal address and sometimes referred to as "post town".

Note that this does not imply that the property resides within the boundaries of this urban area (for example, it might be in an outlying village - which would be referenced via the locality).

"Birmingham"; "San Francisco"

county

string

The largest territorial area division within the country which the property resides in. (Synonymous with e.g.: province; principality.)

"West Midlands"; "California"

postal_code

Mandatory (UK)

string

The postal area code issued by the primary postal service in the country. For example, for the UK, this would be Royal Mail's postcode; for the US, the United States Postal Service's ZIP code.

Note that there is a hierarchical order to the address attributes, so in order of increasing size:

property_number_or_name

street_name

locality

town_or_city

county

country_code

If your software does not currently support the separation of the property_number_or_name from street_name (i.e. they are stored as a single string value; for example: "29 Acacia Road"), then please supply that string via street_name.

6.11 minimum_contract_length object

The rental_term attribute can be specified with a minimum_contract_length object or, if the contract length is not currently known, by an enum that roughly indicates its likely duration (e.g. "short_term").

Object attribute

Requirements

Type

Description

Example

minimum_length

Mandatory

number

The minimum duration of the contract.

6

units

Mandatory

enum

The units of measurement used.

"months"

All values

days

weeks

months

years

6.12 min_max_area object

The min_max_area object defines an area range. Where only one is specified, we will regard them both as having the same value - i.e. a fixed area.

6.13 paf_address object

Royal Mail's addressing scheme is known as Postcode Address File (PAF). Many third-party systems use this to facilitate the selection of an address from an initial postcode. Per Royal Mail's specification: "Each address on PAF® has an eight-digit number associated with it - the Address Key. This number in conjunction with the Organisation Key and Postcode Type identifies the address."

Object attribute

Requirements

Type

Description

Example

address_key

Mandatory

string

The 8-digit Address Key.

"02341509"

organisation_key

Mandatory

string

The 8-digit Organisation Key.

"00000000"; "00001150"

postcode_type

Mandatory

enum

The Postcode Type.

"L"; "S"

All values

L

S

Note that Royal Mail's UDPRN (see location:paf_udprn) is generally preferred to paf_address because it is more stable with respect to changes in property utilisation (e.g. commercial becoming residential).

6.14 price_per_unit_area object

This object describes a price which is provided as a function of floor area.

Object attribute

Requirements

Type

Description

Example

price

Mandatory

number

The per unit area price.

100.00

units

Mandatory

enum

The units of measurement used.

"sq_feet"

All values

sq_feet

sq_yards

sq_metres

acres

hectares

6.15 pricing object

The pricing object's structure is determined by whether its component attribute transaction_type indicates that the listing is for sale or rent. Please note that we do not currently support multiple pricing, nor multiple transaction_types, for a single listing. If you do have multiple pricing models, perhaps due to a range of incentive schemes being available, then you should supply the non-subsidised price in the pricing object and indicate available incentive plans via listing/update:buyer_incentives.

Object attribute

Requirements

Type

Description

Example

transaction_type

Mandatory

enum

The type of financial transaction for this listing.

"sale"

All values

rent

sale

currency_code

Mandatory

string

The ISO 4217 currency code for the stated price. Other monetary attributes (e.g. deposit) also use this as their currency.

"GBP"; "JPY"

price

Mandatory (residential)

number

The non-subsidised price of the listing.

Note that the price should be quoted in its natural currency (i.e. the currency the vendor/landlord has specified), which is usually the official currency of the country that the property resides in. To be clear: this value should not be the result of a currency conversion.

The price quoted as a function of floor area. This is generally only used with commercial properties.

rent_frequency

Mandatory (rent)

enum

The frequency with which rent is required to be paid.

"per_week"

All values

per_person_per_week

per_week

per_month

per_quarter

per_year

price_qualifier

enum

Additional information about the price which requires emphasis, usually for legal reasons.

"guide_price" (auction); "fixed_price" (Scotland)

All values

fixed_price

from

guide_price

non_quoting

offers_in_the_region_of

offers_over

price_on_application

sale_by_tender

auction

boolean

Whether the sale will be transacted by an auction.

true

Because pricing is so important there are a number of schema-enforced dependencies for some of the pricing object's attributes. In particular:

When transaction_type = "rent", rent_frequencymust be specified.

When providing a price_per_unit_area you must also provide an areas attribute with an internal area.

price_qualifier = "non_quoting" is only valid for location:country_code = "gb" and category = "commercial". When stating price_qualifier = "non_quoting" then you must not provide pricing/pricenorpricing/price_per_unit_area.

Note that you do not have to supply both price and price_per_unit_area, although that is allowed. Do not calculate one price attribute from the other.

6.16 service_charge object

This object describes any ongoing service charges which are applicable. Note that you can only specify one of per_unit_area_units or frequency.

Object attribute

Requirements

Type

Description

Example

charge

number

The service charge amount.

100

per_unit_area_units

enum

When the charge is a function of unit area (usually for commercial properties), the units of area measurement.

"sq_feet"

All values

sq_feet

sq_yards

sq_metres

acres

hectares

frequency

enum

When the charge is a fixed amount (usually for residential properties), the frequency with which the service charge is required to be paid.

"per_year"

All values

per_person_per_week

per_week

per_month

per_quarter

per_year

6.17 tenant_eligibility object

This allows you to specify the eligibility of various classifications of applicant for a tenancy. Note that you do not have to specify eligibility for all classifications.

All values

All values

7. Responses

After processing your message we will send you a response that includes a standard HTTP status code and a JSON object in the content body that provides more detailed feedback.

7.1 Success

A response with a 200 OK HTTP status code indicates a successfully processed message. For convenience the JSON response object includes the primary identifiers provided in the original message. The methods respond as follows:

7.2 Errors

If there was a problem with the message then the response will include an HTTP status code in the 4xx range. The accompanying JSON response object will include more detailed information about the error to help you diagnose and fix it.

An error response will always include the following attributes:

Response attribute

Type

Description

error_name

string

A unique string identifier for the error.

error_advice

string

A description of the error and some advice as to how to fix it.

If there are problems parsing your message as JSON, there will also be:

Response attribute

Type

Description

request_content

string

The content body of your request (which should have been a JSON object).

json_validation

string

The reason(s) why request_content could not be parsed as a JSON object.

If there are problems determining which schema or method should be used, there will also be:

Response attribute

Type

Description

method

string

The method your message was sent to.

profile

string

The JSON schema you declared your message would conform to.

If there are problems validating the message against its associated JSON schema, there will also be:

Response attribute

Type

Description

errors

array of JSON objects

The errors that were generated when validating your message against the schema.

For example, if you were to send a correctly formatted message to the incorrect method endpoint you would receive:

{
"error_name" : "schema_method_mismatch",
"error_advice" : "The schema referenced in the Content-Type profile does not match the method endpoint where the message was sent. Please check that you are using the correct message schema and that the message is being sent to the correct endpoint."
"method" : "/sandbox/v1/branch/update",
"profile" : "http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/update.json"
}

7.2.1 Referencing of JSON attributes

The most common errors relate to problems with the JSON message itself (for example, invalid data or a missing mandatory attribute). The JSON validator will reference attributes of interest via a hierarchical path, with "#/" denoting the root level of the object:

7.2.2 JSON schema validation

The JSON validator's error messages should be self-explanatory when describing problems about an individual attribute and its dependencies.

The JSON validator will attempt to validate your message against the schema by interpreting the listing's data in all possible contexts. If it is unable to find a successful match then it will return all the errors it encountered. Recall that there are three principal attributes that are used to interpret the listing's data (#/location/country_code, #/category, #/pricing/transaction_type) so, naturally, there are some schema-enforced rules related to these attributes. If one of these attributes has a problem then it may trigger multiple errors (each produced by considering the message in a different context), which may make it appear as though there are more problems than there actually are, so we recommend you investigate errors for these attributes first. For example, consider the following pricing object:

The validator has attempted to interpret the message as a rent listing and discovered that the required rent_frequency attribute is missing. Having failed to validate as a rent listing, the validator then attempted to interpret the message as a sale listing instead. That failed too because the transaction_type is incorrect for a sale listing. In order to fix this error the sender would have to decide what the correct transaction_type was and then either change the transaction_type or provide the rent_frequency.

8. Structuring of detailed descriptions

Providing a comprehensive description of the listing is very important: it is the estate agent's primary opportunity to convince the user that this property is exactly what they are looking for. Our detail page is divided into the following sections:

Listing detail page layout

Features: a simple bulleted list of important or interesting aspects of the property. These are specified via listing/update:feature_list.

Detailed description: the topic for the rest of this section.

The description should provide an overview of the property and, if relevant, its constituent rooms. Presentation is also very important, from both structural and visual perspectives, as it allows the user to quickly find information about the property that they are specifically interested in. Unfortunately there is no standard as to how to structure descriptions, so our detailed_description attribute has been designed to be as flexible as possible, whilst providing a framework to encourage good practice.

The detailed_description is an array of description objects, each of which can have the following attributes:

heading

dimensions

text

These attributes populate a template with this structure:

heading (dimensions)

text

You must provide at least one of heading or text per object, whilst dimensions is optional (but contingent on the presence of a heading). The heading will be styled appropriately. By specifying a sequence of these objects, each using various combinations of header, dimensions and text, a wide variety of complex layouts can be achieved. A number of common scenarios are discussed below.

The text can include HTML. However, we only support a limited number of HTML tags in order to prevent unforeseen and undesirable interactions with our own HTML layout and styling. We currently only support:

<br> (line break)

<p> (paragraph)

<strong> or <b> (highlighted)

<em> or <i> (emphasised)

<u> (underlined)

<ul> and <li> (unordered list)

Note that any external links and contact details will be removed from the description text.

8.1 Example: a single block of text

If your system only stores a single block of descriptive text then you would use one object with a single text attribute:

Note that sections do not necessarily have to have a heading, in which case they will appear as paragraphs. A common use-case is to have an un-headed introductory paragraph, followed by headed sections:

8.3 Example: sections for individual rooms

The most common (residential) use-case is an overview paragraph followed by sections that describe individual rooms. This is exactly the same as the sectioned description discussed previously but with the addition of the dimensions attribute:

A large bedroom with lots of storage space and an outlook over the valley.

Bedroom (10' x 8')

Bedroom (9' x 9')

Kitchen

The kitchen was recently refitted.

Note that in the above example the dimensions of the kitchen are missing. This is permissible but not good practice when discussing individual rooms.

9. Retrieval of media content

A listing's content is not transferred to ZPG via the Real-time Listings Service directly. Instead the listing/update method includes a content attribute for you to specify a list of URLs from where that content can be obtained. It is likely that you already have a web server hosting this content, so this mechanism allows you to leverage your existing infrastructure to make the transfer of these assets more efficient. We support web servers using either HTTP or HTTPS.

Upon receipt of a listing/update message, we will request all the URLs specified, with appropriate HTTP headers to make the request conditional. So the first time a particular URL is mentioned we will download that content. For subsequent listing/update messages which mention that URL, we will modify our request to include the first of the following HTTP headers that we have an accompanying value for:

If-None-Match with the value from the HTTP ETag response header your web server returned when we previously requested the URL.

If-Modified-Since with the date from the HTTP Last-Modified response header your web server returned when we previously requested the URL.

In this way we will only download that content again when it changes. We recommend that your system support ETags in addition to last-modified dates. Whilst the generation of the ETag is not defined by the HTTP specification, many systems use a hashing algorithm (e.g.: MD5; SHA-1) since they are only generated from the binary data itself (so file system changes do not affect it), and the likelihood of two files producing the same hash is very small. This provides a high level of confidence that when the hash changes, the content is different; and vice versa.

Your web server probably behaves correctly when sent these headers already, particularly if you are using a commercial content delivery network. However, we nevertheless recommend you check its behaviour to be sure - especially since web browsers use the same mechanism to maintain their local data cache, so a misconfiguration could be enhancing your network traffic significantly.

9.1 Downloading your content

Our download process will identify itself as:

User-Agent: Zoopla Property Group automated download

If your content will be hosted on a private web server and therefore wish to use firewall rules to restrict access, please whitelist the following IPs:

31.221.120.234

52.16.166.50

52.18.106.77

52.18.124.138

54.77.228.44

54.171.72.173

54.171.126.99

54.229.57.122

54.229.75.214

9.2 MIME types

We only support web-safe formats for content. Acceptable MIME types are:

application/pdf

image/gif

image/jpeg

image/png

9.3 Images

Images can strongly influence a user's impression of a listing so it is important to provide good quality images which are representative of the property.

Please make available the original image (or the largest downscaled variant of the original if that is not possible). When resizing images for use on our websites and mobile apps, we only downscale because upscaling results in inferior quality images. Be careful not to accidentally submit URLs for image thumbnails.

The first image should ideally be of the exterior of the property.

We recommend including images for features and rooms which are highlighted in the description (e.g. a conservatory, or a recently remodelled kitchen).

Do not send agent logos or placeholder images. These will removed from our websites.

We recommend images have a size of at least 1280x960. Please note that we provide users access to original images and they should display larger than the default view on our listing detail page. Large images are particularly important for mobile devices when viewed full screen. For reference:

Mobile device

Screen resolution

iPhone 3 (and previous generations)

480 x 320

iPhone 4

960 x 640

iPhone 5 (all variants)

1136 x 640

iPhone 6

1334 x 750

iPhone 6 Plus

1920 x 1080

Google Nexus 5

1920 x 1080

HTC One

1920 x 1080

Samsung Galaxy 5

1920 x 1080

iPad Mini

1024 x 768

iPad Retina display

2048 x 1536

iPad Air

2048 x 1536

9.4 Floor plans

Floor plans are also of great interest to our users when attempting to visualise the property. It is important to provide high resolution images for floor plans because otherwise text can become illegible.

10. Workflow overview

This section describes the workflow for how the ZPG Real-time Listings Service should be used and how to ensure that the data ZPG has is synchronised with your own.

10.1 Initial upload

10.1.1 A new estate agent is added to your system

10.1.2 Upload listing data

For each added branch, find the set of listings that they are responsible for. For each listing, send a listing/update message.

Recall that listing/update requires that it be accompanied by a ZPG-Listing-ETag HTTP header. The value of ZPG-Listing-ETag should be an identifier on your system which allows you to accurately determine that when two such values are different, the overall state of the listing is also different. Depending on how you track changes to your data (and how it is represented in a listing/update message) an auto-incrementing revision counter, or possibly a last-modified date may be suitable. However, we would recommend using a checksum based on the listing/update message itself:

Canonicalise the JSON listing/update message. (This is the default for many JSON software libraries but some require you explicitly perform that action, or possibly set a configuration flag to enable it.) This process ensures that the message is represented in a consistent fashion, regardless of the order in which you populated attributes within the message.

Produce a checksum hash (e.g.: MD5; SHA-1) of the canonicalised JSON message. Store this value on your system and send it as the ZPG-Listing-ETag.

10.2 Incremental changes

10.2.1 Amendments are made to a listing on your system

Send a listing/update message which details the complete state of the updated listing.

10.2.2 A listing ceases to be valid

10.3 Periodically check synchronisation

Because of the incremental nature of the updating process, should a message be lost (e.g. due to a networking problem), then the data that ZPG has will be different to yours and will need correcting. We therefore recommend that you periodically, once a week say, check the synchronisation of data:

10.3.1 Ask about the state of a branch's listings on ZPG's system

For each branch you maintain, send a listing/list request. In the response, for each of the branch's listings, we will provide you with your listing_reference and the ZPG-Listing-ETag that we last received for that listing.

10.3.2 Add listings

If listing/list is missing a particular listing, use listing/update to create it.

10.3.3 Remove listings

If listing/list includes a listing that is no longer valid, use listing/delete to remove it.

10.3.4 Update listings

For each listing that is common to both systems, compare the value of the ZPG-Listing-ETag with that of the relevant field in your system:

If the values are identical then the data is the same on both systems and you do not need to do anything. This should be the normal case.

If the values are different then ZPG's data is stale and you should use listing/update to refresh it.

11. Integration

11.1 Transitioning from an existing bulk data feed

If you currently send ZPG a bulk data feed then it may be useful to understand how the transition between that feed and the ZPG Real-time Listings Service will be handled before reading about how to build your implementation.

Data feeds received by ZPG are kept separate from each other: your data never interacts with anyone else's (nor theirs with yours) and should you send us multiple feeds they will not interact with each other either. We then choose which subsets of these data feeds will be shown on our websites (on a per-branch basis).

You should therefore design your system so that you can update ZPG via your bulk data feed and the Real-time Listings Service concurrently, thus maintaining two equivalent data sets simultaneously:

Once your Real-time Listings Service integration is complete and running smoothly, ZPG can then simply switch its websites over from using one data set to the other. This mechanism preserves your data's integrity; simplifies testing; makes it easier to QA attribute coverage and equivalence between both systems; and allows for a quick transition to having real-time behaviour for all branches simultaneously - if desired. (It also provides the security of a viable rollback option in the unlikely event that a major problem occurs during the migration.)

11.2 Test environment

11.2.1 Sandbox environment and API

This is an exact copy of the live environment but is isolated from our public websites. Any test messages you send here will only affect the data you previously sent to the sandbox environment; you cannot "break" anything. There are no restrictions on the number of messages you can send or listings you can store. Indeed, as part of your final testing you should maintain a copy of all your listing data in the sandbox environment (just as you would do in the live environment) in order to demonstrate that the system is mature and suitable for sending data to our public websites in real-time.

11.2.2 Preview listing web page

You can more easily visualise a particular listing's data via our listing preview page. Please note that whilst the preview is styled in a similar fashion to our websites it is only intended for illustrative purposes. The URL for a particular listing's preview page can be found in the responses from listing/update and listing/list.

The preview page will indicate whether we will be able to download the associated image and other content URLs, and for any images we could not access an "awaiting photos" placeholder will be shown. Please see 9. Retrieval of media content for more information about how we will retrieve your content and the set of IPs our download requests will originate from.

11.3 Building your implementation

Whilst you do not have to follow this exactly, we suggest:

Check that your current image-hosting web servers respect If-None-Match and If-Modified-Since headers (see: 9. Retrieval of media content). If they don't then this could be having a negative impact on your infrastructure already.

Having uploaded some listings, you can then use listing/list to see what was uploaded and can then implement your synchronisation mechanism, such as the one discussed in 10. Workflow overview.

11.4 Testing

Once you have a working system, it is important to thoroughly test it:

Check that the ZPG-Listing-ETag is not using a temporary placeholder value and that it behaves correctly.

Send actual listing data. During initial development you may have used our example messages, or written your own, which may not cover all the use-cases present in your actual data. We therefore recommend you send increasingly larger sets of real data to uncover any remaining issues:

A small random set of listings.

All listings for a branch.

All listings for a random set of branches.

All listings on your system.

Check that these display correctly on their preview pages (see the url attribute in the responses for listing/update and listing/list) and that images could be downloaded by us.

If you are able to upload all your listings without encountering any errors, then you should maintain those listings for a period of time (we suggest a week or two) just as you would in the live environment. In other words, perform a "dry run":

Regularly check the synchronisation of data between your system and ours via listing/list. We suggest you also check the preview pages for a random sample of listings too in order to ensure that the data is indeed synchronised correctly.

11.5 Migrating to the live environment

It is expected that your system will communicate with the sandbox environment using actual data on a continual basis for at least a week, in order to prove that the combined system is stable, produces no unexpected errors and has no synchronisation issues. When you are happy that this is the case and it is therefore suitable to become the sole means of sending us listing data, please let us know so that you can be granted access to the live environment. The migration procedure is very similar to that used when testing in the sandbox environment:

Provide us with the details of all your branches. Please contact us if you do not wish to supply them via the branch/update method.

Send listing/update messages for all listings on your system to the live environment. (This is most easily achieved by running your synchronisation process. Please let us know when this initial upload has been completed.)

Periodically check the synchronisation of data between your system and ours via listing/list.

Because the updating process will already have been shown to work correctly over an extended period in the sandbox environment, there should be no difficulties when using the live environment.

If you currently send ZPG a bulk data feed, please continue sending this to us - even after you start sending real-time updates to the live environment. Once your initial upload of data to the live environment has completed, please let us know. We will then change our branch configuration so that we stop using the listing data in your old bulk data feed and start using the data sent via the ZPG Real-time Listings Service instead. When we complete this branch migration process, we will inform you so that you can then terminate your old bulk data feed.

12. Conclusion

We hope that having read this documentation you have decided to integrate the ZPG Real-time Listings Service with your system.

If you have any questions about this documentation, schemas or the service in general, please contact our Support Team.

Thank you for your commitment to improving the quality of service that our mutual clients and users will receive by integrating your system with the ZPG Real-time Listings Service.

Appendix 1. Additional notes on attributes

A1.1 Boolean attributes

The majority of our boolean attributes are optional. You should only include a boolean attribute where you are confident that the value is correct; do not set it to a default value. The reason for this is that you may convey a meaning that you did not intend. For example if you were to default pets_allowed to:

true: potential tenants will complain if the landlord then rejects their application.

false: the landlord will receive fewer enquiries even though they might consider tenants with pets.

Omitting boolean attributes where you do not know the correct value also has the benefit of reducing message sizes and making them easier to read when debugging.

A1.2 Datetime attributes

Dates should be expressed using standard ISO format: YYYY-MM-DD. Where appropriate, a time component may be included by appending the date with a literal T character separator, followed by the time as hh:mm:ss. Note that the time component should use the local time zone of the property. For example:

2014-01-31

2014-01-31T12:34:56

A1.3 Free-text string attributes

The quality of text supplied in free-text string attributes can vary considerably. In order to improve the user experience on our websites we may modify the display of these strings in order to present a consistent style and aid comprehension (e.g.: capitalisation; removal of HTML tags). If the quality of the text is particularly poor then, in some circumstances, we may choose not to display it at all (e.g. invalid data for the attribute). Care should therefore be taken at the initial data-entry stage to ensure that the correct attribute is being populated and that free-text intended to be read by the public is maintained appropriately (e.g.: spelling; grammar).

Our schemas use a regular expression to ensure that free-text attributes:

are neither empty nor only consist of whitespace

have no leading whitespace

have no trailing whitespace

Note that carriage returns (\r) and newlines (\n) are considered whitespace.

These rules are enforced by the schema via a regular expression, which will be included in any validation errors:

does not match '^\\S(|(.|\\n)*\\S)\\Z'

If you encounter this error you may find it easier to re-read the validation rules above in order to correct the text.

A1.4 Monetary attributes

A1.5 display_address

display_address is an obsolete attribute since it can be constructed from the dedicated address attributes: "street_name, locality, town_or_city". ZPG's websites only use address strings and we therefore expect to deprecate the use of display_address in future. We recommend software providers encourage their clients to populate address attributes correctly and provide them with a dedicated field for property_number_or_name so that unique property address information will not be included in a constructed address.

A1.6 floor_levels

The layout of floors within a multi-storey building is defined as follows:

penthouse (top floor)

... (increasing integer values)

2

1 (above street level)

ground (at street level)

basement (below street level)

A1.7 property_type

There are a lot of terms for describing house types. The distinctions between some of these types are sometimes unnecessary (for example: apartment vs flat) and broader groupings that just convey the core aspects of the architecture are preferred. Consequently we only support a relatively limited number of property types. When deciding which property type to use, you should choose the most applicable property type that we support. If you believe that there is a significant difference between your property type and those that we support, then send your property type as a readable string. In such cases we will display property type as "Property", but the string you supplied will be considered during our periodic review of property types to ensure that we are supporting the needs of our users adequately. (Please note that our supported property_type enums use all-lowercase characters; alternative case-variants will be displayed as "Property".)

Some property_types in your system may be conveyed by the appropriate use of other attributes. For example:

a "duplex" is a property_type = "flat" with floors = 2

a "triplex" is a property_type = "flat" with floors = 3

a "penthouse" is a property_type = "flat" with floor_levels = ["penthouse"]

When category = "residential", our schema will reject property_type = "studio" if total_bedrooms is also present and has a value greater than 1, as we cannot determine which value is inaccurate; see 7.2.2 JSON schema validation for more information about how we reject ambiguous messages.

Note that the display of the property_type is also dependent on the associated country_code and category of the listing. The schema does not enforce these rules. If an inappropriate property_type is specified for a particular country_code-category combination then it will be displayed as "Property" (residential) or "Commercial Property" (commercial). The list of supported property_types and their validity is as follows:

Clarifying that the role of ZPG-Listing-ETag only relates to checking data synchronicity. It does not affect how we interpret the listing/update message, which will always replace the previous version.

Correcting documentation for the listing/update:content attribute to indicate that it is an array of content objects. Also reiterating that the display order is determined by the ordering of those objects within the array.

The facility to preview how a listing will be displayed on our websites is now available. A listing-specific link is provided via the preview_url attribute in the responses for listing/update and listing/list.