Mashing up Force.com and Zillow

Zillow is an innovative service that provides residential property valuations for houses across the United States. The valuation, called a Zestimate is based on the county tax records, sale of comparable properties in the neighborhood and a Zillow proprietary algorithm. The valuation is provided for every house, not just those that may be for sale at any given time.

Zillow also provide a web service API, making it a prime candidate for a mashup on the Force.com platform. Imagine, for example, writing an application that could make, complement or qualify a lead based on the demographics and housing data.

This articles shows how to create such a mashup. We implement an interface to the Zillow REST-based web services API, providing all the code. We then build on that code by creating a Visualforce page that integrates the interface with an application on the Force.com platform, in this case Salesforce.com. The techniques used in this article are applicable to other REST based web services.

Creating the Zillow interface

In the next few sections we will look at the details of building an interface to one of Zillow's services. Our goal is to build an interface that is simple and easy to use, and that be easily extended to handle other Zillow services. We start with some preliminary setup information and then describe the various classes.

Getting Started

Zillow provides a REST based API to its services. The HTTP classes provided by Apex provide an easy way to interact with such REST based services. However, before we can make a call to Zillow, we need to complete two simple tasks:

  • Sign up with the Zillow API Network and obtain a token called the Zillow Web Services ID (ZWSID). This token should be passed with every API call, Zillow uses it for authentication as well as monitoring the API usage. Zillow places a limit of 1000 queries per day, per API - higher limits can be requested from Zillow.
  • Add Zillow to the list of allowed remote sites that can be accessed from your Salesforce organization. Go to Setup -> Security Controls -> Remote Site Settings and add Zillow as shown below and Save your changes.
Remote-site-add-small.jpg

Now that we have a Zillow token and enabled the call out from the platform, we are ready to start building the code to interface with Zillow.

Building the ZillowService class

The ZillowService class which we develop in Apex is the main interface to the clients. Zillow provides several services - home valuation, demographics, comparable property data etc. We will implement just one of the services, GetSearchResults. Given an address for a property, this service returns the Zestimate data that contains the property valuation as well as data related to this valuation like the date this valuation was calculated, range, percentile and so on.

Let us first look at the basics of making a REST call in Apex. REST based calls are very simple: they involve invoking one of the standard HTTP commands (or verbs) like GET, PUT etc. on a resource represented by a URL. We have built a private helper method, invokeZillow(), which does this for us. The code is listed below:

private HttpResponse invokeZillow( String method, String url ){		 	
     HttpRequest  req = new HttpRequest();   
     HttpResponse response = null;
     try {
	if( method != 'GET' ){
		throw new ZillowTypes.ZillowException('ZillowService::invokeZillow - only GET supported  '+method +' is not supported') ;
	}	
      req.setEndpoint(url); 
      req.setMethod(method) ;
      Http http = new Http();
      response = http.send(req);
   }
    catch( System.Exception e){			
	throw new ZillowTypes.ZillowException('Error sending HTTP message - ERROR: '+ e) ;
    }					
    if (response.getStatusCode() != 200) { 
	throw  new ZillowTypes.ZillowException('Error in HTTP Response - STATUS: ' +response.getStatus() + 'STATUS_CODE:'+ response.getStatusCode()) ;	
    }				
    return response ;
}

Since GET is the only method supported by Zillow, we throw an exception if some other method name is passed. The code creates an HTTP request with the details, and after checking the HTTP response code, returns the response. The HTTP response code only gives us information at the HTTP transport level, indicating whether the message was successfully sent and received. We still need to check the message for the return code from Zillow. We do that in the searchZillow() method.


The searchZillow() method provides the client interface to invoke Zillow's GetSearchResult service. The method, shown below, prepares the URL and calls invokeZillow() that makes the actual web services call. We prepare the URL using the sample shown on the Zillow site - the parameters (address and citystatezip) must be encoded and passed as part of the URL.

public ZillowTypes.PropertySearchResponse searchZillow( String address, String citystatezip ){
  // construct the URL		
  String endpointURL = ZillowConfig.PROPERTY_SEARCH_URL + ZillowConfig.ZWSID + '&address=' + 	EncodingUtil.urlEncode(address, 'UTF-8') + '&citystatezip=' + EncodingUtil.urlEncode(citystatezip, 'UTF-8') ;						
  ZillowTypes.PropertySearchResponse searchResponse = null ;		
  try{			
	HttpResponse response = invokeZillow( 'GET', endpointURL ) ;
	XMLDom responseXML = new XMLDom( response.getBody() ) ;
	String code = responseXML.getElementByTagName('code').nodeValue ;
	if( code == ZillowTypes.CODE_SUCCESS){
				searchResponse = new ZillowTypes.PropertySearchResponse( responseXML.getElementByTagName('response') ) ;
	 }
	 else{
	    throw new ZillowTypes.ZillowException( 'Error in Zillow response - code = '+code +' Description : ' +ZillowTypes.PropertySearchResponseCode.get(code) );	
	  }
  }
  catch( System.Exception e){
    System.debug( 'Error ' +e) ;
    throw e ;
  } 		
  return searchResponse ;		
}

Notice, how the ZWSID must be passed as part of the URL. We have stored this in a utility class called ZillowConfig. Change the ZWSID in this class to the one you obtain after you register with Zillow.

The response from GetSearchResult is an XML document - a sample response can be seen on the Zillow API web site. The code element is the return code from Zillow. A code element value of 0 indicates success, and we have defined the constant CODE_SUCCESS in class ZillowTypes to represent it. We have also built a Map, PropertySearchResponseCode that maps all possible return codes from Zillow to a textual message. This is useful in providing user friendly error messages if the response indicates an error. XMLDom is a helper class that we use for parsing the XML document.


We also overloaded the searchZillow() method to provide a few different ways for the client to call this service. The Zillow API expects the address to be passed in as two distinct parameters in the URL - these overloaded methods allow the client to invoke the service without having to understand any of these details.

public class ZillowService{
 //..
 public ZillowTypes.PropertySearchResponse searchZillow( String street, String City, String state) { ... }
 public ZillowTypes.PropertySearchResponse searchZillow( String street, String City, String state, String zip ) { ... }
 public ZillowTypes.PropertySearchResponse searchZillow( String address, String citystatezip) { ... }
}

The Zillow services return back an XML document, and we wrote the class PropertySearchResponse to provide a user friendly wrapper around this by parsing this response. The XMLDom class is used here as well to help in parsing the response for the XML element of interest - in this case the amount of the valuation. Here is what the getZEstimateAmount() method looks like

public class PropertySearchResponse {	
 xmldom.element searchResult ;
 public Double getZEstimateAmount(){
    Double d = Double.valueOf( searchResult.getElementByTagName( 'amount').nodeValue ) ;
     return d ;	
  }	
}

This class can be extended in a similar manner to extract other elements of interest from the response. For example, the code below will extract the change in valuation in the last 30 days. We can then wrap this in a user friendly method that the client can call.

 Double.valueOf( searchResult.getElementByTagName( 'valueChange').nodeValue ) 

We now have a simple class to interact with the Zillow GetSearchResults service. We use helper methods to do the actual invocation as well as parse the results.

Using this service

Finally, let us look at how a client would use this service

try{
 ZillowService zillow = new ZillowService() ;
 // search Zillow for property at 2114 Bigelow Ave, Seattle, WA
 ZillowTypes.PropertySearchResponse result = zillow.searchZillow( '2114 Bigelow Ave', 'Seattle, WA') ;
 // retrieve the property valuation 
 Double zEst = result.getZEstimateAmount() ;  
 // do something with zEst
}
catch( ZillowTypes.ZillowException e){
 // log it
 System.debug( 'Caught Exception' +e ) ;
}

The client simply creates an instance of ZillowService and calls the searchZillow() method, which returns a populated PropertySearchResponse object. The details of making the web service request, parsing it and so on is transparent to the client.

Extending it to other Zillow Services

ZillowService can be extended to handle other Zillow APIs in a similar fashion. For example, to get the demographics information, we can add a method like ZillowType.DemographicsResponse getDemographicsInfo(String address, String region) and follow a similar set of steps. The error code returned by Zillow is API specific, meaning the same code number (503 for e.g.) is used to indicate different information in each API call. To provide user friendly error message to the clients of your class, build a Map similar to our PropertySearchResponseCode.

Integrating with Salesforce CRM

At this point we have a utility that lets us interact with Zillow. You can plug this into any Force.com application or integrate it with another application to create a mashup.

We will do the latter and plug this into the Salesforce.com CRM application. We want to associate a lead with the the property valuation based on the address fields. This data could be useful, for example, to do a targeted campaign or to further qualify the lead.

To accomplish this, we will create a new custom field on the Lead object called Home Value that will be populated with the result of the call to the Zillow service. We hook the service up by writing an extension to the standard lead Visualforce controller that overrides the save method.

Lead Entry Page

We created a new VisualForce page called ZillowLead that enables us to enter data for a new Lead as shown below. We have omitted some of the lead fields for the sake of brevity and since they are not relevant to our example.

<apex:page standardController="Lead" extensions="ZillowLeadExtension">
    <apex:form >  
    <!-- Insert new Lead -->
    <apex:pageBlock title="New Lead" >        
        <apex:pageBlockButtons location="top">                                  
           <apex:commandButton action="{!save}" value="Save" id="saveLead" />       
        </apex:pageBlockButtons>
       <apex:pageBlockSection title="Lead Data" columns="1"> 
          <apex:inputField value="{!aLead.LastName}" />
          <apex:inputField value="{!aLead.FirstName}" />
          <apex:inputField value="{!aLead.Company}" />                                       
          <apex:inputField value="{!aLead.Phone}" />
          <apex:inputField value="{!aLead.Email}" />
          <apex:inputField value="{!aLead.Street}" />
          <apex:inputField value="{!aLead.City}" />
          <apex:inputField value="{!aLead.State}" />
          <apex:inputField value="{!aLead.PostalCode}" />
      </apex:pageBlockSection>
   </apex:pageBlock>
  </apex:form>
 </apex:page>

Figure 1 shows what this page looks like. When the user hits the Save button after filling in the lead data, the save() method in the controller extension class ZillowLeadExtension is invoked. We look at this next.

The Controller

A controller extension in Apex lets you extend the functionality of a standard or custom controller. In this example, we want to extend the Save functionality for the lead to be able to fetch the home valuation for a lead. We override the standard save() method to make a call to Zillow as shown below.

public class ZillowLeadExtension {
 public Lead aLead ;
 public ZillowLeadExtension(ApexPages.StandardController controller) {
  aLead = new Lead() ;      
 }

public PageReference save() {        
 PageReference pr ;
 try{
  ZillowService p = new ZillowService() ;
  // make the web service call out
  ZillowTypes.PropertySearchResponse r = p.searchZillow( aLead.Street, aLead.City, aLead.State) ;
  // store in custom field
  aLead.Home_Value__c = r.getZEstimateAmount() ;
  // insert new record into DB
  insert aLead ;
  // redirect to the newly inserted lead
  pr = new PageReference( '/' + aLead.id );
 }
  catch( ZillowTypes.ZillowException e){
  System.debug( '**** Caught Exception' +e ) ;
}
 return pr ;
}
}

The returned home valuation is stored in the custom field and the record is inserted into the database. We then redirect the user to the newly created record as shown in Figure 2 below.

Running the example

To run the example, simply point your browser to the Visualforce page. In our case it is https://na1.salesforce.com/apex/ZillowLead, which results in the simplified Lead entry screen shown in Figure 1.

New-lead-entry.jpg

Figure 1: Lead Entry Page

When Save is clicked, the user will be redirected to the newly inserted lead (see Figure 2) that has the Home Value field populated.

New-lead-saved.jpg

Figure 2: Saved lead with the Home Value field populated

Downloading the code

The zip file containing the code can be downloaded here Zillow-mashup-code.zip. We haven't included any test methods in the sample code you will have to build it if you intend to use this in production. Zillow sends a warning message when you are reaching your limit on the number of API calls allowed in a day. If you think you may hit this limit you should account for it in your code. Finally, this is an illustrative example to show how to consume REST based services within the Force.com platform not a usage guide to Zillow. Please read the Zillow's terms of use carefully.

Summary

Force.com makes it easy to build mashups that allow you to integrate data from external services into your applications. This article shows how to build an interface to the Zillow web service, and then demonstrates how to integrate the Salesforce CRM Lead data with data obtained from the Zillow interface.

References

About the Author

Nick Simha is a Partner Enablement Manager at Salesforce.com working with Salesforce's Systems Integrators and ISVs. His professional background includes extensive experience with enterprise middleware and architecture. He can be reached at nsimha@salesforce.com