Web API Core
RCP-37 · v2.1.0RESO 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
- Summary of Changes
- Approved Testing Rules
- Introduction
- Section 1: Purpose
- Section 2: Specification
- Section 3: Certification
- Section 4: Contributors
- Section 5: References
- Section 6: Appendices
- Section 7: License
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
$expandhas 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.StringorCollection(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. inOperator - For servers advertising OData 4.01, theinoperator is tested for single-valued enumerations. It provides a convenient shorthand for matching a value against a set, for exampleStandardStatus in ('Active', 'Pending', 'Sold'), in place of a chain ofeqcomparisons.
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=Mediaand 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')/MediawhereXXXXXis the key.
- A GET request will be made to
- 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.nextLinkwithout any additional$filterparameters to ensure that the data returned with each response does not match any of the previous responses. - Next links with
$filterwill also be tested. Since ModificationTimestamp is the only required field at this point, it will be used.- A
$filterrequest 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.
- A
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
inoperator was introduced to determine whether a given value is in a set of values. This is more convenient than writingField1 eq 'value1' or Field1 eq 'value2' or .... When the response header indicates that the OData version is “4.01”, theinoperator is tested using a query similar to the following:GET /Property?$filter=StandardStatus in ('Active', 'Pending', 'Sold')- The
inoperator is tested for single-valued enumerations only. Multi-valued enumerations use theanyandalllambda 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:
- Data Dictionary 1.7 Reference XML Metadata
- RESO Testing Tools – open source tools for Web API Core certification
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
$filteror$orderbyexpressions are too large or complex for the server to process. - Deprecation Notice: OData
Edm.EnumTypedefinitions will soon be deprecated within RESO standards due to the fact that theEdm.EnumTypeportion usually requires additional knowledge or discovery of vendor-specific namespaces, and human-friendly lookup names are not allowed. RESO is currently migrating toEdm.Stringlookups, and new implementations should use this approach. Please contact RESO with further questions. - Deprecation Notice: Similar to (2), OData
Edm.EnumTypedefinitions withIsFlags=truewill soon be deprecated within RESO standards, withCollection(Edm.EnumType)being the current default. However, RESO is currently migrating toCollection(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
$metadataappended. To retrieve this document the client issues aGETrequest to the metadata document URL.If a request for metadata does not specify a format preference (via
Acceptheader 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
$metadataappended. To retrieve this document the client issues aGETrequest to the metadata document URL.If a request for metadata does not specify a format preference (via
Acceptheader 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:DataServiceselement MUST contain one or moreedm:Schemaelements 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:Schemaelements 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
$selectsystem 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
$selectquery 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
$selectquery 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
$selectsystem 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
$selectquery 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
$selectquery 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$selectwith 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
$topsystem 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
$orderbyquery 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
$countsystem query option with a value oftruespecifies 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
$countquery option within the$expandclause.A
$countquery option with a value offalse(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
trueorfalseis specified.The
$countsystem query option ignores any$top,$skip, or$expandquery options, and returns the total count of results across all pages including only those results matching any specified$filterand$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
$skipsystem 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
$topand$skipare used together,$skipMUST be applied before$top, regardless of the order in which they appear in the request.If no unique ordering is imposed through an
$orderbyquery 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
$orderbySystem Query option specifies the order in which items are returned from the service.The value of the
$orderbySystem 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
ascfor ascending ordescfor descending, separated from the property name by one or more spaces. Ifascordescis 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
falsecomes before the valuetruein ascending order.Values of type
Edm.Streamor any of the Geo types cannot be sorted.
OData 4.01 — Part 1: Protocol §11.2.6.2 System Query Option $orderby
The
$orderbySystem Query option specifies the order in which items are returned from the service.The value of the
$orderbySystem 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
ascfor ascending ordescfor descending, separated from the property name by one or more spaces. Ifascordescis not specified, the service MUST order by the specified property in ascending order.4.01 services MUST support case-insensitive values for
ascanddesc. 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
falsecomes before the valuetruein 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.Streamor 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
$filtersystem query option restricts the set of items returned.The
$countsegment may be used within a$filterexpression to limit the items returned based on the exact count of related entities or items within a collection-valued property.The value of the
$filteroption 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, andEdm.Int64 - Decimals:
Edm.DecimalandEdm.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
The
eqoperator returns true if the left operand is equal to the right operand, otherwise it returnsfalse.When applied to operands of entity types, the
eqoperator returnstrueif both operands represent the same entity, or both operands representnull.When applied to operands of complex types, the
eqoperator returnstrueif both operands have the same structure and same values, or both operands representnull.When applied to ordered collections, the
eqoperator returnstrueif 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
eqoperator returnstrueif both operands are equal after applying the same ordering on both collections.Each of the special values
null,-INF, andINFis equal to itself, and only to itself.The special value
NaNis 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
The
neoperator returnstrueif the left operand is not equal to the right operand, otherwise it returnsfalse.When applied to operands of entity types, the
neoperator returnstrueif the two operands do not represent the same entity.When applied to operands of complex types, the
neoperator returnstrueif the operands do not have the same structure and same values.When applied to ordered collections, the
neoperator returnstrueif 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
neoperator returnstrueif 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, andINFis not equal to any value but itself.The special value
NaNis not equal to anything, even to itself.The
nullvalue 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
The
gtoperator returnstrueif the left operand is greater than the right operand, otherwise it returnsfalse.The special value
INFis greater than any number, and any number is greater than-INF.The Boolean value
trueis greater thanfalse.If any operand is
null, the operator returnsfalse.
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
The
geoperator returnstrueif the left operand is greater than or equal to the right operand, otherwise it returnsfalse.See rules for
gtandeqfor 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
The
ltoperator returnstrueif the left operand is less than the right operand, otherwise it returnsfalse.The special value
-INFis less than any number, and any number is less thanINF.The Boolean value
falseis less thantrue.If any operand is
null, the operator returnsfalse.
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
The
leoperator returnstrueif the left operand is less than or equal to the right operand, otherwise it returnsfalse.
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.EnumTypeEdm.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
hasoperator returnstrueif the right operand is an enumeration value whose flag(s) are set on the left operand.The
nullvalue is treated as unknown, so if one operand evaluates tonull, the has operator returnsnull.
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
hasoperator returns true if the right operand is an enumeration value whose flag(s) are set on the left operand.The
nullvalue is treated as unknown, so if one operand evaluates tonull, the has operator returnsnull.
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
trueif and only if the expression is true for any member of the collection, otherwise it returnsfalse. This implies that the any operator always returnsfalsefor an empty collection.The
anyoperator can be used without an argument expression. This short form returnsfalseif 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
alloperator applies a Boolean expression to each member of a collection and returnstrueif the expression is true for all members of the collection, otherwise it returnsfalse. This implies that thealloperator always returnstruefor an empty collection.The
alloperator 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
$expandsystem 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$expandquery option is a comma-separated list of navigation property names, optionally followed by a/$refpath segment or a/$countpath 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
$expandsystem 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$expandquery option is a comma-separated list of navigation property names, stream property names, or$valueindicating the stream content of a media-entity. For navigation properties, the navigation property name is optionally followed by a/$refpath segment or a/$countpath 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:
- Property - a Property resource based on the RESO Data Dictionary Specification.
- Member - a Member resource based on the RESO Data Dictionary Specification.
- Office - an Office resource based on the RESO Data Dictionary Specification.
- Media - a Media resource based on the RESO Data Dictionary Specification.
- InternetTracking - an InternetTracking resource based on the RESO Data Dictionary Specification.
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"]
}
]
}
Combine Multiple Criteria in a Listing Search
GET https://api.reso.org/Property?$filter=ListPrice gt 250000 and ListPrice lt 500000
HTTP/2 200 OK
{
"@odata.context": "https://api.reso.org/Property?$filter=ListPrice gt 250000 and ListPrice lt 500000",
"value": [
{
"ListingKey": "c3",
"BedroomsTotal": 5,
"ListPrice": 400000.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. |
Server-Driven Paging (@odata.nextLink)
| 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.
Section 6: Appendices
Related RCPs
The following RCPs are included in Web API Core 2.1.0:
- RCP - WEBAPI-026 Change Default Certification Testing to Bearer Token
- RCP - WEBAPI-029 Revise Web API Certification Procedures
- RCP - WEBAPI-031 Data Dictionary Representation in the Web API
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
Filter (scalar comparisons)
Order By
Enumerations
Collections (lambda operators)
Error Responses
Lookup Resource (2.1.0)
String Enumerations (2.1.0)
in Operator (2.1.0)
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.
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.