Property Snapshot API & Scraper
The Property Enrichment API normalizes and resolves real-estate records as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary snapshot endpoint returns a normalized property (address, country, source engine and id, listing URL) matched by URL or address, and you can resolve, batch and check coverage. It is built for proptech, CRM enrichment and data pipelines that need to canonicalize messy property data across sources. One ReefAPI key, one shared credit pool, the standard envelope.
What each country can return, and what it cannot
This engine covers three countries and each is backed by different sources, so the same request shape returns a different amount of data depending on where the property sits. Valuation is the field that varies most: it is populated in the US and structurally absent in the UK and Japan. Anything outside these three is rejected outright rather than returned empty.
| Country | Accepted input | valuation | History |
|---|---|---|---|
| US | Address search, or a zillow.com, redfin.com or realtor.com listing URL | Populated. A measured snapshot returned valuation.source 'zillow:zestimate' with value 375000 USD | sold_history entries plus a rent estimate, 2212 USD on that same record |
| GB | Address search, or a rightmove.co.uk listing URL | Always null. The object is still present and carries a note saying there is no UK equivalent of a Zestimate | Land Registry sold prices are the only price evidence |
| JP | A suumo.jp listing URL works anywhere, address search only in tokyo, kanagawa, osaka, kyoto | Always null | Not available |
| Anything else | Rejected before any lookup runs | n/a | n/a |
A French address returns ok:false with error code MARKET_UNAVAILABLE and detail.unsupported_country 'FR'. A Japanese address outside the four supported prefectures returns MARKET_UNAVAILABLE with detail.unsupported_region true. Neither is a silent empty result. The coverage action returns this whole map with no network calls, so you can check support before spending a request.
Real request and response JSON
Captured from the indexed primary action, snapshot, on .
{
"method": "POST",
"url": "https://api.reefapi.com/enrich-property/v1/snapshot",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"listing_url": "https://www.zillow.com/homedetails/x/117892026_zpid/"
}
}{
"ok": true,
"meta": {
"api": "enrich-property",
"endpoint": "snapshot",
"mode": "standard",
"latency_ms": 4824.2,
"record_count": 1,
"bytes": 0,
"cache_hit": false,
"sources_matched": 1,
"subcalls": [
{
"api": "zillow",
"action": "property_detail",
"status": "ok",
"latency_ms": 4821.2
}
]
},
"data": {
"property": {
"country": "US",
"source_engine": "zillow",
"source_id": "[redacted-phone]",
"listing_url": "https://www.zillow.com/homedetails/x/[redacted-phone]_zpid/",
"address_normalized": "1444 Fairmont St NW #4"
},
"match": {
"mode": "url",
"confidence": 0.99,
"reasons": [
"url-host+id"
],
"candidates_considered": 0,
"unit_disambiguated": null
},
"status": "sold",
"price": {
"list": {
"value": 375000,
"currency": "USD",
"source": "zillow"
},
"rent": {
"value": 2243,
"currency": "USD",
"source": "zillow"
},
"sold_history": [
{
"price": 375000,
"date": null,
"event": "sold",
"currency": "USD",
"source": "zillow"
}
],
"price_per_area": {
"value": 625,
"currency": "USD",
"unit": "sqft",
"source": "derived"
}
},
"valuation": {
"value": 373400,
"currency": "USD",
"source": "zillow:zestimate"
},
"facts": {
"beds": {
"value": 1,
"source": "zillow",
"confidence": 0.9
},
"baths": {
"value": 1,
"source": "zillow",
"confidence": 0.9
},
"area": {
"value": {
"value": 600,
"unit": "sqft"
},
"source": "zillow",
"confidence": 0.85
},
"lot_area": null,
"year_built": {
"value": 1907,
"source": "zillow",
"confidence": 0.85
},
"property_type": {
"value": "TOWNHOUSE",
"source": "zillow",
"confidence": 0.8
}
},
"photos": {
"count": 15,
"sample": [
"https://photos.zillowstatic.com/fp/71e089bc6009788afd7792ae0e696d8f-uncropped_scaled_within_1536_1152.jpg",
"https://photos.zillowstatic.com/fp/0cc1847196ad832a939915e72613e1f1-uncropped_scaled_within_1536_1152.jpg",
"https://photos.zillowstatic.com/fp/63374eee373bca8571bc0a15481e4679-uncropped_scaled_within_1536_1152.jpg"
],
"source": "zillow"
},
"provenance": {
"zillow": {
"status": "matched",
"engine": "zillow"
}
}
}
}What the Property Snapshot API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| snapshot | address OR listing_url → one normalized property snapshot: status, price, sold-history, core facts, valuation (where available), per-field provenance + a match block (how the address was resolved + match_confidence). | Real-estate investors call snapshot to get address OR listing_url → one normalized property snapshot. | address, listing_url, country, mode |
| resolve | address → ranked candidate listings[] (id + source + match_confidence) so the customer picks the exact property (apartment-unit / stale-listing disambiguation). The false-match-reducing flow — call this when a snapshot returns low confidence. | Brokerage tools call resolve to get address → ranked candidate listings[] (id + source + match_confidence) so the customer picks…. | address, country, limit |
| batch | snapshot up to 10 addresses/listing-URLs in one call (standard mode, cache-first, per-item ok/error). | Property dashboards call batch to get snapshot up to 10 addresses/listing-URLs in one call (standard mode, cache-first, per-item ok…. | items, country |
| coverage | Static capability map: which countries/sources are supported, per-country source set, and the documented gaps (UK has no AVM; JP only 4 prefectures). No sub-calls. | Lead-generation teams call coverage to get static capability map. | none |
Call snapshot from your stack
curl -X POST https://api.reefapi.com/enrich-property/v1/snapshot \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"listing_url":"https://www.zillow.com/homedetails/x/117892026_zpid/"}'import requests
r = requests.post(
"https://api.reefapi.com/enrich-property/v1/snapshot",
headers={"x-api-key": REEF_KEY},
json={
"listing_url": "https://www.zillow.com/homedetails/x/117892026_zpid/"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/enrich-property/v1/snapshot", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"listing_url": "https://www.zillow.com/homedetails/x/117892026_zpid/"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.enrich-property.snapshot with {"listing_url":"https://www.zillow.com/homedetails/x/117892026_zpid/"}.Who uses this API and why
- Proptech tools call resolve to turn a messy address into a canonical property.
- CRM enrichment attaches listing data to a real-estate lead.
- Data pipelines use batch and coverage to normalize a property feed at scale.
Questions developers ask before integrating
I got ok:true but status 'unknown' and an empty facts object. What happened?
That is the no-match response. The engine scores every candidate listing against your address and only accepts one above 0.55. A measured lookup on '1444 Fairmont St NW #4, Washington, DC 20009' considered 41 candidates, scored the best at 0.0 with reasons street-number-mismatch, unit-mismatch and postcode-mismatch, and returned status 'unknown', price.list null, sold_history empty and facts {}. It also sets match.low_confidence true and adds a note telling you to call resolve. Read match.confidence before you read any other field.
When should I call resolve instead of snapshot?
Call resolve when the address is ambiguous, when the property may be an apartment unit, or after a snapshot came back low confidence. It returns up to 25 ranked candidates, each with source_id, listing_url, match_confidence, match_reasons, list_price, beds, baths, status and coordinates, so you can pick the exact one. A measured resolve on '137 Mellison Road, Tooting, London, SW17' returned 10 candidates whose best score was 0.5 with reasons street-name-partial and outcode, which is under the 0.55 bar snapshot enforces, so snapshot would have refused that same address.
How much better is a listing URL than an address?
Considerably. URL mode reads the host and the id straight out of the link, so match.mode comes back 'url' with confidence 0.99 and reasons ['url-host+id'], and candidates_considered is 0 because no search ran. Address mode has to run an area search and score the results, which is where mismatches appear. If your users can paste a link, take the link.
I have a Zillow link. Should I use enrich-url instead?
No, that engine will not route it for you. A measured classify on a zillow.com/homedetails URL returned entity_type 'product' from JSON-LD, platform null and a recommended card pointing at the generic web-extract scraper, because real estate hosts are not in that engine's platform table. The same URL through snapshot here returned match confidence 0.99, status sold, beds 1, baths 1, area 600 sqft, year_built 1907 and 15 photos. Property links belong on this engine.
Why is the list price the same number as the valuation on a sold property?
On a sold record both fields describe the same transaction. A measured snapshot returned status 'sold' with price.list 375000 USD, valuation 375000 from zillow:zestimate, a rent estimate of 2212, and price_per_area 625.0 per sqft with source 'derived', which is the price divided by the 600 sqft area. Also note that a sold_history entry can carry a null date while its price is present, so do not assume every history row is dateable.
Is every fact in the response equally reliable?
No, and the shape tells you. Each entry under facts is an object with value, source and confidence rather than a bare value, and the confidences differ inside one record: a measured UK snapshot returned beds at 0.9, baths at 0.85, property_type at 0.8 and tenure at 0.8. Country-specific fields only appear where they exist, which is why tenure shows up on a UK record and not on a US one.
How many properties can one batch call take?
Ten. Eleven items returns ok:false with INVALID_PARAM and the message 'max 10 items per batch (got 11)'. Batch runs each item in standard mode and reports per-item ok or error, so one unmatched address does not fail the whole call.
What is the Property Snapshot API?
Property Snapshot API is a ReefAPI endpoint group for property snapshot It returns live JSON through POST requests under /enrich-property/v1.
Is the Property Snapshot API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Property Snapshot calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Property Snapshot login or account?
No login to Property Snapshot is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Property Snapshot data?
The page example is captured from a live snapshot call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Property Snapshot API use?
Property Snapshot actions currently cost 1-5 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Property Snapshot from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call enrich-property actions with the same key, credit pool and JSON envelope used by normal REST requests.
Is the Property Snapshot API a Property Snapshot scraper?
It is the managed alternative to a DIY Property Snapshot scraper. Instead of building and maintaining your own scraper — proxies, headless browsers, captcha and constant breakage — you call one ReefAPI endpoint and get the same property snapshot back as clean JSON.