Rapattoni Developer Resources

RESO Web API

Rapattoni offers an implementation of RESO's Web API, and provides data adhering to the RESO Data Dictionary.

You will need to contact the particular MLS you wish to obtain data from, prior to access being granted.

Authentication

We support OAuth2's Password and Authorization Code grant types for authentication.

Password Grant Authentication

Token URL:

  • https://retsidentityapi.raprets.com/{mls}/oauth/token

When accessing the Token URL, you will need to pass the following in the body of a POST request:

  • grant_type: password
  • client_id: Given to you by the MLS
  • client_secret: A password associated with the client id, it is a randomly generated hash. If this is ever compromised a new one may be generated by MLS staff.
  • Username: The MLS ID created in membership.
  • Password: The password associated with the above MLS ID.

Please note, standard web browsers cannot pass all of the criteria; an API client program should be used. One simple testing client is Postman, available here: Get Postman

The body of the request should look similar to the following example:

  • grant_type=password&client_id=ClientID&client_secret=Secret&username=Username&password=Password

You will also need to include the header:Content-Type: application/x-www-form-urlencoded and ensure that the values contained in the body of the request are properly URL-encoded.

If the transaction is successful, then the response body will be a JSON object containing the access token, token type, seconds till expiration, refresh token, username, date time issued and date time of expiration. The token type will always be “bearer”:
{
“access_token”: “9ad80cb9875c6d8e39efc90bc”,
“token_type”: “bearer”,
“expires_in”: 604799,
“refresh_token”: “adb83739efc90bcd9938”,
“userName”: “username”,
“.issued”: “Fri, 17 Mar 2017 16:41:32 GMT”
“.expires”: “Fri, 24 Mar 2017 16:41:32 GMT”
}

Authorization Code Grant Authentication

Authorization URL:

  • https://retsidentityapi.raprets.com/{mls}/oauth/authorize

You will need to pass the following information as query string parameters in a GET request with the Authorization URL.

  • response_type=code Code is the only value we accept for the response type when using Authorization Code authentication.
  • client_id=clientid The value should be the Client ID assigned to you by the MLS.
  • redirect_uri=uri Your URL that will be redirected to, with the authorization code, in order to complete the authentication process
  • state=string_value (Optional) If included, then it is expected to be passed back to your redirect uri with the authorization code.
  • Example: https://retsidentityapi.raprets.com/{mls}/oauth/authorize?response_type=code&client_id=ClientID&redirect_uri=https://your_redirect_uri

Once the request has been sent, our server will respond with a login page to enter a valid MLS Username and Password. This could be information granted to you by the MLS, or it could be the login information of an agent utilizing your product.

If valid MLS credentials have been entered, then you, or the end user, will be prompted to grant your application access to the MLS API information on your/their behalf.

If successful we will pass the Authorization code and state (if included) in the query parameters of a GET request to your Redirect URI.

  • Example: https://your_redirect_uri?code=authorization_code&state=the_state_string_if_used

You can then pass the following to the token end point to recieve a bearer token for API access:

Token URL:

  • https://retsidentityapi.raprets.com/{mls}/oauth/token

When accessing the Token URL, you will need to pass the following in the body of a POST request:

  • grant_type:authorization_code
  • client_id:Given to you by the MLS, the same as you used in the initial authorization request.
  • client_secret:A password associated with the client id given to you by the MLS. It is a randomly generated hash. If this is ever compromised a new one may be generated by MLS staff.
  • code: The Authorization Code you recieved.
  • redirect_uri: The redirect_uri that you used in the initial authorization request. This must be given to the MLS staff as well and will be linked to your credentials.

Please note, standard web browsers cannot pass all of the criteria; an API client program should be used. One simple testing client is Postman, available here: Get Postman

The body of the request should look similar to the following example:

  • grant_type=authorization_code&client_id=ClientID&client_secret=Secret&code=abc8393d9b8c7e8f03&redirect_uri=https://your_redirect

You will also need to include the header:Content-Type: application/x-www-form-urlencoded and ensure that the values contained in the body of the request are properly URL-encoded.

If the transaction is successful, then the response body will be a JSON object containing the access token, token type, seconds till expiration, refresh token, username, date time issued, date time of expiration and your client_id. The token type will always be “bearer”:
{
"access_token": "9ad80cb9875c6d8e39efc90bc",
"token_type": "bearer",
"expires_in": 5183999,
"refresh_token": "adb83739efc90bcd9938",
".issued": "Tue, 10 Sep 2019 23:18:25 GMT",
".expires": "Sat, 09 Nov 2019 23:18:25 GMT",
"client_id": "ClientID"
}


API Transactions

All transactions must include the Authorization header with the value being the client’s current bearer token obtained from the transaction with our Authentication server.

Here is an example of the how the Authorization header with the bearer token should appear in the HTTP request:

  • Authorization:bearer 80957f49b90d449c99dfb01871676e3e

Responses, unless otherwise specified, will be in the JSON format.

Service and Metadata

The Service Document is available via a get request against the root URL (http://retsapi.raprets.com/{mls}/RESO/odata). This document is a standard OData resource listing all top-level entity sets exposed by the service.( ODatav4 10.1)

Querying this endpoint will return a JSON array of all directly queryable entities. Each object will have the following properties:

  • Name matches the name of the entity in the metadata.
  • Kind is the type of object being described.
  • Url is the end point to use in the URL for access.

    {
        "name": "Property",
        "kind": "EntitySet",
        "url": "Property"
    },
    {
        "name": "Agents",
        "kind": "EntitySet",
        "url": "Agents"
    },
                    

The Metadata Document is available via a get request against the root URL with /$metadata appended (i.e. http://retsapi.raprets.com/{mls}/RESO/odata/$metadata). This resource describes the API’s data model; including data types, relationships between entity sets, and available fields (ODatav4 11.1.2). This resource is only available in the XML format.

Each entity definition will begin with the <EntityType> tag containing the Name of the entity.

      
    <EntityType Name="Property">
                
The first child element listed under the EntityType tag will be the Key definition, this tag will contain child elements defining the key field(s) for the entity. Keys can be used to target a specific record in an entity.

    <Key>
        <PropertyRef Name="ListingKeyNumeric" />
    </Key>
                    
The next elements will describe the properties of the entity, these define the available fields and should include the field’s datatype, maximum character length (if applicable), and, if the field is not an RESO defined Data Dictionary field, an annotation with the plain text MLS specific description.

    <Property Name="PrivateRemarks" Type="Edm.String" MaxLength="500" />
    <Property Name="PropertyType" Type="PropertyEnums.PropertyType" MaxLength="20" />
    <Property Name="Taxesannual" Type="Edm.Decimal" Precision="8" Scale="2">
        <Annotation Term="RESO.OData.Metadata.MLSName" String="Taxes(annual)" />
    </Property>
                    
The final elements will be the navigation properties. These define entities that can be expanded to. They contain the Name of the navigation property, the Type of the entity and possible Referential Constraints.

    <NavigationProperty Term="ListingAgent" Type="Rapattoni.Rets.Api.Models.Agents" />
    <NavigationProperty Term="Property" Type="Collection(Rapattoni.Rets.Api.Models.Agents)" />
             
  • Name is the value used in the $expand clause. So to expand to the Offices entity in this case you would use $expand=Offices
  • Type is full name of the entity targeted by the navigation property. While PhoneNumbers is the name of a navigation property above, it’s actually targeting the entity PhoneMember. This means you would look in the metadata for the Entity Type named PhoneMember for more information regarding the entity that navigation property is targeting. If the Type starts with the word Collection, then the mapping between the entity and the navigation property entity is one-to-many (i.e. one record may map to many records, e.g. one member may map to many phone numbers).
  • ReferentialConstraint references the field(s) being used to link records in the two resources; also known as the Foreign Key. In this case the example entity and Offices have an OfficeKeyNumeric field linking them. Usually this reference only appears if the field is not one of the source entity’s keys.

Also included in the metadata are Enumeration definitions. An enumerated field will state it's defined enumeration in it's Type:

  • <Property Name="PropertyType" Type="PropertyEnums.PropertyType" MaxLength="20" />

The first portion of the Type, before the ".", defines the Schema Namespace in which the Enum resides. The second portion, after the ".", defines the EnumType name within the given Namespace.


    <Schema Namespace="PropertyEnums" xmlns:edm="http://docs.oasis-open.org/odata/ns/edm">
        <EnumType Name="PropertyType" UnderlyingType="Edm.Int32">
            <Member Name="CommercialLease" Value="0">
                <Annotation Term="RESO.OData.Metadata.StandardName" String="Commercial Lease" />
            </Member>
            <Member Name="CommercialSale" Value="1">
                <Annotation Term="RESO.OData.Metadata.StandardName" String="Commercial Sale" />
            </Member>
            <Member Name="Land" Value="2" />
            <Member Name="MultiUnit" Value="3">
                <Annotation Term="RESO.OData.Metadata.MLSName" String="Multi-Unit" />
            </Member>
            <Member Name="Rental" Value="4">
                <Annotation Term="RESO.OData.Metadata.StandardName" String="Residential Lease" />
            </Member>
            <Member Name="Residential" Value="5" />
        </EnumType>
                 

DataSystem

The DataSystem endpoint will list all available Resources. The Classes associated with each endpoint can be seen by using the $expand query option as follows:
http://retsapi.raprets.com/{mls}/RESO/odata/DataSystem?$expand=Resources
Example DataSystem Results:


{
    "@odata.context": "http://retsapi.raprets.com/{mls}/RESO/OData/$metadata#DataSystem/$entity",
    "Id": 1,
    "Mls": "mls",
    "Name": "Rapattoni Rets",
    "ServiceUri": "http://retsapi.raprets.com/{mls}/RESO/OData",
    "DateTimeStamp": "2017-03-16T10:18:19.26-07:00",
    "TransportVersion": "1.0.2",
    "DataDictionaryVersion": "1.6",
    "Resources": [
        {
            "Id": 1,
            "DataSystemId": 1,
            "Name": "Property",
            "ResourcePath": "/Property",
            "Description": "The property endpoint",
            "DateTimeStamp": "2016-07-11T14:20:13.863-07:00",
            "TimeZoneOffset": 0,
            "Localization": [
                {
                    "Name": "Land",
                    "ResourcePath": "/Property?Class=Land",
                    "Description": "Contains data for Land searches.",
                    "DateTimeStamp": "2017-11-07T18:16:43.1-08:00"
                },
                {
                    "Name": "Residential",
                    "ResourcePath": "/Property?Class=Residential",
                    "Description": "Contains data for Residential searches.",
                    "DateTimeStamp": "2017-11-07T18:16:43.1-08:00"
                }
            ]
        },
        {
            "Id": 2,
            "DataSystemId": 1,
            "Name": "Agent",
            "ResourcePath": "/Agents",
            "Description": "The agent endpoint",
            "DateTimeStamp": "2016-07-11T14:20:13.863-07:00",
            "TimeZoneOffset": 0,
            "Localization": [
                {
                    "Name": "Member",
                    "ResourcePath": "/Agents?Class=Member",
                    "Description": "Contains data for Agent searches.",
                    "DateTimeStamp": "2017-07-18T23:12:38.953-07:00"
                }
            ]
        }
    ]
}
                            

 

Query Support

Entity Set Queries:Entity Set queries use the default OData format; the root URL is appended with “/EntitySet”. The ResourcePath elements returned from the DataSystem query are the exact entity set values to use. Queries against any entity set MUST include the Class query argument; the Classes available for each entity set are available via the DataSystem endpoint in the Localization array (see above DataSystem example). The following URL would be used for querying the Property entity set and the Residential Class:

  • http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential

When Querying against Entity Sets, our implementation of the RESO Web API supports most OData 4 query options, functions and operators. The below list are those query options and some of their associated supported functions and operators. Please see the OData 4 specification for further information in regards to these:

  • $select : This is used to specify what fields you would like to have return in the results.(ODatav4 11.2.4.2)
    • Example URL: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$select=ListingKeyNumeric,ListingId,StandardStatus,City
  • $filter : restrict the returned entities to only those matching the filter criteria (ODatav4 11.2.5.1)
    • Operators
      • eq : Equals
        • Example : StandardStatus equals Active: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=StandardStatus eq 'Active'
      • ne : Not Equals
        • Example : City not equals Granite Bay: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=City ne 'Granite Bay'
      • gt : Greater than
        • Example: BathroomsTotalInteger greater than 2: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=BathroomsTotalInteger gt '2'
      • lt : Less than
        • Example: BathroomsTotalInteger less than 2: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=BathroomsTotalInteger lt '2'
      • and: Logical and
        • Example: BathroomsTotalInteger less than 2 and City equals San Francisco: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=BathroomsTotalInteger lt '2' and City eq 'San Francisco'
      • or: Logical or
        • Example: BathroomsTotalInteger less than 2 or City equals San Francisco: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=BathroomsTotalInteger lt '2' or City eq 'San Francisco'
      • not: Logical negation, used in conjunction with other functions and operators to create a negative comparison.
        • Example: BathroomsTotalInteger less than 2 and not City equals San Francisco: http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=BathroomsTotalInteger lt '2' and not City eq 'San Francisco'
      • (): Functions and operators can be nested using parentheses for precedence.
        • Example: BathroomsTotalInteger less than 2 or (City equals San Francisco and MlsStatus equals Sold): http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=BathroomsTotalInteger lt '2' or (City eq 'San Francisco' and MlsStatus eq 'Sold')
      • has: The has operator returns true if the right hand operand is an enumeration value whose flag(s) are set on the left operand. The null value is treated as unknown, so if one operand evaluates to null, the has operator returns null. Please refer to the metadata for supported Enumeration values.
        • Example: The value, or one of the values,in ListingTerms is 'Cash': http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$filter=ListingTerms has Rapattoni.Rets.Api.Models.ListingTerms'Cash'
    • Functions
      • contains: string contains, syntax is: contains(FieldName,’string’)
      • endswith: string ends with, syntax is: endswith(FieldName,’string’)
      • startswith: string starts with, syntax is: startswith(FieldName,’string’)
      • day: return the day component from a DateTimeOffset or Date
      • hour: return the hour component from a DateTimeOffset
      • minute: return the minute component from a DateTimeOffset
      • second: return the second component from a DateTimeOffset
      • month: return the month component from a DateTimeOffset or Date
      • year: return the year component from a DateTimeOffset or Date
    • Complex Filter Example:
      • Property?Class=Residential$filter=(contains(PublicRemarks,‘pool’) and City eq ‘Ventura’) or ListingTerms has Rapattoni.Rets.Api.Models.ListingTerms'Cash'
  • $top : Limit the number of results returned.(ODatav4 11.2.5.3)
    • Example: Returning the first 10 records http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$top=10
  • $skip : skip a number of records before returning results(ODatav4 11.2.5.4)
    • Example: Skip the first 100 records, then return results. http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$skip=100
  • $orderby: order the results by a specific field’s value; use asc or desc to specify ascending or descending. To order by multiple criteria use comma delimiting(ODatav4 11.2.5.2)
    Examples:
    • http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$orderby=City desc http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$orderby=Status asc
  • $count: $count=true specifies that the total count of entities matching the request must be returned along with the results (ODatav4 11.2.5.5)
    Example:
    • http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$count=true
  • $apply: $apply triggers data aggrigation behavior, at this time only the groupby extension is supported. (ODatav4 Extension for Data Aggregation Section 3)
    • groupby: groupby takes one or two fields, and returns a distinct list of those fields's values.(ODatav4 Extension for Data Aggregation Section 3.10)
    • Example, the following would return a distinct set of all City values assigned to Property Residential records:
      • http://retsapi.raprets.com/{mls}/RESO/OData/Property?Class=Residential&$apply=groupby((City))
  • $expand: allows for the inclusion of entities related to the base entity set queried. The relationships between entity sets can be found in the metadata document. Multiple entities can be included with comma delimiting.(ODatav4 11.2.4.2)
    Examples:
    • Property?Class=Residential$expand=PropertyPictures: this will return Residential Property records and their related entries from Media's PropertyPictures class.
  • &: Multiple query options can be included at once, each separated by an “&”
    • Example: Property?Class=Residential&$filter=MlsStatus eq 'Active'&$select=ListingKeyNumeric,City&$top=10
  • Single Entity: To return one specific Entity, the format is EntitySet(key); note the Class parameter must still be included. For example, if pulling back Residential listing 101 from the Property entity set, the root URL would be appended with /Property(101). Please refer to the metadata to find the primary key for each Entity Set.
    • Example: http://retsapi.raprets.com/{mls}/RESO/OData/Property(101)?Class=Residential
  • Paging: Server paging is enabled; the server will limit the number of records that it sends in a single response.
    • @odata.nextLink: If the number of records matching the query parameters is greater than the page size limit, then the results will include the “@odata.nextLink” JSON object. This object will usually exist at the end of the current result and will contain the exact URL and query parameters needed to obtain the next page of matching records.
    • odata.maxpagesize: You may specify a custom page size by including the Prefer header with the odata.maxpagesize preference. If the page size specified is greater than the server's page size limits, then the server page size will be used instead.
      • Example: Prefer:odata.maxpagesize=50
    • Server Page Size: The server page size limits are based on the number of fields selected and time of day (based on the MLS's timezone). If $expand is included in the query, then the limits are 1/4 of their normal value.
Columns SelectedResource(Entity)Time of DayPage Size
1-4 Any Expect Media Any 1000000
1-4 Media Any 100000
5-50 Any Any 2000
51+ Any 8am-6pm 1000
51+ Any 6:01pm-7:59am 2000