View on GitHub

RESO Transport Workgroup

RESO Transport Workgroup - Specifications and Change Proposals

RESO Web API Core Specification

RCP 37
Version 2.0.0
Authors Joshua Darnell (RESO)
Status RATIFIED
Date Submitted August 2020
Date Ratified January 2021
Protocol HTTP
Dependencies OData 4.0 or 4.01
TLS 1.2+
OAuth 2 (Auth Token or Client Credentials)
Related Links OASIS OData TC

The Web API Core endorsement defines the primary functionality RESO Web API servers are expected to support in order to provide both replication and live query support.


RESO End User License Agreement (EULA)

This End User License Agreement (the “EULA”) is entered into by and between the Real Estate Standards Organization (“RESO”) and the person or entity (“End User”) that is downloading or otherwise obtaining the product associated with this EULA (“RESO Product”). This EULA governs End Users use of the RESO Product and End User agrees to the terms of this EULA by downloading or otherwise obtaining or using the RESO Product.


Table of Contents


Summary of Changes


Introduction

The Web API Core Endorsement provides a subset of functionality from the OASIS OData specification relevant to those who need to perform live queries or replicate data using the RESO Web API. This includes the ability to express metadata and provide query support for primitive OData types and enumerations.

This document offers normative examples of what these items should look like, both in the metadata and payload.


Section 1: Purpose

In general, the RESO Web API defines mechanisms for creating, reading, updating, or deleting data from web or mobile applications using open standards and JSON Web APIs.

The purpose of the Web API Core specification is to establish a set of queries that can be used to retrieve information related to the RESO Data Dictionary and local data elements

The goals of the RESO Web API are as follows:

The Web API uses the Open Data Protocol (OData), which:

Compatible RESO OData Transport client and server applications MUST be implemented according to versions “4.0” or “4.01” of the OData specification.

All references to the OData specification contained within this document assume version 4.0 of the OData specification, unless otherwise specified.

Compatible server and client applications MUST support OData XML Metadata for schema representation and MUST use the JSON response format for data requests.

RESO Web API servers MUST conform to OData conventions with respect to metadata, query, and response formats as well as HTTP, TLS, and OAuth2 for application layer protocol, transport security, and authentication requirements.


Section 2: Specification

This specification outlines the requirements for the RESO Web API Core Endorsement, which is a subset of the OData 4.0 specification.

The OData specification is divided into three main sections:

While there is no official RESO reference server at this time, reference servers have been provided and have been certified with RESO’s new testing tools.

There is also reference material that should be helpful for developers implementing the Web API Core specification:

Please contact RESO if you have questions about the Web API Core specification or testing rules.


2.1 Terminology

The following terminology is used within this specification:

Term Definition
REST Representational State Transfer. More information.
Resource A resource is an object with a type, associated data, relationships to other resources, and a set of methods that may operate on it.
RESO Data Dictionary A uniform set of field names and data type conventions that set a baseline across the real estate industry for how real estate data will be defined. See the Data Dictionary Overview and DD Wiki (v 1.7) for more information.
Standard Resource A data source or collection of data that is represented using the resource definitions defined in the RESO Data Dictionary (e.g. Property, Member, Office).
Local Resource A data source or collection of data that is represented using resources not defined in the RESO Data Dictionary. This may also be localized data, such as language localization.
Metadata Descriptive information about a data set, object, or resource that helps a recipient understand how resources, fields, and lookups are defined, and relationships between resources. This information contains field names, data types, and annotations that help data producers and consumers understand what’s available on a given server. In OData, metadata is always located at the path /$metadata relative to the provider’s service root URL.
Payload The term “payload” generally refers to the JSON response returned by the server for a given request. The term is also used when creating or updating data, in which case the payload would be the data provided for create or update.
Schema A way of logically defining, grouping, organizing and structuring information about data so it may be understood by different systems. The schema defines the payload a given server is expected to support.
Authorization Authorization defines a set of protocols and processes for verifying that a given user has server access to one or more server resources. At the time of writing, the RESO Web API uses the OAuth2 Bearer Token and Client Credentials standards for authorization.
Bearer Token A type of authorization that provides simple token-based authentication. More information.
Client Credentials A type of authorization grant that uses a client_id and client_secret (essentially username and password) as an additional layer of security in order to provide a Bearer Token upon request. This method is more resilient against man-in-the-middle attacks than Bearer Tokens since there is an additional token request step involved, and tokens may be expired and refreshed programmatically using this approach. More information.
MUST The given item is an absolute requirement of the specification. A feature that the specification states MUST be implemented is required in an implementation in order to be considered compliant. If the data is available in the system AND the data is presented for search then it MUST be implemented in the manner described in the specification. See Notes (1), below.
SHOULD A feature that the specification states SHOULD be implemented is treated for compliance purposes as a feature that may be implemented. There may exist valid reasons in particular circumstances to ignore an item classified as SHOULD, but the full implications should be understood and the case carefully weighed before choosing not to implement the given feature. See Notes (1), below.
MAY This term means that an item is truly optional. A feature that the specification states MAY be implemented need not be implemented in order to be considered compliant. However, if it is implemented, the feature MUST be implemented in accordance with the specification. See Notes (1), below.
Out of Scope This statement means that the specific topic has not been addressed in the current specification but may be addressed in future versions.
N/A This term means “not applicable” to the scope of this standard and will not be addressed by this standard specification.


2.2 HTTP Protocol

A compatible RESO Web API server MUST use HTTPS as the protocol declared by the server URL.

The HTTP version MUST be HTTP/1.1 or above, which includes HTTP/2 at the time of writing.

While OData supports HTTP/1.0, there are many limitations in the HTTP/1.0 specification that we want to avoid. Therefore, we are limiting compatible implementations to HTTP/1.1 or above. For specific HTTP references, please see the references section.

Since the RESO Web API requires that HTTPS and the OAuth2 protocols are used, all server implementations MUST implement Transport Layer Security (TLS).


2.2.1 Version Header

The OData version header is used by the server to communicate the currently supported version of the specification:

OData-Version: [Version]

where

[Version] = MAJOR.MINOR

Examples

OData-Version: 4.0

OData-Version: 4.01

From MDN:

HTTP headers let the client and the server pass additional information with an HTTP request or response. An HTTP header consists of its case-insensitive name followed by a colon (:), then by its value. Whitespace before the value is ignored.

This means that odata-version: 4.0 and OdAtA-vErSiOn:4.01 are also valid, though not recommended. Those using headers should be prepared to process them accordingly.

Requirements

Client Request Result
No Version Server MUST return the current supported version
Current Version Server MUST return the current version
Older Version, Still Supported Server MUST return requested version
Older Version, Not Supported Server MUST return HTTP 400 Bad Request
Newer Version Server MUST return HTTP 400 Bad Request

See Response Message Bodies for details on expected responses.


2.2.2 Optional OData Headers

The following optional headers are defined in the specification:

Header Functionality
omit=nulls It is recommended that that servers fully support this functionality in order to reduce the outbound payload size.
omit=defaults It is recommended that servers do not support this functionality in order to ensure that clients get important default values that are integral to the service.


2.3 URL Formatting

The OData transport protocol defines a few standardized URL formatting requirements for ease of use and application interoperability.


2.3.1 Hostname

The hostname of the URL is arbitrary and no naming convention is required.

The following example protocol and hostname are used in the examples in this document. HTTPS is required.


<br />

### 2.3.2 URI Conventions
The OData transport protocol defines the following URI conventions:

| **Item** | **URI** |
| --- | --- |
| **Metadata Path** | `https://api.reso.org/reso/$metadata` |
| **Resource Path** | `https://api.reso.org/reso/Resource` |
| **Service Root** | `https://api.reso.org/reso/` |
| **Singleton Resource Path (String Key)** | `https://api.reso.org/reso/Resource('ID')` |
| **Singleton Resource Path (Numeric Key)** | `https://api.reso.org/reso/Resource(123)`

RESO uses **TitleCase** for Resources, Fields, OData Lookup Values, and Navigation Properties.

<br />

### 2.3.3 Metadata URI Conventions
OData offers a [special endpoint](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_AddressingtheModelforaService) for conveying server metadata, located at:

`https://<service root>/$metadata`

The metadata document MUST be located relative to the [OData service root](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_ServiceRootURL), as shown above.

**Example**

Assume the OData service root is located at `https://api.reso.org`

The metadata document MUST be located at `https://api.reso.org/$metadata`

**Example**

Service root definitions MUST not overlap, meaning that one service root cannot be nested in another.  A request to https://api.reso.org/systemA/$metadata would fail if used in conjunction with the service root definition above.

Vendors who offer multiple endpoints should structure their services accordingly. If there are two systems, systemA and systemB, their service roots would be:

**System A**: `https://api.reso.org/systemA`

**System B**: `https://api.reso.org/systemB`

with metadata endpoints,

**System A**: `https://api.reso.org/systemA/$metadata`

**System B**: `https://api.reso.org/systemB/$metadata`

respectively.

<br />

### 2.3.4 Resource Endpoint
Resources are defined by the server’s XML Metadata document, which also defines the URLs used to query those resources.

In the language of OData, resource definitions use the `EntityType` tag.

**Example**
Assume a given server defines a Property resource as follows, using the XML Metadata example from [section 2.3.3](#233-metadata-uri-conventions):

GET https://api.reso.org/$metadata&$format=application/xml HTTP/2 200 OK


```xml
<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="org.reso.metadata" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="Property">
        <Key>
          <PropertyRef Name="ListingKey"/>
        </Key>
        <Property MaxLength="255" Name="ListingKey" Type="Edm.String"/>
        <Property Name="ListPrice" Precision="14" Scale="2" Type="Edm.Decimal"/>
        <Property Name="StandardStatus" Type="org.reso.metadata.enums.StandardStatus"/>
        <Property Name="ModificationTimestamp" Precision="27" Type="Edm.DateTimeOffset"/>
      </EntityType>
    </Schema>
    <Schema Namespace="org.reso.metadata.enums" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EnumType Name="StandardStatus">
        <Member Name="Active"/>
        <Member Name="Closed"/>
        <Member Name="ComingSoon"/>
        <Member Name="Pending"/>
      </EnumType>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

This metadata defines the following items:

Item XML Schema Data Type Attributes Comments
Property Resource EntityType N/A Key field of ListingKey Each entity MUST define a Key property.
ListingKey Field Property Edm.String MaxLength of 255 MaxLength is an optional attribute.
ListPrice Field Property Edm.Decimal Precision is 14, Scale is 2 Precision and Scale are optional attributes.
StandardStatus Field Property org.reso.metadata.enums.StandardStatus N/A The type in an Edm.EnumType definition is defined by the namespace and references the StandardStatus EnumType defined on line 16.
ModificationTimestamp Field Property Edm.DateTimeOffset Precision of 27 to support the ISO 8601 format Supported timestamps in this case would be: 2021-05-21T06:28:34+00:00 OR 2021-05-21T06:28:34Z either of which MAY have a trailing millisecond component, for example: 2021-05-21T06:28:34+00:00.108 OR 2021-05-21T06:28:34.007Z
StandardStatus Enumeration EnumType Edm.EnumType N/A Defines enumerations for: Active, Closed, ComingSoon, and Pending using the SimpleIdentifier format.


Request Data from the Property Resource without an OData $filter Expression

GET https://api.reso.org/Property
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property",
  "value": [
    {
      "ListingKey": "a1",
      "ListPrice": 100000.00,
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "StandardStatus": "Active"
    },
    {
      "ListingKey": "b2",
      "ListPrice": 100001.00,
      "ModificationTimestamp": "2020-04-03T02:02:02.02Z",
      "StandardStatus": "Pending"
    }
  ]
}


Request Data from the Property Resource using an OData $filter Expression

GET https://api.reso.org/Property?$filter=ListPrice gt 100000.00
200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice gt 100000.00",
  "value": [
    {
      "ListingKey": "b2",
      "ListPrice": 100001.00,
      "ModificationTimestamp": "2020-04-03T02:02:02.02Z",
      "StandardStatus": "Pending"
    }
  ]
}


2.4 Data Types

This section outlines the standard data types supported by the Web API Core specification.

Data Type Mappings

The following mappings exist between the RESO Data Dictionary and OData data types, as outlined in RCP-031:

Data Dictionary 1.6+ Web API 2.0.0+ Notes
Boolean Edm.Bool MUST be one of the literatrue or false (case-sensitive).
Collection Edm.Collection In Web API Core, collections are only suppported when used with Collection(Edm.EnumType) or Collection(Edm.String) to represent lookups.

Providers MAY use collection data types for their own expansions.

RESO also has defined standard NavigationProperty definitions, which allow expansion between related resources. See RESO’s reference metadata and search for “NavigationProperty” for normative XML Metadata references.
Date Edm.Date MUST be in YYYY-MM-DD formaccording to the ISO 8601 date format.
Number Edm.Decimal OR Edm.Double for decimal values; Edm.Int64 OR Edm.Int32 OR Edm.Int16 for integers. Numbers that require decimal precision MUST use Edm.Decimal or Edm.Double, whose query and payload semantics are the same. Integers MAY be sized accordingly to support the data in a given field.
String Edm.String MUST be case-sensitiby the OData specification. Field names are also case sensitive when used in the $select, $filter, and $orderby query operators and clients MUST respect case sensitivity defined in the resource metadata.
String List, Single Edm.EnumType OR Edm.String with the Lookup Resource (RCP-032) RESO supports either Edm.EnumType OR EString lookups. The former MUST conform to OData SimpleIdentifier conventions, which essentially means they begin with a letter or underscore, followed by at most 127 letters, underscores or digits. Deprecation Notice applies. See Notes.
Sting List, Multi Edm.EnumType with IsFlags=true OR Collection(Edm.EnumType) OR Collection(Edm.String) with the Lookup Resource (RCP-032) REsupports three kinds of multi-valued enumerations at the moment. Deprecation Notice applies. See Notes.
Timestamp Edm.DateTimeOffset Timestamps also use the ISO 86format. Examples: 2021-05-21T16:43:43+00:00 and 2021-05-21T16:43:43Z. Millisecond precision: 2021-05-21T16:43:43.108+00:00 and 2021-05-21T16:43:43.007Z

Notes


2.5 Query Support

Each OData data type supports query operators relevant to its type. For instance, dates, timestamps, and numbers allow for greater than and less than comparisons.

Specifics regarding data types and query operators are outlined in the sections that follow.

See the OData specification for further details regarding Query Support.

The query operators shown in this section are MUST requirements for the Web API Core Endorsement unless otherwise specified.


2.5.1 Metadata Request

OData supports both XML and JSON metadata formats.

Servers MAY support JSON metadata, but RESO requires they MUST support the XML metadata format.

The OData format parameter allows clients to request a specific format, such as $format=application/xml for XML Metadata.

If no $format parameter is passed, the server MUST return the application/xml format of the metadata when a request is made to the /$metadata endpoint.

Example

GET https://api.reso.org/$metadata?$format=application/xml
HTTP/2 200 OK
<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="org.reso.metadata" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="Property">
        <Key>
          <PropertyRef Name="ListingKey"/>
        </Key>
        <Property MaxLength="255" Name="ListingKey" Type="Edm.String"/>
        <Property Name="ListPrice" Precision="14" Scale="2" Type="Edm.Decimal"/>
        <Property Name="StandardStatus" Type="org.reso.metadata.enums.StandardStatus"/>
        <Property Name="ModificationTimestamp" Precision="27" Type="Edm.DateTimeOffset"/>
      </EntityType>
    </Schema>
    <Schema Namespace="org.reso.metadata.enums" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EnumType Name="StandardStatus">
        <Member Name="Active"/>
        <Member Name="Closed"/>
        <Member Name="ComingSoon"/>
        <Member Name="Pending"/>
      </EnumType>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

Notes


2.5.2 Service Document Request

Servers MUST support a service document request, according to the OData Minimal Conformance Rules.

The service root URL identifies the root of an OData service. A GET request to this URL returns the format-specific service document, see OData-JSON.

The service root URL MUST terminate in a forward slash.

The service document enables simple hypermedia-driven clients to enumerate and explore the resources published by the OData service.

RESO validates that the service document request can be made and that it produces a valid JSON response, but does not have any additional requirements about what the document must contain. Data providers may choose which entities they want advertise in their service document according to their business needs.

Example

Assuming the metadata in section 2.5.1,

GET https://api.reso.org/$metadata?$format=application/xml
HTTP/2 200 OK
<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="org.reso.metadata" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="Property">
        <Key>
          <PropertyRef Name="ListingKey"/>
        </Key>
        <Property MaxLength="255" Name="ListingKey" Type="Edm.String"/>
        <Property Name="ListPrice" Precision="14" Scale="2" Type="Edm.Decimal"/>
        <Property Name="StandardStatus" Type="org.reso.metadata.enums.StandardStatus"/>
        <Property Name="ModificationTimestamp" Precision="27" Type="Edm.DateTimeOffset"/>
      </EntityType>
    </Schema>
    <Schema Namespace="org.reso.metadata.enums" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EnumType Name="StandardStatus">
        <Member Name="Active"/>
        <Member Name="Closed"/>
        <Member Name="ComingSoon"/>
        <Member Name="Pending"/>
      </EnumType>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

The Service Document would be as follows:

GET https://api.reso.org
HTTP/2 200 OK
{
  "@odata.context": "$metadata",
  "value": [{
    "name": "Property",
    "url": "Property"
  }]
}


2.5.3 Fetch by Key

OData provides a way to access a single record by its key, called a singleton record.

How the key is referenced depends on its type.

The following examples assume that a resource called Property is defined.

Note: unlike requests that return a collection of items in a value array, singleton requests return a instance of the requested type at the top level if a given record exists.

String Keys

String keys are surrounded with single quotes when used in an OData key query:

GET https://api.reso.org/Property('a1')
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property('a1')",
  "ListingKey": "a1",
  "BedroomsTotal": 5,
  "ListPrice": 100000.00,
  "StreetName": "Main",
  "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
  "ListingContractDate": "2020-04-02",
  "StandardStatus": "ActiveUnderContract",
  "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
}

Numeric Keys

Numeric keys do not use any special characters:

GET https://api.reso.org/Property(123)
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property(123)",
  "ListingKeyNumeric": 123,
  "BedroomsTotal": 5,
  "ListPrice": 100000.00,
  "StreetName": "Main",
  "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
  "ListingContractDate": "2020-04-02",
  "StandardStatus": "ActiveUnderContract",
  "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
}


2.5.4 $select Operator

OData allows clients to specify which fields they would like returned in a given payload through the use of the $select operator.

GET https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp",
  "value": [
    {
      "ListingKey": "a1",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z"
    },
    {
      "ListingKey": "b2",
      "ModificationTimestamp": "2020-04-02T02:02:02.007Z"
    }
  ]
}

RESO Web API Core servers MUST support the $select operator.


2.5.5 $top Operator

The OData $top operator allows clients to specify the number of records they would like to request from a given server.

Servers MAY respond with a page size different than the one requested, and clients should be prepared to respond accordingly.

GET https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$top=1
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$top=1",
  "value": [
    {
      "ListingKey": "a1",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z"
    }
  ]
}

RESO Web API Core servers MUST support the $top operator.


2.5.6 $count Operator

The $count system query option allows clients to request a count of the matching resources included with the resources in the response.

The $count query option has a Boolean value of true or false.

The semantics of $count are covered in the OData-Protocol document.

GET https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$top=1&$count=true
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$top=1&$count=true",
  "@odata.count": 2,
  "value": [
    {
      "ListingKey": "a1",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z"
    }
  ]
}

RESO Web API Core servers MUST support the $count operator.


2.5.7 $skip Operator

The $skip query option requests the number of items in the queried collection that are to be skipped and not included in the result. A client can request a particular page of items by combining $top and $skip.

The semantics of $top and $skip are covered in the OData-Protocol document. The OData-ABNF top and skip syntax rules define the formal grammar of the $top and $skip query options respectively.

GET https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$count=true&$top=1&$skip=1
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$count=true&$top=1&$skip=1",
  "@odata.count": 2,
  "value": [
    {
      "ListingKey": "b2",
      "ModificationTimestamp": "2020-04-02T02:02:02.007Z"
    }
  ]
}

RESO Web API Core servers MUST support the $skip operator but providers are allowed to decide for themselves how many records they want to allow skipping over.


### 2.5.8 $orderby Operator

The $orderby system query option allows clients to request resources in a particular order.

The semantics of $orderby are covered in the OData-Protocol document.

The OData-ABNF orderby syntax rule defines the formal grammar of the $orderby query option.

GET https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$orderby=ModificationTimestamp asc
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$orderby=ModificationTimestamp asc",
  "value": [
    {
      "ListingKey": "b2",
      "ModificationTimestamp": "2020-04-02T02:02:02.007Z"
    }
     {
      "ListingKey": "a1",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z"
    }
  ]
}

RESO Web API Core servers MUST support the $orderby operator.


2.5.9 $filter Operator

OData $filter expressions provide query support for boolean search expressions.

This includes logical operators such as AND, OR, and NOT, as well as greater than, greater than or equal, less than, less than or equal, and not equals for applicable OData primitive types, and query support for enumerations.

OData Primitive Types

Primitive types are things like integers, decimal numbers, dates, and timestamps.

Strings are not included in the RESO Web API Core specification at this time, but are expected to behave according to the OData specification, including case sensitivity.

Enumerations

Enumerations define the allowed values in a given lookup field.

They can either be single enumerations, where only one value is allowed within a given field, or multiple enumerations, in which case there is a list of values.

The standard values used in transport are determined by a given lookup field’s underlying data type.

At this document’s time of writing, most implementations use Edm.EnumType enumerations since they’ve historically been the only supported lookup data type. As such, the single and multiple enumeration examples contained in this document use Edm.EnumType and Collection(Edm.EnumType), respectively.

Note: support for Edm.String versions of enumerations, which use human-friendly display names as values, has recently been added and is the preferred approach for new implementations. The RESO community is in the process of moving away from Edm.EnumType lookups to simplify implementations and improve user friendliness. See RCP-032 for information about string lookups.


2.5.9.1 OData Primitive Types

This section outlines logical operators and query expressions available in Web API Core for the following OData Primitive Types:

Note: String query operators are not part of the RESO Web API Core specification at this time.

The quoted descriptions outlined in this document contain excerpts from the OData 4.01 Specification. Please see the sections linked to in each of the following sections for the most up-to-date information.

The examples presented here assume that the server is using a subset of RESO’s reference XML Metadata with the following Primitive Type fields available:

Enumerations are also shown in the sample payloads. Queries for enumerations are covered in later sections of this document.


2.5.9.2 Equals

OData Documentation

The eq operator returns true if the left operand is equal to the right operand, otherwise it returns false.

When applied to operands of entity types, the eq operator returns true if both operands represent the same entity, or both operands represent null.

When applied to operands of complex types, the eq operator returns true if both operands have the same structure and same values, or both operands represent null.

When applied to ordered collections, the eq operator returns true if both operands have the same cardinality and each member of the left operand is equal to the corresponding member of the right operand.

For services that support comparing unordered collections, the eq operator returns true if both operands are equal after applying the same ordering on both collections.

Each of the special values null, -INF, and INF is equal to itself, and only to itself.

The special value NaN is not equal to anything, even to itself.

Example

Total number of bedrooms equals 3.

GET https://api.reso.org/Property?$filter=BedroomsTotal eq 3
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=BedroomsTotal eq 3",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 3,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
  ]
}


2.5.9.3 Not Equals

OData Documentation

The ne operator returns true if the left operand is not equal to the right operand, otherwise it returns false.

When applied to operands of entity types, the ne operator returns true if the two operands do not represent the same entity.

When applied to operands of complex types, the ne operator returns true if the operands do not have the same structure and same values.

When applied to ordered collections, the ne operator returns true if both operands do not have the same cardinality or any member of the left operand is not equal to the corresponding member of the right operand.

For services that support comparing unordered collections, the ne operator returns true if both operands do not have the same cardinality or do not contain the same members, in any order.

Each of the special values null, -INF, and INF is not equal to any value but itself.

The special value NaN is not equal to anything, even to itself.

The null value is not equal to any value but itself.

Example

Total number of bedrooms does not equal 3.

GET https://api.reso.org/Property?$filter=BedroomsTotal ne 3
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=BedroomsTotal ne 3",
  "value": [
    {
      "ListingKey": "a2",
      "BedroomsTotal": 4,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-05-02T02:02:02.02Z",
      "ListingContractDate": "2020-05-02",
      "StandardStatus": "Active",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp"]
    }
  ]
}


2.5.9.4 Greater Than

OData Documentation

The gt operator returns true if the left operand is greater than the right operand, otherwise it returns false.

The special value INF is greater than any number, and any number is greater than -INF.

The Boolean value true is greater than false.

If any operand is null, the operator returns false.

Example

List price is greater than $100,000.00.

GET https://api.reso.org/Property?$filter=ListPrice gt 100000.00
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice gt 100000.00",
  "value": [
    {
      "ListingKey": "a3",
      "BedroomsTotal": 4,
      "ListPrice": 100000.01,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-06-02T02:02:02.02Z",
      "ListingContractDate": "2020-06-02",
      "StandardStatus": "Closed",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
  ]
}


2.5.9.5 Greater Than or Equal

OData Documentation

The ge operator returns true if the left operand is greater than or equal to the right operand, otherwise it returns false.

See rules for gt and eq for details.

Example

Modification timestamp is greater than or equal to May 22 2022 at midnight in UTC time.

GET https://api.reso.org/Property?$filter=ModificationTimestamp ge 2021-05-22T00:00:00Z
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ModificationTimestamp ge 2021-05-22T00:00:00Z",
  "value": [
    {
      "ListingKey": "a4",
      "BedroomsTotal": 4,
      "ListPrice": 100000.01,
      "StreetName": "1st",
      "ModificationTimestamp": "2021-05-22T00:01:01.01.123Z",
      "ListingContractDate": "2021-05-01",
      "StandardStatus": "Active",
      "AccessibilityFeatures": ["Visitable"]
    }
  ]
}


2.5.9.6 Less Than

OData Documentation

The lt operator returns true if the left operand is less than the right operand, otherwise it returns false.

The special value -INF is less than any number, and any number is less than INF.

The Boolean value false is less than true.

If any operand is null, the operator returns false.

Example

Listing contract date is less than Jan 1 2021.

GET https://api.reso.org/Property?$filter=ListingContractDate lt 2021-01-01
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListingContractDate lt 2021-01-01",
  "value": [
    {
      "ListingKey": "a5",
      "BedroomsTotal": 4,
      "ListPrice": 100000.01,
      "StreetName": "1st",
      "ModificationTimestamp": "2020-12-31T00:01:01.01.007Z",
      "ListingContractDate": "2020-12-31",
      "StandardStatus": "Closed",
      "AccessibilityFeatures": []
    }
  ]
}


2.5.9.7 Less Than or Equal

OData Documentation The le operator returns true if the left operand is less than or equal to the right operand, otherwise it returns false.

See rules for lt and eq for details.

Example

Listing contract date is less than or equal to Dec 31 2020.

GET https://api.reso.org/Property?$filter=ListingContractDate le 2020-12-31
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListingContractDate le 2020-12-31",
  "value": [
    {
      "ListingKey": "a5",
      "BedroomsTotal": 4,
      "ListPrice": 100000.01,
      "StreetName": "1st",
      "ModificationTimestamp": "2020-12-31T00:01:01.01.007Z",
      "ListingContractDate": "2020-12-31",
      "StandardStatus": "Closed",
      "AccessibilityFeatures": []
    }
  ]
}


2.5.9.8 Single Enumerations

These are single-valued lookups, such as the StandardStatus field.

There are two ways to express single enumerations in the RESO Web API Core specification:

Edm.EnumType enumerations are the most prevalent at the time of writing.

RESO is transitioning to Edm.String enumerations and new implementations should follow that path.


2.5.9.8.1 Edm.EnumType Enumerations

OData provides the Edm.EnumType data type to express enumerations. More information.

The Edm.EnumType data type supports has, eq, and ne queries.

The StandardStatus field is used in the examples in this section.

Assume given server has the following XML Metadata:

<?xml version="1.0" encoding="UTF-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="org.reso.metadata" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="Property">
        <Key>
          <PropertyRef Name="ListingKey"/>
        </Key>
        <Property MaxLength="255" Name="ListingKey" Type="Edm.String"/>
        <Property Name="ListPrice" Precision="14" Scale="2" Type="Edm.Decimal"/>
        <Property Name="StandardStatus" Type="org.reso.metadata.enums.StandardStatus"/>
        <Property Name="ModificationTimestamp" Precision="27" Type="Edm.DateTimeOffset"/>
      </EntityType>
    </Schema>
    <Schema Namespace="org.reso.metadata.enums" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EnumType Name="StandardStatus">
        <Member Name="Active"/>
        <Member Name="Closed"/>
        <Member Name="ComingSoon"/>
        <Member Name="Pending"/>
      </EnumType>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>


has Operator

The OData has operator is defined for items of Edm.EnumType as follows:

The has operator returns true if the right 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.

Example

Find listings where StandardStatus is Active.

GET https://api.reso.org/Property?$filter=StandardStatus has org.reso.metadata.enums.StandardStatus'Active'
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=StandardStatus has org.reso.metadata.enums.StandardStatus'Active'",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 2,
      "ListPrice": 100000.01,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-06-02T02:02:02.02Z",
      "ListingContractDate": "2020-06-02",
      "StandardStatus": "Active",
      "AccessibilityFeatures": ["AccessibleEntrance", "Visitable"]
    }
  ]
}

Note: one of the drawbacks of the OData Edm.EnumType data type is that it’s dependent on the namespace it was defined in. This is the reason the preceding query uses org.reso.metadata.enums.StandardStatus'Active' as part of the filter expression. There is no RESO standard for a single namespace to put standard enumerations in, so they vary among implementations. This is one reason RESO is migrating to Edm.String values instead, which don’t require a namespace.


eq Operator

OData added support for the eq operator on Edm.EnumType in version 4.01. It is included in the Web API Core specification. The syntax is similar to the has operator, and right hand values MUST use the correct namespaces as well.

Example

Find listings where StandardStatus is Active.

GET https://api.reso.org/Property?$filter=StandardStatus eq org.reso.metadata.enums.StandardStatus'Active'
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=StandardStatus eq org.reso.metadata.enums.StandardStatus'Active'",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 2,
      "ListPrice": 100000.01,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-06-02T02:02:02.02Z",
      "ListingContractDate": "2020-06-02",
      "StandardStatus": "Active",
      "AccessibilityFeatures": ["AccessibleEntrance", "Visitable"]
    }
  ]
}


ne Operator

OData also added support for the ne operator on Edm.EnumType in version 4.01. It is included in the Web API Core specification. This allows consumers to filter on enumerations using ne rather than not (has ...).

Right hand values MUST use correct namespaces.

Example

Find listings where StandardStatus is not Active.

GET https://api.reso.org/Property?$filter=StandardStatus ne org.reso.metadata.enums.StandardStatus'Active'
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=StandardStatus ne org.reso.metadata.enums.StandardStatus'Active'",
  "value": [
    {
      "ListingKey": "a2",
      "BedroomsTotal": 3,
      "ListPrice": 100000.00,
      "StreetName": "1st",
      "ModificationTimestamp": "2020-07-02T02:02:02.02Z",
      "ListingContractDate": "2020-07-02",
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": []
    }
  ]
}


2.5.9.8.2 Edm.String Enumerations

Support for string-based enumerations was added in Web API Core through the use of the Lookup resource, as outlined in RCP-032.

This resource is still in DRAFT status. Please contact RESO if you are interested in being certified using Edm.String lookups.


2.5.9.9 Multiple Enumerations

The Web API Core specification currently offers three ways to express multiple enumerations:

The IsFlags=true method is not recommended for current implementations due to size limitations of the underlying data type and will be deprecated in future versions of the Web API.

Currently, RESO’s reference XML metadata uses Collection(Edm.EnumType) for normative examples of multiple enumerations.

RESO is in the process of transitioning to human-friendly string values using Collection(Edm.String) for multiple enumerations, as outlined in RCP-032. New implementations are encouraged to take this approach. Please contact RESO if you are interested in being certified using string lookups.


2.5.9.9.1 OData IsFlags=true

In the past, it was common for implementations to use OData IsFlags enumerations. While this type is still supported for backwards compatibility, multiple enumerations should use Collection(Edm.EnumType) or Collection(Edm.String) instead.

The current RESO XML reference metadata uses Collection(Edm.EnumType).

One of the main reasons the IsFlags=true approach is being deprecated is that if the OData specification were followed strictly, it limits the total number of possible values to 64.

To understand why this is the case, OData uses an underlying type of at most Edm.Int64 to represent flag based enumerations. The design of the IsFlags=true lookup suggests that bitwise comparisons across multiple entries should be possible using this approach, meaning that if each item in the list is mutually exclusive there are only 64 choices.

The OData specification shows the following example for Enumeration Types:

<EnumType Name="Pattern" UnderlyingType="Edm.Int32" IsFlags="true">
  <Member Name="Plain"             Value="0" />
  <Member Name="Red"               Value="1" />
  <Member Name="Blue"              Value="2" />
  <Member Name="Yellow"            Value="4" />
  <Member Name="Solid"             Value="8" />
  <Member Name="Striped"           Value="16" />
  <Member Name="SolidRed"          Value="9" />
  <Member Name="SolidBlue"         Value="10" />
  <Member Name="SolidYellow"       Value="12" />
  <Member Name="RedBlueStriped"    Value="19" />
  <Member Name="RedYellowStriped"  Value="21" />
  <Member Name="BlueYellowStriped" Value="22" />
</EnumType>

Notice how Red has a value of 1 (20), Blue has a value of 2 (21), and Striped has a value of 16 (24), which is equivalent to the value of RedBlueStriped, with a value of 19 (0001 0011).

The underlying value in the payload from the example above would be 19, the Edm.Int64 representation of the value defined in the lookup. This MUST match what’s defined in the server metadata.

RESO does not explicitly validate bitmapped values to ensure that lookup choices do not overlap.


has Operator

The OData has operator is defined for items of Edm.EnumType as follows:

The has operator returns true if the right 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.

Example

Find listings with AccessibleEntrance in the AccessibilityFeatures field.

Assume AccessibleEntrance is defined with an underlying value of 4:

GET https://api.reso.org/Property?$filter=AccessibilityFeatures has org.reso.metadata.enums.AccessibilityFeatures'AccessibleEntrance'
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=AccessibilityFeatures has org.reso.metadata.enums.AccessibilityFeatures'AccessibleEntrance'",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 2,
      "ListPrice": 100000.01,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-06-02T02:02:02.02Z",
      "ListingContractDate": "2020-06-02",
      "StandardStatus": "Active",
      "AccessibilityFeatures": "AccessibleBedroom,AccessibleDoors"
    }
  ]
}


2.5.9.9.2 Collection of Edm.EnumType

The RESO Web API Core specification allows for multiple enumerations to be defined in terms of collections of OData Edm.EnumType definitions.

While this approach is not well documented in OData reference material, it is nonetheless valid and was previously offered in order to overcome the 64-value limitation of IsFlags enumerations.

Let’s consider the AccessibilityFeatures field, which is defined as follows in the RESO reference XML metadata:

  <EntityType Name="Property">
    <Property Name="AccessibilityFeatures" Type="Collection(org.reso.metadata.enums.AccessibilityFeatures)" />
  </EntityType>
  <EnumType Name="AccessibilityFeatures">
    <Member Name="AccessibleEntrance"/>
    <Member Name="AccessibleFullBath"/>
    <Member Name="AccessibleHallways"/>
    <Member Name="AccessibleKitchen"/>
  </EnumType>


Collection Queries

Multiple enumerations with the Collection(Edm.EnumType) data type use any() and all() lambda operators rather than the has operator, as outlined in IsFlags Enumerations.

Similar to other Edm.EnumType definitions, queries rely on namespaces being present, when defined.

From the OData specification:

OData defines two operators that evaluate a Boolean expression on a collection. Both must be prepended with a navigation path that identifies a collection.

4.01 Services MUST support case-insensitive lambda operator names. Clients that want to work with 4.0 services MUST use lower case lambda operator names.

The argument of a lambda operator is a case-sensitive lambda variable name followed by a colon (:) and a Boolean expression that uses the lambda variable name to refer to properties of members of the collection identified by the navigation path.

If the name chosen for the lambda variable matches a property name of the current resource referenced by the resource path, the lambda variable takes precedence. Clients can prefix properties of the current resource referenced by the resource path with $it.

Other path expressions in the Boolean expression neither prefixed with the lambda variable nor $it are evaluated in the scope of the collection instances at the origin of the navigation path prepended to the lambda operator.


any() Operator

The any operator applies a Boolean expression to each member of a collection and returns true if and only if the expression is true for any member of the collection, otherwise it returns false. This implies that the any operator always returns false for an empty collection.

The any operator can be used without an argument expression. This short form returns false if and only if the collection is empty.

Example

Find listings where AccessibilityFeatures has AccessibleEntrance, including other values:

GET https://api.reso.org/Property?$filter=AccessibilityFeatures/any(enum:enum eq org.reso.metadata.enums.AccessibilityFeatures'AccessibleEntrance')
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=AccessibilityFeatures/any(enum:enum eq org.reso.metadata.enums.AccessibilityFeatures'AccessibleEntrance')",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 3,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
  ]
}


all() Operator

The all operator applies a Boolean expression to each member of a collection and returns true if the expression is true for all members of the collection, otherwise it returns false. This implies that the all operator always returns true for an empty collection.

The all operator cannot be used without an argument expression.

Example

Find all listings with only the AccessibilityFeatures Visitable flag set:

GET https://api.reso.org/Property?$filter=AccessibilityFeatures/all(enum:enum eq org.reso.metadata.enums.AccessibilityFeatures'Visitable')
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=AccessibilityFeatures/all(enum:enum eq org.reso.metadata.enums.AccessibilityFeatures'Visitable')",
  "value": [
    {
      "ListingKey": "a39",
      "BedroomsTotal": 3,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["Visitable"]
    }
  ]
}


2.5.9.9.3 Collection of Edm.String

Support for string-based enumerations was added in Web API Core through the use of the Lookup resource, as outlined in RCP-032.

This resource is still in DRAFT status. Please contact RESO if you are interested in being certified using Edm.String lookups.


2.6 Response Codes and Error Message Bodies

This section describes expected response codes and error message bodies.


2.6.1 HTTP Response Codes

A compatible server implementation MUST return a valid HTTP status code for each request indicating the status of the request.

If the response was not successful the server MAY include an error message in the body of the HTTP response. There is a defined response body for JSON but there is no explicit requirement in the OData standard.

Code Short Description Detail
200 OK Returned by GET method when retrieving a record or records. If no records are found an empty result set is returned.
202 Accepted Returned by GET method to indicate that the server received the request but that it may take time to fulfill a response.
400 Bad Request Returned by GET method calls when the data fails validation and more detail on the error may be found in the body of the response.
403 Forbidden Returned when the selected Authentication mechanism is not successful.
404 Not Found Returned when a GET cannot find a resource or collection.
413 Request Entity Too Large Returned at the discretion of the server. Used to indicate when the server cannot handle the complexity of the specific request.
415 Unsupported Media Returned when a media format requested is not supported by the system.
429 Too Many Requests Returned at the discretion of the server. Used to indicate that the user / licensee has met or exceeded their allowed usage (transactions per second, per day, per month, etc.
500 Internal Server Error Returned when an unexpected error is encountered and more detail may be provided in the response body.
501 Not Implemented Returned when the requested method is not available.


2.6.2 Error Message Bodies

When the client makes a request which cannot be satisfied or produces an error condition, a compliant server MUST follow the OData error handling guidelines.

Full details of this mechanism may be found in the JSON Error Response section of the OData specification.

The following example includes a client request and a compliant server error response for reference:

GET https://api.reso.org/reso/odata/Member?$orderby=ModificationTimestamp&$top=5&$skip=5
HTTP/2 200 OK
{
  "error": {
    "code": "501",
    "message": "Unsupported functionality",
    "target": "query",
    "details": [
      {
       "code": "501",
       "target": "$skip", 
       "message": "Resource does not support the $skip parameter"
      }
    ],
    "innererror": {
      "trace": [],
      "context": {}
    } 
  }
}


2.7 Standard Resources

In general, the expectation is for the RESO Web API to output data as per the RESO Data Dictionary when a given data element is defined.

As of Web API 1.0.2+ and Data Dictionary 1.6+, at least ONE of the following Data Dictionary resources MUST be present for a server to be considered compliant for Certification:

Note: Additional standard resources may be added to this list in future versions of the specification.

Servers MAY support more than one version of the RESO Data Dictionary and may also define additional resources to support specific use cases. For example, a server could provide a “Mobile” resource that returns a condensed list of fields to reduce the size of a response. However, if this resource contains standard Property resource fields, the Property resource MUST also be available on the given server during Certification even if it’s not visible in production for certain roles.

Servers MAY support local resources, fields, or lookups that don’t follow the RESO Data Dictionary specifications, and may extend any of the existing standard resources or lookups with their own localized values, except where otherwise noted. For instance, StandardStatus is a closed enumeration in the Property resource and may not be extended.


2.8 Core Query Examples

The following examples show how Core queries can be used to query data on a given RESO Web API server.


Get Properties Listed in December of 2020

GET https://api.reso.org/Property?$filter=ListingContractDate ge 2020-12-01 and ListingContractDate lt 2021-01-01
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListingContractDate ge 2020-12-01 and ListingContractDate lt 2021-01-01",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 5,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
  ]
}


Get Properties Listed in a Given Year

GET https://api.reso.org/Property?$filter=ListingContractDate ge 2020-01-01 and ListingContractDate lt 2021-01-01
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListingContractDate ge 2020-01-01 and ListingContractDate lt 2021-01-01",
  "value": [
    {
      "ListingKey": "b2",
      "BedroomsTotal": 3,
      "ListPrice": 200000.00,
      "StreetName": "2nd",
      "ModificationTimestamp": "2020-12-31T00:01:01.01.007Z",
      "ListingContractDate": "2020-05-25",
      "StandardStatus": "Closed",
      "AccessibilityFeatures": []
    }
  ]
}


Get Active Members with First Name ‘James’ or ‘Adam’

GET https://api.reso.org/Member?$filter=MemberStatus eq 'Active' and (MemberFirstName eq 'James' or MemberFirstName eq 'Adam')
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Member?$filter=MemberStatus eq 'Active' and (MemberFirstName eq 'James' or MemberFirstName eq 'Adam')",
  "value": [
    {
      "MemberKey": "a1",
      "MemberStatus": "Active",
      "MemberFirstName": "James",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
     {
      "MemberKey": "b2",
      "MemberStatus": "Active",
      "MemberFirstName": "Adam",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
  ]
}


Query on Boolean Field to Find Short Sales

GET https://api.reso.org/Property?$filter=ShortSale eq true
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ShortSale eq true",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 5,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "ShortSale": true,
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
  ]
}


Combine Multiple Criteria in a Listing Search

GET https://api.reso.org/Property?$filter=ListPrice gt 250000 and ListPrice lt 500000
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice gt 250000 and ListPrice lt 500000",
  "value": [
    {
      "ListingKey": "c3",
      "BedroomsTotal": 5,
      "ListPrice": 400000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "Active",
      "AccessibilityFeatures": ["Visitable"]
    }
  ]
}


Get Properties with a Listing Price Greater Than $300K

GET https://api.reso.org/Property?$filter=ListPrice gt 300000
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice gt 300000",
  "value": [
    {
      "ListingKey": "c3",
      "BedroomsTotal": 5,
      "ListPrice": 400000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "Active",
      "AccessibilityFeatures": ["Visitable"]
    }
  ]
}


Get Properties with a Listing Price of $300K

GET https://api.reso.org/Property?$filter=ListPrice eq 300000
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice eq 300000",
  "value": [
    {
      "ListingKey": "d4",
      "BedroomsTotal": 4,
      "ListPrice": 300000.00,
      "StreetName": "Oak",
      "ModificationTimestamp": "2021-08-15T00:01:01.01.007Z",
      "StandardStatus": "Active",
      "AccessibilityFeatures": []
    }
  ]
}


Retrieve Records in a Specific Order

GET https://api.reso.org/Property?$filter=ListPrice lt 500000&$orderby=ListPrice desc
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice lt 500000&$orderby=ListPrice desc",
  "value": [
    {
      "ListingKey": "e5",
      "BedroomsTotal": 5,
      "ListPrice": 499999,
      "StreetName": "7th",
      "ModificationTimestamp": "2021-06-25T00:01:01.01.007Z",
      "StandardStatus": "Active",
      "AccessibilityFeatures": []
    }
    {
      "ListingKey": "f6",
      "BedroomsTotal": 5,
      "ListPrice": 489000,
      "StreetName": "Maple",
      "ModificationTimestamp": "2021-06-25T00:01:01.01.007Z",
      "StandardStatus": "Active",
      "AccessibilityFeatures": []
    }
  ]
}


Get a Count of Property Records

GET https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$top=1&$count=true
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$top=1&$count=true",
  "@odata.count": 2,
  "value": [
    {
      "ListingKey": "a1",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z" 
    }
  ]
}


Get Top Five Residential Properties

GET https://api.reso.org/Property?$filter=PropertyType eq 'Residential'&$top=5
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=PropertyType eq 'Residential'&$top=5",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 5,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
    {
    "ListingKey": "b2",
    "BedroomsTotal": 5,
    "ListPrice": 400000.00,
    "StreetName": "Main",
    "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
    "ListingContractDate": "2020-04-02",
    "StandardStatus": "Active",
    "AccessibilityFeatures": ["Visitable"]
    }
    {
    "ListingKey": "c3",
    "BedroomsTotal": 4,
    "ListPrice": 300000.00,
    "StreetName": "Oak",
    "ModificationTimestamp": "2021-08-15T00:01:01.01.007Z",
    "StandardStatus": "Active",
    "AccessibilityFeatures": []
    }
    {
    "ListingKey": "d4",
    "BedroomsTotal": 5,
    "ListPrice": 499999,
    "StreetName": "7th",
    "ModificationTimestamp": "2021-06-25T00:01:01.01.007Z",
    "StandardStatus": "Active",
    "AccessibilityFeatures": []
    }
    {
    "ListingKey": "e5",
    "BedroomsTotal": 5,
    "ListPrice": 489000,
    "StreetName": "Maple",
    "ModificationTimestamp": "2021-06-25T00:01:01.01.007Z",
    "StandardStatus": "Active",
    "AccessibilityFeatures": []
    }
  ]
}
  


Get the First Five Members

GET https://api.reso.org/Member?$top=5&$skip=0
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Member?$top=5&$skip=0",
  "value": [
       {
      "MemberKey": "a1",
      "MemberStatus": "Active",
      "MemberFirstName": "Angela",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
     {
      "MemberKey": "b2",
      "MemberStatus": "Active",
      "MemberFirstName": "Betty",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
    {
      "MemberKey": "c3",
      "MemberStatus": "Active",
      "MemberFirstName": "Henry",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
    {
      "MemberKey": "d4",
      "MemberStatus": "Active",
      "MemberFirstName": "Kevin",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
    {
      "MemberKey": "e5",
      "MemberStatus": "Active",
      "MemberFirstName": "Timothy",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
  ]
}


Get the Next Five Members

GET https://api.reso.org//Member?$top=5&$skip=5
HTTP/2 200 OK
{
  "@odata.context": "GET https://api.reso.org//Member?$top=5&$skip=5",
  "value": [
       {
      "MemberKey": "f6",
      "MemberStatus": "Active",
      "MemberFirstName": "James",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
     {
      "MemberKey": "g7",
      "MemberStatus": "Active",
      "MemberFirstName": "Adam",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
    {
      "MemberKey": "h8",
      "MemberStatus": "Active",
      "MemberFirstName": "Jennifer",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
    {
      "MemberKey": "i9",
      "MemberStatus": "Active",
      "MemberFirstName": "Kevin",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
    {
      "MemberKey": "j10",
      "MemberStatus": "Active",
      "MemberFirstName": "Theresa",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.01.007Z"
    }
  ]
}

Note: The implementation of $top and $orderby is defined by the server and may restrict what values may be used in either option. A compliant client SHOULD use the $orderby query to sustain consistency between requests, however a compliant server is not required to guarantee consistent results between requests.


Get Properties with a Listing Price of Less than $300K

GET https://api.reso.org/Property?$filter=ListPrice lt 300000
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice lt 300000",
  "value": [
    {
      "ListingKey": "a1",
      "BedroomsTotal": 5,
      "ListPrice": 100000.00,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
  ]
}


Get Properties with a Price Range of $250k to $500k

GET https://api.reso.org/Property?$filter=ListPrice gt 250000 and ListPrice lt 500000
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$filter=ListPrice gt 250000 and ListPrice lt 500000",
  "value": [
    {
      "ListingKey": "b2",
      "BedroomsTotal": 4,
      "ListPrice": 300000.00,
      "StreetName": "Oak",
      "ModificationTimestamp": "2021-08-15T00:01:01.01.007Z",
      "StandardStatus": "Active",
      "AccessibilityFeatures": []
    }
  ]
}


Select Specific Field Values

GET https://api.reso.org/Member?$select=MemberLastName,MemberFirstName,MemberMlsId
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Member?$select=MemberLastName,MemberFirstName,MemberMlsId",
  "value": [
    {
      "MemberLastName": "Smith",
      "MemberFirstName": "James",
      "MemberMlsId": "JSMITH"
    }
  ]
}

Note: All names in the $select option are case-sensitive to match the names of elements provided by the resource.


Get Most Recent ListingKey and ModificationTimestamp in Descending Order

GET https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$orderby=ModificationTimestamp desc
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property?$select=ListingKey,ModificationTimestamp&$orderby=ModificationTimestamp desc",
  "value": [
    {
      "ListingKey": "b2",
      "ModificationTimestamp": "2021-08-15T00:01:01.01.007Z"
    }
    {
      "ListingKey": "a1",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z"
    }
  ]
}


Get a Single Property Record

GET https://api.reso.org/Property('a3')
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Property('a3')",
  "ListingKey": "a3",
  "BedroomsTotal": 3,
  "ListPrice": 200000,
  "StreetName": "3rd",
  "ModificationTimestamp": "2021-09-12T00:01:01.01.007Z",
  "StandardStatus": "Active",
  "AccessibilityFeatures": []
}

Note: this is referred to as a “Singleton Property” in the OData specification. Notice how the return value of the item is located at the top level rathre than in a value array.


Filter by Multiple Field Values

GET https://api.reso.org/Member?$filter=MemberFirstName eq 'Joe' and MemberLastName eq 'Smith'
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Member?$filter=MemberFirstName eq 'Joe' and MemberLastName eq 'Smith'",
  "value": [
    {
      "MemberKey": "a1",
      "MemberStatus": "Active",
      "MemberFirstName": "Joe",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2020-02-21T00:01:01.01.007Z"
    }
  ]
}

Note: Query strings MUST be URL encoded where appropriate by a compliant client.


2.9 Security

Servers MUST implement one of the following OAuth2 authentication methods to be compliant with the RESO Web API specification:

Note: The Open ID Connect layer was previously supported by the RESO Web API. As of Web API 1.0.2, RESO only supports Bearer tokens and Client Credentials during Certification.


Section 3: Certification

3.1 Purpose

The goal of the Web API 2.0.0 Core specification is to provide a common, stable set of authentication protocols and API functionality to meet the needs of the real estate industry, with the intent that the Core specification will rarely change going forward.

Endorsements will be used to provide additional functionality to the Core specification in a modular manner and treated as separate specifications with their own dependencies, one of which may or may not be a dependency on Web API Core.

This section will focus exclusively on the Web API Core specification.

3.2 Background

The RESO Web API provides an open standard for a RESTful, JSON-based API that’s centered around the RESO Data Dictionary, with the ability to support local extension. At its core, the RESO Web API standard is based on a subset of the OData specification from OASIS.

The OData specification consists of the following:

Each of these items MUST be valid with respect to OData for a Web API server to be considered compliant.

Additionally, there are RESO requirements beyond those of OData. For instance, Web API Servers MUST expose at least one Property, Member, Office, Media, or InternetTracking Data Dictionary resource in order to be certified.

There are also authentication requirements which, at the time of writing, are servers that MUST support OAuth2 Auth Tokens OR Client Credentials.

The Web API Core testing rules ensure that server metadata are compliant, the data types provided by the RESO Data Dictionary support a minimum set of query operations valid for their types, that the query and response format are correct, and that the results logically match the query that was being used.

3.3 Testing Framework

RESO Web API Core certification is provided by the RESO Commander.

The RESO Commander is an open source, cross-platform Java library created by RESO that uses established community libraries, such as the Apache Olingo OData Client, XML parsers, and JSON Schema Validators, to provide a testing API.

Web API tests are written in a high-level testing language (DSL) called Gherkin. This is part of a Behavior Driven Development (BDD) platform called Cucumber that allows for the expression of testing workflows using a natural language that is intended to be accessible to business analysts, QA testers, and programmers alike.

A command-line interface has been provided during the initial development phase as an entry point into the testing API. This provides the environment used for certification, self-assessment, or even a test automation server in a continuous integration and deployment platform such as GitHub CI, Jenkins, Travis, or CircleCI to help prevent regressions in a RESO-certified codebase.

A graphical user interface is also available through popular and free Integrated Development Environment (IDE) plugins for IntelliJ and Eclipse. IDEs provide a superior testing platform, as they provide better informational messages and are able to run and debug the entire test suite or a given individual test. The availability of plugins saves significant time in testing, development, and certification. The level of community support is one of the reasons Cucumber was chosen as a testing platform.

3.4 Testing Methodology

Configuring the Test Client

Configuration of the RESO Commander for Web API Certification involves providing a service endpoint, authentication, resource, and field information in a template that will be used during the automated testing process.

A blank Web API Core template may be found in the root of the RESO Commander project.

There is also a sample template used internally for acceptance testing of the Web API testing tool. The sample template provides a useful reference when filling out RESOScript files.

Items marked as REQUIRED in the configuration file MUST be completed, but things like sample field values have already been provided and should be sufficient for testing. If not, they also may be changed.

Metadata Request Using RESO Standard Authentication

When testing begins, an HTTP request is made to an applicant’s given service location with either OAuth2 Bearer Tokens or Client Credentials.

Both of these authentication strategies allow for data consumption to be machine automated so that additional interaction from a user isn’t necessary during the authentication process. As such, the RESO Data Dictionary Commander can be used for automated testing.

The metadata request is expected to function according to the OData specification in terms of request and response headers and response formats.

RESO specifically uses the XML version of OData metadata, which contains an Entity Data Model (EDM) and model definitions, and is often referred to as EDMX.

Metadata Validation

Syntax Checking

Metadata returned from a RESO Web API server are checked for XML validity as well as validated against Entity Data Model (EDM) and EDMX definitions published by OASIS, the creators of the OData specification. If metadata are invalid for any reason, Data Dictionary testing will halt.

Semantic Checking

After metadata syntax has been validated, declared data models are checked for correctness.

For example, if a given server declares they support the RESO Property resource, then the RESO Commander will look for an OData EntityType definition for Property. If the underlying data model is not found, metadata validation will fail with a diagnostic message to help users understand why a given error occurred. Once the model is found, its field and enumeration definitions will be checked for correctness as well.

Another aspect of semantic checking is ensuring that all models have keys so they can be indexed, meaning that a data request can be made to the server by key. This is a basic requirement for fetching data from a server.

RESO Certification

Certification of Web API servers consists mainly of ensuring that a core set of required query operations are supported in a manner adhering to the RESO and OData specifications, and that servers send the appropriate response for each query. These fields are preconfigured in a file, as mentioned in the section on Configuration, rather than sampled from the RESO Data Dictionary.

Data Dictionary resources, fields, and enumerations MUST be used in the configuration of the testing tool for RESO Certification.

In addition to comparison operators, such as greater and less than for things like Integers and Timestamps, OData query operators such as $select, which allows the consumer to specify a list of fields to be returned in the payload, or $top which allows the consumer to specify the size of the result set. These are outlined in the next section.

3.5 Web API Core Testing Queries

The following queries are used during Web API Core testing.

Each item links to its relevant acceptance test in the RESO Commander repository.

Sample queries assume that https://api.reso.org/ is being used as the OData service root.


Request and Validate OData XML Metadata

| Item | Details | | – | – | | Id| metadata-validation | | Description | Request and Validate Server Metadata | | Sample Query | GET https://api.reso.org/$metadata?$format=application/xml | | Section | 2.5.1 | | Acceptance Test | Source | | Notes | See: Metadata Validation |


Service Document Request

| Item | Details | | – | – | | Id | service-document | | Description | Request and validate OData service document | | Sample Query | GET https://api.reso.org/ | | Section | 2.5.2 | | Acceptance Test | Source | | Notes | See: OData service document request |


Fetch by Key

| Item | Details | | – | – | | Id | fetch-by-key | | Description | Allows Records to be retrieved by primary key. | | Sample Query | GET https://api.reso.org/Property('12345')?$select=ListingKey | | Section | 2.5.3 | | Acceptance Test | Source | | Notes | Data Indexability by Key Requirement. |


$select Query Option

| Item | Details | | – | – | | Id | select | | Description | $select allows fields to be requested on an individual basis as part of a query. | | Sample Query | GET https://api.reso.org/Property?$select=ListingKey,BedroomsTotal | | Section | 2.5.4 | | Acceptance Test | Source | | Notes | The $select list determines the “data shape” of the response for a given query. |


$top Query Option

| Item | Details | | – | – | | Id | top | | Description | $top allows the client to request a specific number of records in a query. | | Sample Query | GET https://api.reso.org/Property?$top=5 | | Section | 2.5.5 | | Acceptance Test | Source | | Notes | None |


$count Query Option

| Item | Details | | – | – | | Id | top | | Description | The $count system query option with a value of true specifies that the total |count of items within a collection matching the request be returned along with the result. | | Sample Query | GET https://api.reso.org/Property?$top=0&$count=true | | Section | 2.5.6 | | Acceptance Test | Source | | Notes | None |


$skip Query Option

| Item | Details | | – | – | | Id | top | | Description | $top allows the client to request a specific number of records in a query. | | Sample Query | GET https://api.reso.org/Property?$top=5&$skip=5 | | Section | 2.5.7 | | Acceptance Test | Source | | Notes | Use $top and $skip in conjunction to page. |


$orderby ISO 8601 Timestamp Field Ascending

| Item | Details | | – | – | | Id | orderby-timestamp-asc | | Description | $orderby allows results to be returned in a specified order. | | Sample Query | GET https://api.reso.org/Property?$top=20&$select=ListingKey,BedroomsTotal,ModificationTimestamp&$orderby=ModificationTimestamp asc | | Section | 2.5.8 | | Acceptance Test | Source | | Notes | More information |


$orderby ISO 8601 Timestamp Field Descending

| Item | Details | | – | – | | Id | orderby-timestamp-desc | | Description | $orderby allows results to be returned in a specified order. | | Sample Query | GET https://api.reso.org/Property?$top=20&$select=ListingKey,BedroomsTotal,ModificationTimestamp&$orderby=ModificationTimestamp desc | | Section | 2.5.8 | | Acceptance Test | Source | | Notes | More information |


$orderby ISO 8601 Timestamp Field Ascending with Integer gt Filter

| Item | Details | | – | – | | Id | orderby-timestamp-asc-filter-int-gt | | Description | $orderby allows results to be returned in a specified order. | | Sample Query | GET https://api.reso.org/Property?$top=20&$select=ListingKey,BedroomsTotal,ModificationTimestamp&$orderby=ModificationTimestamp asc&$filter=BedroomsTotal gt 3 | | Section | 2.5.8 | | Acceptance Test | Source | | Notes | More information |


$orderby ISO 8601 Timestamp Field Descending with Integer gt Filter

| Item | Details | | – | – | | Id | orderby-timestamp-desc-filter-int-gt | | Description | $orderby allows results to be returned in a specified order. | | Sample Query | GET https://api.reso.org/Property?$top=20&$select=ListingKey,BedroomsTotal,ModificationTimestamp&$orderby=ModificationTimestamp desc&$filter=BedroomsTotal gt 3 | | Section | 2.5.8 | | Acceptance Test | Source | | Notes | More information |


Filter Integer Field Using and Logical Operator

| Item | Details | | – | – | | Id | filter-int-and | | Description | $filter with and logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal gt 3 and BedroomsTotal lt 10 | | Section | 2.5.9.1 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.7 |


Filter Integer Field Using or Logical Operator

| Item | Details | | – | – | | Id | filter-int-or | | Description | $filter with or logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal lt 10 or BedroomsTotal gt 3 | | Section | 2.5.9.1 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.8 |


Filter Integer Field Using not () Logical Operator

| Item | Details | | – | – | | Id | filter-int-not | | Description | $filter with not logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=not (BedroomsTotal le -1) | | Section | 2.5.9.1 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.9 |


Filter Integer Field Using eq Logical Operator

| Item | Details | | – | – | | Id | filter-int-eq | | Description | $filter with eq logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal eq 3 | | Section | 2.5.9.2 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.1 |


Filter Integer Field Using ne Logical Operator

| Item | Details | | – | – | | Id | filter-int-ne | | Description | $filter with ne logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal ne 3 | | Section | 2.5.9.3 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.2 |


Filter Integer Field Using gt Logical Operator

| Item | Details | | – | – | | Id | filter-int-gt | | Description | $filter with gt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal gt 3 | | Section | 2.5.9.4 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.3 |


Filter Integer Field Using ge Logical Operator

| Item | Details | | – | – | | Id | filter-int-ge | | Description | $filter with ge logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal ge 3 | | Section | 2.5.9.5 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.4 |


Filter Integer Field Using lt Logical Operator

| Item | Details | | – | – | | Id | filter-int-lt | | Description | $filter with lt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal lt 3 | | Section | 2.5.9.6 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.5 |


Filter Integer Field Using le Logical Operator

| Item | Details | | – | – | | Id | filter-int-le | | Description | $filter with le logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,BedroomsTotal&$filter=BedroomsTotal le 3 | | Section | 2.5.9.7 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.6 |


Filter Decimal Field Using ne Logical Operator

| Item | Details | | – | – | | Id | filter-decimal-ne | | Description | $filter with ne logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListPrice&$filter=ListPrice ne 0.00 | | Section | 2.5.9.3 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.2 |


Filter Decimal Field Using gt Logical Operator

| Item | Details | | – | – | | Id | filter-decimal-gt | | Description | $filter with gt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListPrice&$filter=ListPrice gt 0.00 | | Section | 2.5.9.4 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.4 |


Filter Decimal Field Using ge Logical Operator

| Item | Details | | – | – | | Id | filter-decimal-ge | | Description | $filter with ge logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListPrice&$filter=ListPrice ge 0.00 | | Section | 2.5.9.5 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.5 |


Filter Decimal Field Using lt Logical Operator

| Item | Details | | – | – | | Id | filter-decimal-lt | | Description | $filter with lt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListPrice&$filter=ListPrice lt 1234567.89 | | Section | 2.5.9.6 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.3 |


Filter Decimal Field Using le Logical Operator

| Item | Details | | – | – | | Id | filter-decimal-le | | Description | $filter with le logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListPrice&$filter=ListPrice le 1234567.89 | | Section | 2.5.9.7 | | Acceptance Test | Source | | Notes | See OData 5.1.1.1.4 |


Filter ISO 8601 Date Using eq Logical Operator

| Item | Details | | – | – | | Id | filter-date-eq | | Description | $filter ISO 8601 date in YYYY-MM-DD format with eq logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListingContractDate&$filter=ListingContractDate eq 2019-12-31 | | Section | 2.5.9.2 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.1 |


Filter ISO 8601 Date Using ne Logical Operator

| Item | Details | | – | – | | Id | filter-date-ne | | Description | $filter ISO 8601 date in YYYY-MM-DD format with ne logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListingContractDate&$filter=ListingContractDate ne 2019-12-31 | | Section | 2.5.9.3 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.1 |


Filter ISO 8601 Date Using gt Logical Operator

| Item | Details | | – | – | | Id | filter-date-gt | | Description | $filter ISO 8601 date in YYYY-MM-DD format with gt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListingContractDate&$filter=ListingContractDate gt 2019-12-31 | | Section | 2.5.9.4 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.1 |


Filter ISO 8601 Date Using ge Logical Operator

| Item | Details | | – | – | | Id | filter-date-ge | | Description | $filter ISO 8601 date in YYYY-MM-DD format with ge logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListingContractDate&$filter=ListingContractDate ge 2019-12-31 | | Section | 2.5.9.5 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.1 |


Filter ISO 8601 Date Using lt Logical Operator

| Item | Details | | – | – | | Id | filter-date-lt | | Description | $filter ISO 8601 date in YYYY-MM-DD format with lt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListingContractDate&$filter=ListingContractDate lt 2019-12-31 | | Section | 2.5.9.6 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.1 |


Filter ISO 8601 Date Using le Logical Operator

| Item | Details | | – | – | | Id | filter-date-le | | Description | $filter ISO 8601 date in YYYY-MM-DD format with le logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ListingContractDate&$filter=ListingContractDate le 2019-12-31 | | Section | 2.5.9.7 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.1 |


Filter ISO 8601 Timestamp Using ne Logical Operator

| Item | Details | | – | – | | Id | filter-datetime-ne | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with ne logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp ne 2019-12-31T23:55:55-09:00 | | Section | 2.5.9.3 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.11 |


Filter ISO 8601 Timestamp Using gt Logical Operator

| Item | Details | | – | – | | Id | filter-datetime-gt | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with gt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp gt 2019-12-31T23:55:55-09:00 | | Section | 2.5.9.4 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.11 |


Filter ISO 8601 Timestamp Using ge Logical Operator

| Item | Details | | – | – | | Id | filter-datetime-ge | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with ge logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp ge 2019-12-31T23:55:55-09:00 | | Section | 2.5.9.5 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.11 |


Filter ISO 8601 Timestamp Using lt Logical Operator

| Item | Details | | – | – | | Id | filter-datetime-lt | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with lt logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp lt 2020-12-31T23:55:55-09:00 | | Section | 2.5.9.6 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.11 |


Filter ISO 8601 Timestamp Using le Logical Operator

| Item | Details | | – | – | | Id | filter-datetime-le | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with le logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp le 2020-12-31T23:55:55-09:00 | | Section | 2.5.9.7 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.11 |


Filter ISO 8601 Timestamp Using ne Logical Operator and now()

| Item | Details | | – | – | | Id | filter-datetime-ne-now | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with ne logical operator. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp ne 2019-12-31T23:55:55-09:00 | | Section | 2.5.9.3 | | Acceptance Test | Source | | Notes | See OData 5.1.1.6.11 |


Filter ISO 8601 Timestamp Using le Logical Operator and now()

| Item | Details | | – | – | | Id | filter-datetime-lt-now | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with le logical operator and the OData now() function. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp lt now() | | Section | 2.5.9.6 | | Acceptance Test | Source | | Notes | See OData 5.1.1.8.9 |


Filter ISO 8601 Timestamp Using LE Logical Operator and now()

| Item | Details | | – | – | | Id | filter-datetime-le-now | | Description | $filter ISO 8601 timestamp in YYYY-MM-DD format with le logical operator and the OData now() function. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,ModificationTimestamp&$filter=ModificationTimestamp lt now() | | Section | 2.5.9.7 | | Acceptance Test | Source | | Notes | See OData 5.1.1.8.9 |


Filter Single Enumeration Using has and OData Edm.EnumType

| Item | Details | | – | – | | Id | filter-enum-single-has | | Description | has operator for Edm.EnumType. | | **Sample Query** | GET https://api.reso.org/Property?$top=5&$select=ListingKey,PropertyType&$filter=PropertyType has PropertyEnums.PropertyType'Residential'` | | Section | 2.5.9.8.1 | | Acceptance Test | Source | | Notes | More Information |


Filter Single Enumeration Using eq and OData Edm.EnumType

| Item | Details | | – | – | | Id | filter-enum-single-eq | | Description | eq operator for Edm.EnumType. | | **Sample Query** | GET https://api.reso.org/Property?$top=5&$select=ListingKey,PropertyType&$filter=PropertyType eq PropertyEnums.PropertyType'Residential'` | | Section | 2.5.9.8.1 | | Acceptance Test | Source | | Notes | More Information |


Filter Single Enumeration Using ne and OData Edm.EnumType

| Item | Details | | – | – | | Id | filter-enum-single-ne | | Description | ne operator for Edm.EnumType. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,PropertyType&$filter=PropertyType ne PropertyEnums.PropertyType'Residential' | | Section | 2.5.9.8.1 | | Acceptance Test | Source | | Notes | More Information |


Filter Multiple Enumeration Using has and OData Edm.EnumType

| Item | Details | | – | – | | Id | filter-enum-multi-has | | Description | has operator for Edm.EnumType and IsFlags=true. | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,Appliances&$filter=Appliances has PropertyEnums.Appliances'Refrigerator' | | Section | 2.5.9.9.1 | | Acceptance Test | Source | | Notes | More Information |


Filter Multiple Enumeration Using has and OData Edm.EnumType with and Logical Operator

| Item | Details | | – | – | | Id | filter-enum-multi-has-and | | Description | has operator for Edm.EnumType and IsFlags=true with and logical operator. | | **Sample Query** | GET https://api.reso.org/Property?$top=5&$select=ListingKey,Appliances&$filter=Appliances has PropertyEnums.Appliances'Refrigerator' and Appliances has PropertyEnums.Appliances'Stacked'` | Section | 2.5.9.9.1 | | Acceptance Test | Source | | Notes | More Information |


Filter Multiple Enumeration Using any Lambda Operator and OData Collection(Edm.EnumType)

| Item | Details | | – | – | | Id | filter-coll-enum-any | | Description | any lambda for Collection(Edm.EnumType). | | Sample Query | GET https://api.reso.org/Property?$top=5&$select=ListingKey,Appliances&$filter=Appliances/any(enum:enum eq PropertyEnums.Appliances'Refrigerator') | | Section | 2.5.9.9.2 | | Acceptance Test | Source | | Notes | See OData 5.1.1.10.1 |


Filter Multiple Enumeration Using all Lambda Operator and OData Collection(Edm.EnumType)

| Item | Details | | – | – | | Id | filter-coll-enum-any | | Description | any lambda for Collection(Edm.EnumType). | | Sample Query | ```GET https://api.reso.org/Property?$top=5&$select=ListingKey,Appliances&$filter=Appliances/all(enum:enum eq PropertyEnums.Appliances’Refrigerator’) `| | Section | 2.5.9.9.2 | | Acceptance Test | Source | | Notes | See OData 5.1.1.10.2 |


HTTP 400 Response Code Test

| Item | Details | | – | – | | Id | response-code-400 | | Description | Issues query to trigger HTTP 400 response code. | | Sample Query | GET https://api.reso.org/Property?$filter=BadField eq 'SoBad' | | Section | 2.6.1 | | Acceptance Test | Source | | Notes | None |


HTTP 404 Response Code Test

| Item | Details | | – | – | | Id | response-code-404 | | Description | Issues query to trigger HTTP 404 response code. | | Sample Query | GET https://api.reso.org/ResourceNotFound | | Section | 2.6.1 | | Acceptance Test | Source | | Notes | None |


Section 4: Contributors

This document was written by Joshua Darnell.

Thanks to the following contributors for their help with this project:

Contributor Company
Paul Stusiak Falcon Technologies Corp.
Sergio Del Rio Templates for Business, Inc.
Joshua Darnell kurotek, LLC
Cody Gustafson FBS Data Systems
Chris Lambrou MetroMLS
Scott Petronis Onboard Informatics
Matthew McGuire CoreLogic
Fred Larsen UtahRealEstate.com
James McDaniel UtahRealEstate.com
Robert Gottesman RESO
Rob Larson Larson Consulting, LLC
Paul Hethmon Corelogic
Rick Trevino MetroList
Pace Davis Zillow Group
Michael Watt Zillow Group
Geoff Rispin Templates 4 Business, Inc.
Maria Dalarcao MLSListings, Inc.
Jeremy Crawford RESO

Many thanks to those who contributed to the Web API Core specification, including volunteers from the Transport workgroup.


Section 5: References

Please see the following references for more information regarding topics covered in this document.

Description Link
REST Representational State Transfer
Open Data Protocol or “OData” OData - the Best Way to REST
OData “4.0” Documentation · OData - the Best Way to REST
OData “4.0” - Part 1 - Protocol OData Version 4.0. Part 1: Protocol Plus Errata 03
OData “4.0” - Part 2 - URL Conventions OData Version 4.0. Part 2: URL Conventions Plus Errata 03
OData “4.0” - Part 3 - Common Schema Definition Language OData Version 4.0. Part 3: Common Schema Definition Language (CSDL) Plus Errata 03
Geospatial Support in OData Geospatial data support in OData · OData - the Best Way to REST
HTTP/1.1 Protocol Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests
Hypertext Transfer Protocol (HTTP/1.1): Range Requests
Hypertext Transfer Protocol (HTTP/1.1): Caching
Hypertext Transfer Protocol (HTTP/1.1): Authentication
HTTP/2.0 Protocol Hypertext Transfer Protocol Version 2 (HTTP/2)
HPACK: Header Compression for HTTP/2
Transport Layer Security (TLS) (Encryption for HTTP support) The Transport Layer Security (TLS) Protocol Version 1.2
Recommendations fSecure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)
OWASP TLS implementation guide
SSL Labs TLS Deployment Best Practices


Section 6: Appendices

Approved RCPs

The following RCPs are included in Web API Core 2.0.0:


Section 7: License

This document is covered by the RESO EULA.

Please contact RESO if you have any questions.