← Back to all specifications

Web API Core

RCP-37 · v2.1.0

RESO Web API Core Specification

RCP 39
Version 2.1.0
Authors Joshua Darnell (RESO)
Status RATIFIED
Date Proposed April 2022
Date Ratified December 2023
Protocol HTTP
Dependencies OData 4.0 or 4.01
TLS 1.2+
OAuth 2 (Bearer Token or Client Credentials)
Related Links OASIS OData TC

The Web API Core 2.1.0 endorsement defines the primary functionality RESO Web API servers are expected to have 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


Requirement Levels

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.


Summary of Changes

  • Expanded Data Elements - Support for expanded data elements using OData $expand has been added. The Property, Member, Office, Field, and Lookup Resources MUST be available at the top level. All other resources MAY be expanded. Data consumers should be prepared to support either.
  • Server-Driven Paging - In order to improve the ease and ability for data consumers to retrieve data from RESO Web API servers, providers MUST support server-driven paging using @odata.nextLink.
  • String Comparison Operators - Support for the Lookup Resource was added in Data Dictionary 1.7, meaning that single- and multi-enumerations may be Edm.String or Collection(Edm.String), respectively. In order to provide parity with existing enumeration tests, string comparison operators have been added to Web API Core 2.1.0 in order to test those cases.
  • in Operator - For servers advertising OData 4.01, the in operator is tested for single-valued enumerations. It provides a convenient shorthand for matching a value against a set, for example StandardStatus in ('Active', 'Pending', 'Sold'), in place of a chain of eq comparisons.


Approved Testing Rules

See GitHub Issue.

The RESO Certification Subgroup has requested a new Web API Core specification be created to include certain features like support for OData Expand and Server-Driven paging.


Support for Expanded Data Elements

  • An authoritative list of top-level items will be created for Data Dictionary 1.7, and each subsequent version, starting with Property, Member, Office, Field, and Lookup Resources.
  • These items MAY be expanded into other resources, expanding Member into Property as ListAgent for example, but MUST be available at the top level.
  • Other items, such as Media or HistoryTransactional MAY be available at the top level but also MAY be available as expansions (or both).
  • Web API Core testing only requires one resource to be tested, though providers may test more than one.
  • Providers who support expand should specify at least one navigation property to test for a given resource.
  • Additional queries on expanded resources WILL NOT be tested at this time. Providers MAY support them as they see fit.
  • Assume the resource being tested is Property, with a navigation property of Media:
    • A GET request will be made to /Property?$expand=Media and the Property payload will be checked for a collection-based property called Media, and each result in the collection will be validated against the OData EntityType advertised in the metadata for Media.
    • Keys will be collected from the first request to Property, and a GET request will be made to /Property('XXXXX')/Media where XXXXX is the key.
  • As of Data Dictionary 1.7, standard names have been added for related RESO standard data elements. For example, here are those in the Property Resource. Providers MUST use standard names for standard expansions, when present, but MAY create their own local expansions outside of this as long as they are OData compliant.
  • Expanded properties MUST use the definitions in the RESO Data Dictionary, when applicable. These items have been added to the Data Dictionary Wiki (e.g. Media) and DD reference sheets, as well as generated in the reference metadata. Providers MAY add their own local expansions as well.

Providers MUST Support Server-Driven Paging

Providers MUST support server-driven paging using @odata.nextLink.

This functionality is needed so that data consumers may reliably consume data when only a partial result is returned and it’s classified as a MUST within OData Minimal Conformance Requirements (item 3) for version 4.0 and above.

Basic tests are included in the Web API Core 2.1.0 tests, but the majority of nextLink testing is done in the Payloads 2.0 Specification, which could result in a failure of nextLink-based operations even if the provider passed Web API Core 2.1.0 testing.

  • A count will be made on the given resource being tested to ensure there are records.
  • A request will be made using $top=1. This should NOT contain @odata.nextLink, since one record should be available, and we’ve reached the end of the set.
  • Several pages (10+) of records will be fetched using @odata.nextLink without any additional $filter parameters to ensure that the data returned with each response does not match any of the previous responses.
  • Next links with $filter will also be tested. Since ModificationTimestamp is the only required field at this point, it will be used.
    • A $filter request will be made for records greater than one year back using the timestamp field being tested in the Web API Core tests, and several pages (10+), will be fetched to ensure that each page has an @odata.nextLink, when applicable. If the total number of records are fetched during this process, the last page will be checked to ensure there is no nextLink. This testing will be done using the greater than (gt) operator.
    • The same test will be performed using the less than (lt) operator for records prior to the current timestamp.
    • Each page of data fetched will be validated to ensure that the timestamp field value is greater or less than what was requested.

String Comparison Operators for Single- and Multi-Valued Enumerations

With the current Web API Core tests, both single- and multi-valued enumerations are tested for those using OData Edm.EnumType enumerations and either Collection(Edm.EnumType) or Edm.EnumType with IsFlags=true.

String-based enumerations were added to Data Dictionary 1.7+ using the Lookup resource. There is currently no way to test this case in Web API Core 2.0.0.

The following tests support this case:

  • Providers will supply a LookupName to test for single- and multi-valued enumerations, as well as sample values for each case. For single-valued enumerations, there will be one sample value, and for multi-valued enumerations there will be two.
  • Data will be consumed from the Lookup Resource and validated. The provided LookupName will be checked to ensure it’s present in the Lookup Resource data, as will the lookup values.
  • For single-valued enumeration tests, assume that the LookupName provided is “StandardStatus” with a sample value of “Active” - the following requests will be made, and resulting data validated to ensure it matches the given queries:
    • GET /Property?$filter=StandardStatus eq 'Active'
    • GET /Property?$filter=StandardStatus ne 'Active'
  • For multi-valued enumeration tests, assume that the LookupName provided is “AccessibilityFeatures” and the values are “Accessible Entrance” and “Visitable” - the following tests will be made and resulting data validated to ensure it matches the given queries:
    • GET /Property?$filter=AccessibilityFeatures/any(enum:enum eq 'Accessible Entrance' or enum eq 'Visitable') - returns records that contain at least one of the provided values.
    • GET /Property?$filter=AccessibilityFeatures/all(enum:enum eq 'Accessible Entrance' or enum eq 'Visitable') - returns records whose values are only within the provided set (a subset); a record with an empty collection satisfies this vacuously.
  • As of OData “4.01” the in operator was introduced to determine whether a given value is in a set of values. This is more convenient than writing Field1 eq 'value1' or Field1 eq 'value2' or .... When the response header indicates that the OData version is “4.01”, the in operator is tested using a query similar to the following:
    • GET /Property?$filter=StandardStatus in ('Active', 'Pending', 'Sold')
    • The in operator is tested for single-valued enumerations only. Multi-valued enumerations use the any and all lambda operators shown above.




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:

  • Adopt existing open standards rather than creating new specifications, when possible.
  • Leverage existing software tool kits and libraries.
  • Favor convention over configuration, which increases predictability by reducing the number of decisions data consumers need to make.

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

  • Is an established, existing, open standard.
  • Has well-defined functionality that supports primary RESO use cases.
  • Has existing open source server and client implementations to promote community adoption.
  • Provides extensibility to handle industry-specific use cases, as needed.

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 authorization 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:

RESO provides an open source reference server in the reso-tools repository, and additional reference servers have been provided by the community, certified with RESO’s open source 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 access. 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). Providers MUST use TLS 1.2 or above.


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

  • Servers MUST provide an OData-Version header in their responses.
  • Clients MAY specify a given OData-Version in their requests.
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 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.

https://api.reso.org


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 https://api.reso.org/reso/Resource('Id')
Navigation Property Path https://api.reso.org/Resource('Id')/Media
Expanded Data Set https://api.reso.org/Resource?$expand=Media

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


2.3.3 Metadata URI Conventions

OData offers a special endpoint for conveying server metadata, located at:

https://<service root>/$metadata

The metadata document MUST be located relative to the OData service root, 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.


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:

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>

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.108+00:00 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.7+ Web API 2.0.0+ Notes
Boolean Edm.Boolean MUST be one of the literals true or false (case-sensitive).
Collection Edm.Collection In Web API Core, collections are only supported 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 format according 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 String comparison MUST be case-sensitive, in accordance with the OData specification. Field names are also case-sensitive when used in the $select, $filter, and $orderby query options, and clients MUST respect the case defined in the resource metadata.
String List, Single Edm.EnumType or Edm.String with the Lookup Resource RESO supports either Edm.EnumType or Edm.String 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.
String List, Multi Edm.EnumType with IsFlags=true or Collection(Edm.EnumType) or Collection(Edm.String) with the Lookup Resource RESO supports three kinds of multi-valued enumerations at the moment. Deprecation Notice applies. See Notes.
Timestamp Edm.DateTimeOffset Timestamps also use the ISO 8601 format. 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

  • A server MAY return HTTP 413 - Request Entity Too Large if the $filter or $orderby expressions are too large or complex for the server to process.
  • Deprecation Notice: OData Edm.EnumType definitions will soon be deprecated within RESO standards due to the fact that the Edm.EnumType portion usually requires additional knowledge or discovery of vendor-specific namespaces, and human-friendly lookup names are not allowed. RESO is currently migrating to Edm.String lookups, and new implementations should use this approach. Please contact RESO with further questions.
  • Deprecation Notice: Similar to (2), OData Edm.EnumType definitions with IsFlags=true will soon be deprecated within RESO standards, with Collection(Edm.EnumType) being the current default. However, RESO is currently migrating to Collection(Edm.String) for these lookups, which new implementations should use instead. Please contact RESO with further questions.


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.

OData 4.0 — Part 1: Protocol §11.1.2 Metadata Document Request

An OData Metadata Document is a representation of the data model that describes the data and operations exposed by an OData service.

[OData-CSDL] describes an XML representation for OData metadata documents and provides an XML schema to validate their contents. The media type of the XML representation of an OData metadata document is application/xml.

OData services MUST expose a metadata document that describes the data model exposed by the service. The Metadata Document URL MUST be the root URL of the service with $metadata appended. To retrieve this document the client issues a GET request to the metadata document URL.

If a request for metadata does not specify a format preference (via Accept header or $format) then the XML representation MUST be returned.

OData 4.01 — Part 1: Protocol §11.1.2 Metadata Document Request

An OData metadata document is a representation of the data model that describes the data and operations exposed by an OData service.

[OData-CSDLJSON] describes a JSON representation for OData metadata documents and provides a JSON schema to validate their contents. The media type of the JSON representation of an OData metadata document is application/json.

[OData-CSDLXML] describes an XML representation for OData metadata documents and provides an XML schema to validate their contents. The media type of the XML representation of an OData metadata document is application/xml.

OData services can expose a metadata document that describes the data model exposed by the service. The metadata document URL MUST be the root URL of the service with $metadata appended. To retrieve this document the client issues a GET request to the metadata document URL.

If a request for metadata does not specify a format preference (via Accept header or $format) then the XML representation MUST be returned.

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

  • For more information about the XML Metadata format, see the OData 4.0 Errata 03 Specification.
  • The edmx:DataServices element MUST contain one or more edm:Schema elements which define the schemas exposed by the OData service.
  • Each EntityType definition MUST define a Key. More information.
  • A schema is identified by a namespace. All edm:Schema elements MUST have a namespace defined through a Namespace attribute which MUST be unique within the document, and SHOULD be globally unique. A schema cannot span more than one document. More information.
  • OData EntityType, Property, and EnumType Member elements MUST conform to OData’s SimpleIdentifier naming conventions.
  • OASIS publishes XML Metadata XSD definitions that can be used to validate the syntax of an XML Metadata document.
  • RESO offers reference metadata in XML format which can be used as a guide, and corresponds to RESO Data Dictionary 1.7.
  • The above example does not demonstrate the use of annotations, which are outlined in the reference XML Metadata document.


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 to 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 4.0 — Part 2: URL Conventions §4.3.1 Canonical URL OData 4.01 — Part 2: URL Conventions §4.3.1 Canonical URL

For OData services conformant with the addressing conventions in this section, the canonical form of an absolute URL identifying a non-contained entity is formed by adding a single path segment to the service root URL. The path segment is made up of the name of the entity set associated with the entity followed by the key predicate identifying the entity within the collection. No type-cast segment is added to the canonical URL, even if the entity is an instance of a type derived from the declared entity type of its entity set.

The canonical key predicate for single-part keys consists only of the key property value without the key property name. For multi-part keys the key properties appear in the same order they appear in the key definition in the service metadata.

Applies to all supported OData versions (4.0, 4.01).

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"]
}


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.

OData 4.0 — Part 1: Protocol §11.2.4.1 System Query Option $select

The $select system query option requests that the service return only the properties, dynamic properties, actions and functions explicitly requested by the client. The service returns the specified content, if available, along with any available expanded navigation properties, and MAY return additional information.

The value of the $select query option is a comma-separated list of properties, qualified action names, qualified function names, the star operator (*), or the star operator prefixed with the namespace or alias of the schema in order to specify all operations defined in the schema. Only aliases defined in the metadata document of the service can be used in URLs.

It is also possible to request all structural properties, including any dynamic properties, using the star operator. The star operator SHOULD NOT introduce navigation properties, actions or functions not otherwise requested.

If the $select query option is not specified, the service returns the full set of properties or a default set of properties. The default set of properties MUST include all key properties.

If the service returns less than the full set of properties, either because the client specified a select or because the service returned a subset of properties in the absence of a select, the context URL MUST reflect the set of selected properties and expanded navigation properties.

OData 4.01 — Part 1: Protocol §11.2.5.1 System Query Option $select

The $select system query option requests that the service return only the properties, dynamic properties, actions and functions explicitly requested by the client. The service returns the specified content, if available, along with any available expanded navigation or stream properties, and MAY return additional information.

The value of the $select query option is a comma-separated list of properties, qualified action names, qualified function names, the star operator (*), or the star operator prefixed with the namespace or alias of the schema in order to specify all operations defined in the schema. Only aliases defined in the metadata document of the service can be used in URLs.

It is also possible to request all structural properties, including any dynamic properties, using the star operator. The star operator SHOULD NOT introduce navigation properties, actions or functions not otherwise requested.

If the $select query option is not specified, the service returns the full set of properties or a default set of properties. The default set of properties MUST include all key properties. Services may change the default set of properties returned. This includes returning new properties by default and omitting properties previously returned by default. Clients that rely on specific properties in the response MUST use $select with the required properties or with *.

If the service returns less than the full set of properties, either because the client specified a select or because the service returned a subset of properties in the absence of a select, the context URL MUST reflect the set of selected properties and projected expanded navigation properties.

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.

OData 4.0 — Part 1: Protocol §11.2.5.3 System Query Option $top
OData 4.01 — Part 1: Protocol §11.2.6.3 System Query Option $top

The $top system query option specifies a non-negative integer n that limits the number of items returned from a collection. The service returns the number of available items up to but not greater than the specified value n.

If no unique ordering is imposed through an $orderby query option, the service MUST impose a stable ordering across requests that include $top.

Applies to all supported OData versions (4.0, 4.01).

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.

OData 4.0 — Part 1: Protocol §11.2.5.5 System Query Option $count OData 4.01 — Part 1: Protocol §11.2.6.5 System Query Option $count

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.

The count of related entities can be requested by specifying the $count query option within the $expand clause.

A $count query option with a value of false (or not specified) hints that the service SHOULD NOT return a count.

The service returns an HTTP Status code of 400 Bad Request if a value other than true or false is specified.

The $count system query option ignores any $top, $skip, or $expand query options, and returns the total count of results across all pages including only those results matching any specified $filter and $search.

Clients should be aware that the count returned inline may not exactly equal the actual number of items returned, due to latency between calculating the count and enumerating the last value or due to inexact calculations on the service.

How the count is encoded in the response body is dependent upon the selected format.

Applies to all supported OData versions (4.0, 4.01).

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.

OData 4.0 — Part 1: Protocol §11.2.5.4 System Query Option $skip OData 4.01 — Part 1: Protocol §11.2.6.4 System Query Option $skip

The $skip system query option specifies a non-negative integer n that excludes the first n items of the queried collection from the result. The service returns items starting at position n+1.

Where $top and $skip are used together, $skip MUST be applied before $top, regardless of the order in which they appear in the request.

If no unique ordering is imposed through an $orderby query option, the service MUST impose a stable ordering across requests that include $skip.

Applies to all supported OData versions (4.0, 4.01).

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.

OData 4.0 — Part 1: Protocol §11.2.5.2 System Query Option $orderby

The $orderby System Query option specifies the order in which items are returned from the service.

The value of the $orderby System Query option contains a comma-separated list of expressions whose primitive result values are used to sort the items. A special case of such an expression is a property path terminating on a primitive property. A type cast using the qualified entity type name is required to order by a property defined on a derived type. Only aliases defined in the metadata document of the service can be used in URLs.

The expression can include the suffix asc for ascending or desc for descending, separated from the property name by one or more spaces. If asc or desc is not specified, the service MUST order by the specified property in ascending order.

Null values come before non-null values when sorting in ascending order and after non-null values when sorting in descending order.

Items are sorted by the result values of the first expression, and then items with the same value for the first expression are sorted by the result value of the second expression, and so on.

The Boolean value false comes before the value true in ascending order.

Values of type Edm.Stream or any of the Geo types cannot be sorted.

OData 4.01 — Part 1: Protocol §11.2.6.2 System Query Option $orderby

The $orderby System Query option specifies the order in which items are returned from the service.

The value of the $orderby System Query option contains a comma-separated list of expressions whose primitive result values are used to sort the items. A special case of such an expression is a property path terminating on a primitive property. A type cast using the qualified entity type name is required to order by a property defined on a derived type. Only aliases defined in the metadata document of the service can be used in URLs.

The expression can include the suffix asc for ascending or desc for descending, separated from the property name by one or more spaces. If asc or desc is not specified, the service MUST order by the specified property in ascending order.

4.01 services MUST support case-insensitive values for asc and desc. Clients that want to work with 4.0 services MUST use lower case values.

Null values come before non-null values when sorting in ascending order and after non-null values when sorting in descending order.

Items are sorted by the result values of the first expression, and then items with the same value for the first expression are sorted by the result value of the second expression, and so on.

The Boolean value false comes before the value true in ascending order.

Services SHOULD order language-dependent strings according to the content-language of the response, and SHOULD annotate string properties with language-dependent order with the term Core.IsLanguageDependent, see [OData-VocCore].

Values of type Edm.Stream or any of the Geo types cannot be sorted.

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 4.0 — Part 1: Protocol §11.2.5.1 System Query Option $filter OData 4.01 — Part 1: Protocol §11.2.6.1 System Query Option $filter

The $filter system query option restricts the set of items returned.

The $count segment may be used within a $filter expression to limit the items returned based on the exact count of related entities or items within a collection-valued property.

The value of the $filter option is a Boolean expression as defined in [OData-ABNF].

Applies to all supported OData versions (4.0, 4.01).

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, is available 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 the Lookup Resource section of the Data Dictionary 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:

  • Integers: Edm.Int16, Edm.Int32, and Edm.Int64
  • Decimals: Edm.Decimal and Edm.Double
  • Dates: Edm.Date
  • Timestamps: Edm.DateTimeOffset

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:

  • BedroomsTotal (Edm.Int64)
  • ListPrice (Edm.Decimal)
  • ListingContractDate (Edm.Date)
  • ModificationTimestamp (Edm.DateTimeOffset)

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.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.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.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
  • Edm.String, as defined in the Lookup Resource section of the Data Dictionary

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.

Edm.String lookups are certified as part of Web API Core 2.1.0. Please contact RESO with any questions about implementing them.


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 defined in the Lookup Resource section of the Data Dictionary. 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 bit-mapped 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": "AccessibleEntrance,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.

Edm.String lookups are certified as part of Web API Core 2.1.0. Please contact RESO with any questions about implementing them.


2.5.10 Expanded Data Elements

Web API Core 2.1.0 also supports the use of OData $expand, which results in a nested data set from another defined resource.

OData 4.0 — Part 1: Protocol §11.2.4.2 System Query Option $expand

The $expand system query option indicates the related entities that MUST be represented inline. The service MUST return the specified content, and MAY choose to return additional information. The value of the $expand query option is a comma-separated list of navigation property names, optionally followed by a /$ref path segment or a /$count path segment, and optionally a parenthesized set of expand options (for filtering, sorting, selecting, paging, or expanding the related entities). For a full description of the syntax used when building requests, see [OData-URL].

OData 4.01 — Part 1: Protocol §11.2.5.2 System Query Option $expand

The $expand system query option indicates the related entities and stream values that MUST be represented inline. The service MUST return the specified content, and MAY choose to return additional information. The value of the $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. For navigation properties, the navigation property name is optionally followed by a /$ref path segment or a /$count path segment, and optionally a parenthesized set of expand options (for filtering, sorting, selecting, paging, or expanding the related entities). For a full description of the syntax used when building requests, see [OData-URL].

Example Assume a given server defines a Property resource as follows, using the XML Metadata example from section 2.3.3:

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" />
        <NavigationProperty Name="Media" Type="Collection(org.reso.metadata.Media)" />
      </EntityType>
      <EntityType Name="Media">
        <Key>
          <PropertyRef Name="MediaKey" />
        </Key>
        <Property MaxLength="255" Name="ChangedByMemberKey" Type="Edm.String" />
        <Property MaxLength="255" Name="MediaKey" Type="Edm.String" />
        <Property Name="MediaModificationTimestamp" Precision="27" Type="Edm.DateTimeOffset" />
        <Property MaxLength="8000" Name="MediaURL" Type="Edm.String" />
        <Property Name="ModificationTimestamp" Precision="27" Type="Edm.DateTimeOffset" />
        <Property Name="Order" Type="Edm.Int64" />
        <Property Name="ResourceName" Type="org.reso.metadata.enums.ResourceName" />
        <Property MaxLength="255" Name="ResourceRecordKey" Type="Edm.String" />
      </EntityType>
      <EntityContainer Name="RESO">
        <EntitySet Name="Property" EntityType="org.reso.metadata.Property">
          <NavigationPropertyBinding Path="Media" Target="Media" />
        </EntitySet>
        <EntitySet Name="Media" EntityType="org.reso.metadata.Media" />
      </EntityContainer>
    </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>
      <EnumType Name="ResourceName">
        <Member Name="Property" />
      </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.108+00:00 or 2021-05-21T06:28:34.007Z
Media Navigation Property Media org.reso.metadata.Media Collection Navigation properties provide $expand support.
StandardStatus Enumeration EnumType Edm.EnumType N/A Defines enumerations for: Active, Closed, ComingSoon, and Pending using the SimpleIdentifier format.
ResourceName Enumeration EnumType Edm.EnumType N/A Defines possible resources for the ResourceName field in the Media Resource.


2.5.10.1 Request Data from the Property Resource with a Media Expansion

GET https://api.reso.org/Property?$expand=Media&$top=2
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/$metadata#Property(Media())",
  "value": [
    {
      "ListingKey": "a1",
      "ListPrice": 100000.0,
      "ModificationTimestamp": "2022-04-02T02:02:02.02Z",
      "StandardStatus": "Active",
      "Media": [
        {
          "ChangedByMemberKey": "ABC123",
          "MediaKey": "AT23852983",
          "MediaModificationTimestamp": "2022-04-02T03:02:02.02Z",
          "MediaURL": "https://cdn.reso.org/media/AT23852983",
          "ModificationTimestamp": "2022-04-02T02:02:02.03Z",
          "Order": 1,
          "ResourceName": "Property",
          "ResourceRecordKey": "a1"
        }
      ]
    },
    {
      "ListingKey": "b2",
      "ListPrice": 100001.0,
      "ModificationTimestamp": "2022-04-03T02:02:02.02Z",
      "StandardStatus": "Pending",
      "Media": [
        {
          "ChangedByMemberKey": "ABC123",
          "MediaKey": "AT23852983",
          "MediaModificationTimestamp": "2022-04-02T03:02:02.02Z",
          "MediaURL": "https://cdn.reso.org/media/AT23852983",
          "ModificationTimestamp": "2022-04-02T02:02:02.03Z",
          "Order": 1,
          "ResourceName": "Property",
          "ResourceRecordKey": "b2"
        },
        {
          "ChangedByMemberKey": "ABC123",
          "MediaKey": "BU23852983",
          "MediaModificationTimestamp": "2022-04-02T03:02:02.02Z",
          "MediaURL": "https://cdn.reso.org/media/BU23852983",
          "ModificationTimestamp": "2022-04-02T02:02:02.03Z",
          "Order": 2,
          "ResourceName": "Property",
          "ResourceRecordKey": "b2"
        }
      ]
    }
  ]
}

Note: Since $top=2 was requested, there is no @odata.nextLink.


2.5.10.2 Request Media for a Specific Listing in the Property Resource using Navigation Property Paths

GET https://api.reso.org/Property('a1')/Media
200 OK
{
  "@odata.context": "https://api.reso.org/$metadata#Media",
  "value": [
    {
      "ChangedByMemberKey": "ABC123",
      "MediaKey": "AT23852983",
      "MediaModificationTimestamp": "2022-04-02T03:02:02.02Z",
      "MediaURL": "https://cdn.reso.org/media/AT23852983",
      "ModificationTimestamp": "2022-04-02T02:02:02.03Z",
      "Order": 1,
      "ResourceName": "Property",
      "ResourceRecordKey": "a1"
    }
  ]
}


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.
401 Unauthorized Returned when authentication fails, such as a missing, expired or invalid token. This is distinct from a 403, which indicates an authorization failure.
403 Forbidden Returned when the selected authorization 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 501 Not Implemented
{
  "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 is not visible in production for certain roles.

Servers MAY support local resources, fields, or lookups that do not 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.0,
      "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.0,
      "StreetName": "2nd",
      "ModificationTimestamp": "2020-12-31T00: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.007Z"
    },
    {
      "MemberKey": "b2",
      "MemberStatus": "Active",
      "MemberFirstName": "Adam",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00: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.0,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "ShortSale": true,
      "StandardStatus": "ActiveUnderContract",
      "AccessibilityFeatures": ["AccessibleApproachWithRamp", "AccessibleEntrance", "Visitable"]
    }
  ]
}


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.0,
      "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.0,
      "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.0,
      "StreetName": "Oak",
      "ModificationTimestamp": "2021-08-15T00: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.007Z",
      "StandardStatus": "Active",
      "AccessibilityFeatures": []
    },
    {
      "ListingKey": "f6",
      "BedroomsTotal": 5,
      "ListPrice": 489000,
      "StreetName": "Maple",
      "ModificationTimestamp": "2021-06-25T00: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.0,
      "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.0,
      "StreetName": "Main",
      "ModificationTimestamp": "2020-04-02T02:02:02.02Z",
      "ListingContractDate": "2020-04-02",
      "StandardStatus": "Active",
      "AccessibilityFeatures": ["Visitable"]
    },
    {
      "ListingKey": "c3",
      "BedroomsTotal": 4,
      "ListPrice": 300000.0,
      "StreetName": "Oak",
      "ModificationTimestamp": "2021-08-15T00:01:01.007Z",
      "StandardStatus": "Active",
      "AccessibilityFeatures": []
    },
    {
      "ListingKey": "d4",
      "BedroomsTotal": 5,
      "ListPrice": 499999,
      "StreetName": "7th",
      "ModificationTimestamp": "2021-06-25T00:01:01.007Z",
      "StandardStatus": "Active",
      "AccessibilityFeatures": []
    },
    {
      "ListingKey": "e5",
      "BedroomsTotal": 5,
      "ListPrice": 489000,
      "StreetName": "Maple",
      "ModificationTimestamp": "2021-06-25T00: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.007Z"
    },
    {
      "MemberKey": "b2",
      "MemberStatus": "Active",
      "MemberFirstName": "Betty",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    },
    {
      "MemberKey": "c3",
      "MemberStatus": "Active",
      "MemberFirstName": "Henry",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    },
    {
      "MemberKey": "d4",
      "MemberStatus": "Active",
      "MemberFirstName": "Kevin",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    },
    {
      "MemberKey": "e5",
      "MemberStatus": "Active",
      "MemberFirstName": "Timothy",
      "MemberLastName": "Adams",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    }
  ]
}


Get the Next Five Members

GET https://api.reso.org/Member?$top=5&$skip=5
HTTP/2 200 OK
{
  "@odata.context": "https://api.reso.org/Member?$top=5&$skip=5",
  "value": [
    {
      "MemberKey": "f6",
      "MemberStatus": "Active",
      "MemberFirstName": "James",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    },
    {
      "MemberKey": "g7",
      "MemberStatus": "Active",
      "MemberFirstName": "Adam",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    },
    {
      "MemberKey": "h8",
      "MemberStatus": "Active",
      "MemberFirstName": "Jennifer",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    },
    {
      "MemberKey": "i9",
      "MemberStatus": "Active",
      "MemberFirstName": "Kevin",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00:01:01.007Z"
    },
    {
      "MemberKey": "j10",
      "MemberStatus": "Active",
      "MemberFirstName": "Theresa",
      "MemberLastName": "Smith",
      "ModificationTimestamp": "2021-08-21T00: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 maintain 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.0,
      "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.0,
      "StreetName": "Oak",
      "ModificationTimestamp": "2021-08-15T00: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.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.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 rather 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.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 authorization 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.1.0 Core specification is to provide a common, stable set of authorization 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 is 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:

  • Metadata format
  • Query format and URL structure to support it
  • Response format and type system

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 authorization requirements which, at the time of writing, are servers that MUST support OAuth2 Bearer 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 provides open source testing tools for Web API Core certification in the reso-tools repository.

Certification validates a server against the OData standard on two levels: its metadata against the OASIS Entity Data Model (EDM) and EDMX schemas, and its response payloads against JSON Schema derived from the server’s own declared metadata.

The Web API Core tests are a fixed set of scenarios, published as the Web API Core Scenario Catalog in Section 6. The catalog is the canonical list of what is tested, so the requirements and the tests remain in lockstep.

3.4 Testing Methodology

Metadata Request Using RESO Standard Authorization

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 authorization strategies allow data consumption to be machine automated, so that no additional interaction from a user is necessary during the authorization process.

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, the metadata is checked for an OData EntityType definition for Property. If the underlying data model is not found, metadata validation fails with a diagnostic message to help users understand why a given error occurred. Once the model is found, its field and enumeration definitions are 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.

Rather than being configured by hand, the fields, enumerations and values used in the tests are discovered from the server’s own metadata and sampled from its data. For each data type, a field is selected to exercise, preferring RESO Data Dictionary standard fields over local extensions and ranking candidates by how densely they are populated so that the tests run against representative data. Real values are then sampled from the chosen field to build each query, so that the result is known in advance and an empty result is a genuine defect rather than an artifact of sparse data. When a prerequisite yields no usable sample, the scenarios that depend on it are reported as not tested rather than failed.

In addition to comparison operators, such as greater than and less than for types like Integers and Timestamps, the tests exercise OData query operators such as $select, which allows the consumer to specify the list of fields returned in the payload, and $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 entry in the Web API Core Scenario Catalog in Section 6, generated from the certification scenario definitions.

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 count
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 skip
Description $skip requests the number of items in the collection to be skipped and not included in the result.
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-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-all
Description all 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


The following queries are new in Web API Core 2.1.0 (RCP-039).

$expand a Navigation Property

Item Details
Id expand
Description $expand a navigation property and validate the expanded data set against the EntityType advertised in the metadata.
Sample Query GET https://api.reso.org/Property?$expand=Media
Section Support for Expanded Data Elements
Acceptance Test Source
Notes The provider specifies at least one navigation property to test. Keys collected from the first request also drive a GET /Property('XXXXX')/Media request. New in 2.1.0.


Lookup Resource Validation

Item Details
Id lookup-resource-validation
Description Fetch the Lookup Resource by LookupName and validate that the provider’s declared LookupName and its sample values are present. Runs first among the string-enumeration tests.
Sample Query GET https://api.reso.org/Lookup?$filter=LookupName eq 'StandardStatus'
Section String Comparison Operators for Single- and Multi-Valued Enumerations
Acceptance Test Source
Notes If the LookupName or its sample values are absent from the Lookup Resource, the dependent string-enumeration and in tests are skipped. New in 2.1.0.


Item Details
Id server-driven-paging
Description Providers MUST support server-driven paging using @odata.nextLink. A $top=1 request MUST NOT return an @odata.nextLink, since the single record reaches the end of the set.
Sample Query GET https://api.reso.org/Property?$top=1
Section Providers MUST Support Server-Driven Paging
Acceptance Test Source
Notes A $count confirms records exist first. The majority of @odata.nextLink testing is performed in the Payloads 2.0 Specification. New in 2.1.0.


Filter String Enumeration Using eq

Item Details
Id filter-string-enum-single-eq
Description $filter eq on a string-backed single-valued enumeration (Lookup Resource).
Sample Query GET https://api.reso.org/Property?$filter=StandardStatus eq 'Active'
Section String Comparison Operators for Single- and Multi-Valued Enumerations
Acceptance Test Source
Notes New in 2.1.0.


Filter String Enumeration Using ne

Item Details
Id filter-string-enum-single-ne
Description $filter ne on a string-backed single-valued enumeration (Lookup Resource).
Sample Query GET https://api.reso.org/Property?$filter=StandardStatus ne 'Active'
Section String Comparison Operators for Single- and Multi-Valued Enumerations
Acceptance Test Source
Notes New in 2.1.0.


Filter String Enumeration Collection Using any()

Item Details
Id filter-string-enum-multi-any
Description $filter with the any() lambda over a string-backed multi-valued enumeration – returns records that contain at least one of the provided values.
Sample Query GET https://api.reso.org/Property?$filter=AccessibilityFeatures/any(enum:enum eq 'Accessible Entrance' or enum eq 'Visitable')
Section String Comparison Operators for Single- and Multi-Valued Enumerations
Acceptance Test Source
Notes New in 2.1.0.


Filter String Enumeration Collection Using all()

Item Details
Id filter-string-enum-multi-all
Description $filter with the all() lambda over a string-backed multi-valued enumeration – returns records whose values are only within the provided set (a subset); a record with an empty collection satisfies this vacuously.
Sample Query GET https://api.reso.org/Property?$filter=AccessibilityFeatures/all(enum:enum eq 'Accessible Entrance' or enum eq 'Visitable')
Section String Comparison Operators for Single- and Multi-Valued Enumerations
Acceptance Test Source
Notes New in 2.1.0.


Filter Single Enumeration Using the in Operator (OData 4.01)

Item Details
Id filter-string-enum-single-in
Description $filter with the OData 4.01 in operator on a single-valued enumeration – tests whether a field’s value is within a set of values.
Sample Query GET https://api.reso.org/Property?$filter=StandardStatus in ('Active', 'Pending', 'Sold')
Section String Comparison Operators for Single- and Multi-Valued Enumerations
Acceptance Test Source
Notes Gated on the response advertising OData-Version: 4.01; skipped for OData 4.0-only servers. New in 2.1.0.

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 RESO
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 AMP Systems
Rick Trevino MetroList
Pace Davis Zillow Group
Michael Watt Zillow Group
Geoff Rispin Templates 4 Business, Inc.
Maria Dalarcao MLSListings, Inc.
Jeremy Crawford RESO
Eric Finlay Bridge Interactive
Ryan Yates Rapattoni

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
OData “4.01” - Part 1 - Protocol OData Version 4.01. Part 1: Protocol
OData “4.01” - Part 2 - URL Conventions OData Version 4.01. Part 2: URL Conventions
OData “4.01” - Common Schema Definition Language (CSDL) XML OData CSDL XML Version 4.01
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 for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)
OWASP TLS implementation guide
SSL Labs TLS Deployment Best Practices
RESO Data Dictionary RESO Data Dictionary reference
RESO Testing Tools and Reference Server reso-tools


Section 6: Appendices

The following RCPs are included in Web API Core 2.1.0:


Web API Core Scenario Catalog

This catalog is the canonical list of Web API Core test scenarios. Each Testing Query in Section 3.5 links to its entry here by a stable scenario-<id> anchor.

Structural

Scenario Version What it checks
metadata-validation – Validate server metadata 2.0.0 · Required Request and validate server $metadata (EDMX; XSD + semantic validation).
service-document – Service document request 2.0.0 · Required Request and validate the OData service document.
fetch-by-key – Fetch by key field 2.0.0 · Required Retrieve a single record by its key field.
select – $select query support 2.0.0 · Required $select returns only the requested fields.
top – $top query support 2.0.0 · Required $top limits the number of records returned.
skip – $skip query support 2.0.0 · Required $skip offsets the result set.
count – $count query support 2.0.0 · Required $count returns the total record count.

Filter (scalar comparisons)

Scenario Version What it checks
filter-int-and – Integer: and 2.0.0 · Required $filter on an integer field: gt, compound and with lt.
filter-int-or – Integer: or 2.0.0 · Required $filter on an integer field: gt, compound or with lt.
filter-int-not – Integer: not() 2.0.0 · Required $filter on an integer field: le, wrapped in not().
filter-int-eq – Integer: eq 2.0.0 · Required $filter on an integer field: eq.
filter-int-ne – Integer: ne 2.0.0 · Required $filter on an integer field: ne.
filter-int-gt – Integer: gt 2.0.0 · Required $filter on an integer field: gt.
filter-int-ge – Integer: ge 2.0.0 · Required $filter on an integer field: ge.
filter-int-lt – Integer: lt 2.0.0 · Required $filter on an integer field: lt.
filter-int-le – Integer: le 2.0.0 · Required $filter on an integer field: le.
filter-decimal-ne – Decimal: ne 2.0.0 · Required $filter on a decimal field: ne.
filter-decimal-gt – Decimal: gt 2.0.0 · Required $filter on a decimal field: gt.
filter-decimal-ge – Decimal: ge 2.0.0 · Required $filter on a decimal field: ge.
filter-decimal-lt – Decimal: lt 2.0.0 · Required $filter on a decimal field: lt.
filter-decimal-le – Decimal: le 2.0.0 · Required $filter on a decimal field: le.
filter-date-eq – Date: eq 2.0.0 · Required $filter on a date field: eq.
filter-date-ne – Date: ne 2.0.0 · Required $filter on a date field: ne.
filter-date-gt – Date: gt 2.0.0 · Required $filter on a date field: gt.
filter-date-ge – Date: ge 2.0.0 · Required $filter on a date field: ge.
filter-date-lt – Date: lt 2.0.0 · Required $filter on a date field: lt.
filter-date-le – Date: le 2.0.0 · Required $filter on a date field: le.
filter-datetime-gt – Timestamp: gt 2.0.0 · Required $filter on a timestamp field: gt.
filter-datetime-ge – Timestamp: ge 2.0.0 · Required $filter on a timestamp field: ge.
filter-datetime-ne – Timestamp: ne 2.0.0 · Required $filter on a timestamp field: ne.
filter-datetime-lt – Timestamp: lt 2.0.0 · Required $filter on a timestamp field: lt.
filter-datetime-le – Timestamp: le 2.0.0 · Required $filter on a timestamp field: le.
filter-datetime-lt-now – Timestamp: lt now() 2.0.0 · Required $filter on a timestamp field: lt.
filter-datetime-le-now – Timestamp: le now() 2.0.0 · Required $filter on a timestamp field: le.
filter-datetime-ne-now – Timestamp: ne now() 2.0.0 · Required $filter on a timestamp field: ne.

Order By

Scenario Version What it checks
orderby-timestamp-asc – OrderBy: asc 2.0.0 · Required $orderby asc.
orderby-timestamp-desc – OrderBy: desc 2.0.0 · Required $orderby desc.
orderby-timestamp-asc-filter-int-gt – OrderBy: asc + int filter 2.0.0 · Required $orderby asc with an integer filter.
orderby-timestamp-desc-filter-int-gt – OrderBy: desc + int filter 2.0.0 · Required $orderby desc with an integer filter.

Enumerations

Scenario Version What it checks
filter-enum-single-has – Single enum: has 2.0.0 · Required $filter on a single-valued enumeration: has.
filter-enum-single-eq – Single enum: eq 2.0.0 · Required $filter on a single-valued enumeration: eq.
filter-enum-ne – Single enum: ne 2.0.0 · Required $filter on a single-valued enumeration: ne.
filter-enum-multi-has – Multi enum: has 2.0.0 · Required $filter on a multi-valued enumeration: has.
filter-enum-multi-has-and – Multi enum: has + and 2.0.0 · Required $filter on a multi-valued enumeration: has.

Collections (lambda operators)

Scenario Version What it checks
filter-coll-enum-any – Collection: any() 2.0.0 · Required $filter with the any() lambda over a multi-valued collection.
filter-coll-enum-all – Collection: all() 2.0.0 · Required $filter with the all() lambda over a multi-valued collection.

Error Responses

Scenario Version What it checks
response-code-400 – 400 Bad Request 2.0.0 · Required A malformed query MUST return HTTP 400.
response-code-404 – 404 Not Found 2.0.0 · Required A request for a resource that does not exist MUST return HTTP 404.

Lookup Resource (2.1.0)

Scenario Version What it checks
lookup-resource-validation – Lookup Resource: LookupName and sample values present 2.1.0 · Required Fetch the Lookup Resource by LookupName and validate the declared name and sample values are present.

String Enumerations (2.1.0)

Scenario Version What it checks
filter-string-enum-single-eq – String enum: eq 2.1.0 · Required $filter on a string-backed single-valued enumeration: eq.
filter-string-enum-single-ne – String enum: ne 2.1.0 · Required $filter on a string-backed single-valued enumeration: ne.
filter-string-enum-multi-any – String enum collection: any() 2.1.0 · Required $filter on a string-backed multi-valued enumeration: any.
filter-string-enum-multi-all – String enum collection: all() 2.1.0 · Required $filter on a string-backed multi-valued enumeration: all.

in Operator (2.1.0)

Scenario Version What it checks
filter-string-enum-single-in – String enum: in (…) 2.1.0 · Required $filter … in ('a','b','c') on a single-valued enumeration – OData 4.01, gated on the server advertising OData-Version: 4.01.

Server-Driven Paging (2.1.0)

Scenario Version What it checks
server-driven-paging – Server-driven paging (nextLink) 2.1.0 · Required Server-driven paging via @odata.nextLink; $top=1 MUST NOT return a nextLink.

Expand (2.1.0)

Scenario Version What it checks
expand – $expand navigation property 2.1.0 · Required $expand a navigation property and validate the expanded data set.

String Functions (Optional)

These string comparison operators are not required for Web API Core certification. They are exercised as OData functions because some providers support them, and we want to recognize that support – a failure here is only ever reported as “Not Supported” and never affects the Core verdict.

Scenario Version What it checks
filter-string-contains – String: contains() 2.1.0 · Optional $filter string function contains() – Optional Test, not required for Core certification.
filter-string-startswith – String: startswith() 2.1.0 · Optional $filter string function startswith() – Optional Test, not required for Core certification.
filter-string-endswith – String: endswith() 2.1.0 · Optional $filter string function endswith() – Optional Test, not required for Core certification.


Section 7: License

This document is covered by the 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.

Please contact RESO if you have any questions.

Link copied