RESO Web API Quick Start Guide
This guide will walk you through the basics of the RESO Web API — searching properties, retrieving details, and fetching media — in under 20 minutes. You can follow along using curl, Python, JavaScript, Ruby, or PHP.
The RESO Web API is built on the RESO standard and uses OData query syntax. It shares the same credentials and authentication as the Spark® API, so if you have already completed the Spark API Quick Start, your credentials work here too.
- Prerequisites
- Step 1 — Get an Access Token
- Step 2 — Search Properties
- Step 3 — Get a Specific Property
- Step 4 — Expand Media (Photos)
Prerequisites
You need the same API credentials used for the Spark API. Register as a developer at sparkplatform.com to receive your client_id, client_secret, and optionally a pre-provisioned access_token.
The RESO Web API v3 base URL is:
https://replication.sparkapi.com/Version/3/Reso/OData/
All requests must be made over HTTPS.
Step 1 — Get an Access Token
Authentication is identical to the Spark API — see the Spark API Quick Start, Step 1 for the full walkthrough. The short version:
curl -X POST "https://sparkapi.com/v1/oauth2/grant" \
-H "Content-Type: application/json" \
-d '{
"client_id": "[client_id]",
"client_secret": "[client_secret]",
"grant_type": "authorization_code",
"code": "[code]",
"redirect_uri": "[redirect_uri]"
}'
import requests
response = requests.post(
"https://sparkapi.com/v1/oauth2/grant",
json={
"client_id": "[client_id]",
"client_secret": "[client_secret]",
"grant_type": "authorization_code",
"code": "[code]",
"redirect_uri": "[redirect_uri]"
}
)
access_token = response.json()["access_token"]
print("Access token:", access_token)
const response = await fetch("https://sparkapi.com/v1/oauth2/grant", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: "[client_id]",
client_secret: "[client_secret]",
grant_type: "authorization_code",
code: "[code]",
redirect_uri: "[redirect_uri]"
})
});
const { access_token } = await response.json();
console.log("Access token:", access_token);
require "net/http"
require "json"
uri = URI("https://sparkapi.com/v1/oauth2/grant")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path, "Content-Type" => "application/json")
request.body = JSON.generate(
client_id: "[client_id]",
client_secret: "[client_secret]",
grant_type: "authorization_code",
code: "[code]",
redirect_uri: "[redirect_uri]"
)
access_token = JSON.parse(http.request(request).body)["access_token"]
puts "Access token: #{access_token}"
<?php
$ch = curl_init("https://sparkapi.com/v1/oauth2/grant");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"client_id" => "[client_id]",
"client_secret" => "[client_secret]",
"grant_type" => "authorization_code",
"code" => "[code]",
"redirect_uri" => "[redirect_uri]"
]));
$access_token = json_decode(curl_exec($ch), true)["access_token"];
echo "Access token: " . $access_token;
Step 2 — Search Properties
Query the Property resource using OData's $filter parameter. The example below returns the first 5 active listings in Portland with at least 3 bedrooms.
The Authorization header uses the same OAuth scheme as the Spark API.
curl "https://replication.sparkapi.com/Version/3/Reso/OData/Property?\$filter=City eq 'Portland' and BedroomsTotal ge 3 and StandardStatus eq 'Active'&\$top=5&\$select=ListingKey,StreetName,City,ListPrice,BedroomsTotal" \
-H "Authorization: OAuth [access_token]"
import requests
access_token = "your_access_token_here"
base_url = "https://replication.sparkapi.com/Version/3/Reso/OData"
response = requests.get(
f"{base_url}/Property",
headers={"Authorization": f"OAuth {access_token}"},
params={
"$filter": "City eq 'Portland' and BedroomsTotal ge 3 and StandardStatus eq 'Active'",
"$top": 5,
"$select": "ListingKey,StreetName,City,ListPrice,BedroomsTotal"
}
)
properties = response.json()["value"]
for prop in properties:
print(prop["ListingKey"], prop["StreetName"], prop["City"], prop["ListPrice"])
const accessToken = "your_access_token_here";
const baseUrl = "https://replication.sparkapi.com/Version/3/Reso/OData";
const params = new URLSearchParams({
$filter: "City eq 'Portland' and BedroomsTotal ge 3 and StandardStatus eq 'Active'",
$top: 5,
$select: "ListingKey,StreetName,City,ListPrice,BedroomsTotal"
});
const response = await fetch(`${baseUrl}/Property?${params}`, {
headers: { Authorization: `OAuth ${accessToken}` }
});
const { value: properties } = await response.json();
properties.forEach(p => console.log(p.ListingKey, p.StreetName, p.City, p.ListPrice));
require "net/http"
require "json"
access_token = "your_access_token_here"
uri = URI("https://replication.sparkapi.com/Version/3/Reso/OData/Property")
uri.query = URI.encode_www_form(
"$filter" => "City eq 'Portland' and BedroomsTotal ge 3 and StandardStatus eq 'Active'",
"$top" => 5,
"$select" => "ListingKey,StreetName,City,ListPrice,BedroomsTotal"
)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri, "Authorization" => "OAuth #{access_token}")
properties = JSON.parse(http.request(request).body)["value"]
properties.each do |p|
puts "#{p['ListingKey']} #{p['StreetName']}, #{p['City']} $#{p['ListPrice']}"
end
<?php
$access_token = "your_access_token_here";
$query = http_build_query([
"\$filter" => "City eq 'Portland' and BedroomsTotal ge 3 and StandardStatus eq 'Active'",
"\$top" => 5,
"\$select" => "ListingKey,StreetName,City,ListPrice,BedroomsTotal"
]);
$ch = curl_init("https://replication.sparkapi.com/Version/3/Reso/OData/Property?" . $query);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: OAuth $access_token"]);
$data = json_decode(curl_exec($ch), true);
foreach ($data["value"] as $prop) {
echo $prop["ListingKey"] . " " . $prop["StreetName"] . ", " . $prop["City"] . " $" . $prop["ListPrice"] . "\n";
}
A successful response looks like:
{
"@odata.context": "https://replication.sparkapi.com/Version/3/Reso/OData/$metadata#Property",
"value": [
{
"ListingKey": "20100000000000000000000000",
"StreetName": "Main St",
"City": "Portland",
"ListPrice": 450000,
"BedroomsTotal": 3
}
]
}
For a full list of OData query options, see RESO Request Parameters.
Step 3 — Get a Specific Property
Retrieve full details for a single property using its ListingKey.
curl "https://replication.sparkapi.com/Version/3/Reso/OData/Property('20100000000000000000000000')" \
-H "Authorization: OAuth [access_token]"
listing_key = "20100000000000000000000000"
response = requests.get(
f"{base_url}/Property('{listing_key}')",
headers={"Authorization": f"OAuth {access_token}"}
)
prop = response.json()
print(f"{prop['StreetName']}, {prop['City']} — ${prop['ListPrice']:,}")
const listingKey = "20100000000000000000000000";
const response = await fetch(`${baseUrl}/Property('${listingKey}')`, {
headers: { Authorization: `OAuth ${accessToken}` }
});
const prop = await response.json();
console.log(`${prop.StreetName}, ${prop.City} — $${prop.ListPrice}`);
listing_key = "20100000000000000000000000"
uri = URI("https://replication.sparkapi.com/Version/3/Reso/OData/Property('#{listing_key}')")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri, "Authorization" => "OAuth #{access_token}")
prop = JSON.parse(http.request(request).body)
puts "#{prop['StreetName']}, #{prop['City']} — $#{prop['ListPrice']}"
<?php
$listing_key = "20100000000000000000000000";
$ch = curl_init("https://replication.sparkapi.com/Version/3/Reso/OData/Property('$listing_key')");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: OAuth $access_token"]);
$prop = json_decode(curl_exec($ch), true);
echo "{$prop['StreetName']}, {$prop['City']} — \${$prop['ListPrice']}";
Step 4 — Expand Media (Photos)
Use the $expand parameter to include related Media records (photos) directly in the property response, avoiding a second request.
curl "https://replication.sparkapi.com/Version/3/Reso/OData/Property('20100000000000000000000000')?\$expand=Media" \
-H "Authorization: OAuth [access_token]"
response = requests.get(
f"{base_url}/Property('{listing_key}')",
headers={"Authorization": f"OAuth {access_token}"},
params={"$expand": "Media"}
)
prop = response.json()
for media in prop.get("Media", []):
print(media.get("MediaURL")) # URL to the photo
const response = await fetch(
`${baseUrl}/Property('${listingKey}')?$expand=Media`,
{ headers: { Authorization: `OAuth ${accessToken}` } }
);
const prop = await response.json();
(prop.Media || []).forEach(m => console.log(m.MediaURL));
uri = URI("https://replication.sparkapi.com/Version/3/Reso/OData/Property('#{listing_key}')")
uri.query = URI.encode_www_form("$expand" => "Media")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri, "Authorization" => "OAuth #{access_token}")
prop = JSON.parse(http.request(request).body)
(prop["Media"] || []).each { |m| puts m["MediaURL"] }
<?php
$query = http_build_query(["\$expand" => "Media"]);
$ch = curl_init("https://replication.sparkapi.com/Version/3/Reso/OData/Property('$listing_key')?" . $query);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: OAuth $access_token"]);
$prop = json_decode(curl_exec($ch), true);
foreach ($prop["Media"] ?? [] as $media) {
echo $media["MediaURL"] . "\n";
}
A successful response looks like:
{
"@odata.context": "https://replication.sparkapi.com/Version/3/Reso/OData/$metadata#Property/$entity",
"ListingKey": "20100000000000000000000000",
"StreetName": "Main St",
"City": "Portland",
"ListPrice": 450000,
"Media": [
{
"MediaKey": "media_id_here",
"MediaURL": "https://cdn.sparkapi.com/reso/media/photo1.jpg",
"Order": 0,
"MediaCategory": "Photo"
}
]
}
Next Steps
- Explore available resources: Property, Member, Office, OpenHouse, and more.
- Learn all supported OData parameters on the RESO Request Parameters page.
- Set up ongoing data sync with RESO Web API Replication.
- Go back to the Spark API Quick Start Guide for the proprietary Spark API format.