Read OnTheMarket sale and rental listings as clean JSON
The OnTheMarket API returns UK real-estate listings as clean JSON.
3 active endpoints, on 0, 2 and 3 credit tiers.
- POST/onthemarket/v1/location_search
- POST/onthemarket/v1/search
- POST/onthemarket/v1/property_detail
What OnTheMarket endpoints does ReefAPI ship?
3 live read endpoints. Read-only data API: no writes, no account actions, no dashboard access on the target site.
OnTheMarket API
3 of 3 endpoints, ready to run
The results grid, 30 a page, with an added string per row that also tells you when a price was cut.
{ "ok": true, "meta": { "api": "onthemarket", "endpoint": "search", "mode": "live", "latency_ms": 522.2, "record_count": 28, "cache_hit": false, "completeness_pct": 100 }, "data": { "items": [ { "sponsored": false, "property_id": "5362474", "title": "2 bedroom apartment to rent", "property_type": "Apartment", "price_display": "£1,650 pcm (£381 pw)", "price_short": "£1,650", "price_qualifier": null, "bedrooms": 2, "address": "Cranmer Court, Ham, Richmond, Surrey, KT2", "added": "Added yesterday", "features": [ "Nearest station 1.2mi.", "Available as furnished or unfurnished", "Nearest school 0.1mi." ], "latitude": 51.431185, "longitude": -0.306768, "spotlight": true, "has_virtual_tour": null, "cover_image": "https://media.onthemarket.com/properties/5362474/1632373637/image-0-480x320.jpg", "images": [ "https://media.onthemarket.com/properties/5362474/1632373637/image-0-480x320.jpg", "https://media.onthemarket.com/properties/5362474/1632373637/image-1-480x320.jpg", "https://media.onthemarket.com/properties/5362474/1632373637/image-2-480x320.jpg" ], "details_url": "https://www.onthemarket.com/details/5362474/", "agent": { "id": 26802, "name": "Antony Roberts - Richmond", "logo_url": "https://media.onthemarket.com/agents/companies/70/211129120528197/logo-190x100.jpg", "details_url": "https://www.onthemarket.com/agents/branch/antony-roberts-richmond/" } }, { "sponsored": false, "property_id": "10318668", "title": "Studio to rent", "property_type": "Studio", "price_display": "£1,350 pcm (£312 pw)", "price_short": "£1,350", "price_qualifier": null, "bedrooms": null, "address": "Clarence Street, Kingston Upon Thames", "added": "Added today", "features": [ "Studio Apartment", "Large Reception Room", "Modern Kitchen" ], "latitude": 51.411105, "longitude": -0.301219, "spotlight": null, "has_virtual_tour": null, "cover_image": "https://media.onthemarket.com/properties/10318668/1640472497/image-0-480x320.jpg", "images": [ "https://media.onthemarket.com/properties/10318668/1640472497/image-0-480x320.jpg", "https://media.onthemarket.com/properties/10318668/1640472497/image-1-480x320.jpg", "https://media.onthemarket.com/properties/10318668/1640472497/image-2-480x320.jpg" ], "details_url": "https://www.onthemarket.com/details/10318668/", "agent": { "id": 46930, "name": "Martin & Co - Kingston upon thames", "logo_url": "https://media.onthemarket.com/agents/branches/46930/210202102814279/logo-190x100.jpg", "details_url": "https://www.onthemarket.com/agents/branch/martin-and-co-kingston-upon-thames/" } }, { "sponsored": false, "property_id": "20223320", "title": "2 bedroom apartment to rent", "property_type": "Apartment", "price_display": "£3,207 pcm (£740 pw)", "price_short": "£3,207", "price_qualifier": null, "bedrooms": 2, "address": "Queensway, London W2", "added": "Added yesterday", "features": [ "670 sq ft floor area", "Nearest station 0.1mi.", "Furnished" ], "latitude": 51.511163, "longitude": -0.187555, "spotlight": null, "has_virtual_tour": null, "cover_image": "https://media.onthemarket.com/properties/20223320/1640186728/image-0-480x320.jpg", "images": [ "https://media.onthemarket.com/properties/20223320/1640186728/image-0-480x320.jpg", "https://media.onthemarket.com/properties/20223320/1640186728/image-1-480x320.jpg", "https://media.onthemarket.com/properties/20223320/1640186728/image-2-480x320.jpg" ], "details_url": "https://www.onthemarket.com/details/20223320/", "agent": { "id": 54086, "name": "Inverness Properties UK - Bayswater", "logo_url": "https://media.onthemarket.com/agents/branches/54086/260414112721308/logo-190x100.png", "details_url": "https://www.onthemarket.com/agents/branch/inverness-properties-uk-bayswater/" } } ], "count": 28, "channel": "to-rent", "total_results": 5440, "sponsored_count": 2, "filters_applied": { "location-type": "country", "price-frequency": "pcm", "page": 1, "recently-added": "3-days", "exclusive-first": true, "let-agreed": false, "location-id": "london", "search-type": "to-rent", "frame-size": 30, "direction": "desc", "location-data": { "name": "London", "seo-id": "london", "location-type": "country" }, "view": "list", "sort-field": "recommended" }, "pages_fetched": 1, "stop_reason": "max_pages" } }
How the OnTheMarket API works
OnTheMarket is a normal ReefAPI surface — the same four rules that hold for every other engine on the key.
No OAuth app, no request signing, no per-site account. One key covers all 184 engines.
Every route is a POST with a JSON body. Parameters are validated against the published schema before anything is charged.
Credits, not seats. Failed and blocked calls are never charged, and cache hits cost nothing.
One envelope everywhere. meta carries latency_ms, record_count and the endpoint that answered.
Get the numeric price, not the string
The search grid prints prices the way a human reads them. The numeric value lives one level down, so a pipeline that needs to compare prices takes two calls rather than parsing text.
{"location": "london", "channel": "to-rent", "added_within_days": 3}2 flat credits for 30 rows. Drop the rows where title and price_display are both null — those are adverts, not listings.
{"property_id": "…"}3 credits. price_amount is a plain integer here (3650), where the grid only had the string "£3,650 pcm (£842 pw)".
Use the grid to decide what is worth opening and the detail call for anything you will do arithmetic on. Resolving place names through location_search costs nothing.
curl -X POST https://api.reefapi.com/onthemarket/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"location":"London"}'{
"ok": true,
"data": { … },
"meta": {
"api": "onthemarket",
"endpoint": "search",
"mode": "live",
"latency_ms": …,
"record_count": …
},
"error": null
}OnTheMarket price fields, and why some rows have almost nothing in them
Prices behave differently in the two channels and differently between search and property_detail, and every page of 30 results can contain sponsored rows that look like broken listings. These values were read from live for-sale and to-rent searches plus a property_detail call on 2026-08-27.
| Field | for-sale | to-rent | Note |
|---|---|---|---|
| property_id | "19648142" | "18159214" | 8 digits for a real listing. Sponsored rows carry a short 4-digit id such as "1689". |
| price_display | "£695,000" | "£1,625 pcm (£375 pw)" | Formatted text. The rental string packs monthly and weekly rent into one value. |
| price_short | "£695k" | "£1,625" | Abbreviated display text. |
| price_amount | absent on search rows | absent on search rows | The numeric price exists only on property_detail, which returned 695000 for the same listing. |
| price_qualifier | "Guide price" or null | null on every row sampled | Free text from the agent, not a fixed list. null means no qualifier was set. |
| added | "Added today", "Reduced today", "Added > 14 days" | "Added today", "Added < 14 days" | A human phrase, not a date. Use added_within_days to filter by recency. |
| features[0] | "Tenure: Share of freehold" | - | Tenure is usually first in features on a sale listing; property_detail returns it properly in key_info. |
| total_results vs count | 8933 vs 30 | 2071 vs 30 | total_results is the market-wide match count; count is what came back on this page. |
Sponsored developer adverts are returned inside items[] alongside real listings. They have a short property_id, an agent block, images under an /agent-products/in-market-ads/ path, and null for title, price_display, bedrooms, address, latitude, longitude and details_url. A Manchester to-rent page of 30 contained 2 of them. Filter on details_url being non-null to get properties only.
What is in the grid, what is an advert, and where the 50,000 stops being real
Measured on 2026-08-28 on London for-sale and to-rent, with three listings opened individually. The first line is a fault of ours that we found in an audit two days earlier and re-measured for this page.
A schema change on the source knocked this endpoint over in our own audit on 2026-08-26. Re-measured on 2026-08-28 against three listings — two for sale, one to rent — it returned completeness 100% on all three, 35 populated fields each, with the numeric price, key_info, images, floorplans and virtual tours present. It is working. We are writing the outage down because a page where nothing ever goes wrong is not a page anyone should believe.
The United Kingdom, for-sale and to-rent as separate channels. OnTheMarket is agent-first, so its stock is not a subset of the larger portals and is worth reading alongside them rather than instead of them.
Against us, and easy to trip over. Two of every 30 rows, on both channels, came back with a short numeric property_id (1689), every content field null, and a cover image pointing at an in-market advertising path. There is no is_ad field. Until we add one, drop any row where title and price_display are both null — that filter caught all of them in our samples and cost no real listings.
Against us. A rent row printed price_display as "£3,650 pcm (£842 pw)" and price_short as "£3,650" — both weekly and monthly in one string, no integer field. The same listing opened with property_detail returned price_amount 3650 as a number. Do not parse the grid string; open the listing.
Rows report added as a human phrase: "Added today", "Added yesterday", "Added < 7 days", "Added > 14 days" and, on 5 of 30 rent rows, "Reduced yesterday". So price cuts do surface here, but only as text and only in relative terms. There is no absolute listing date on the grid at all.
Against us. The schema says the value is mapped to the nearest supported window of 1, 3, 7 or 14. In practice 3 returned a full 30 rows and 1 returned zero rows with total_results null. Use 3 as your tightest window.
Every London for-sale query reported total_results 50,000, unchanged by filters. The reachable feed is far shorter: page 34 returned a full 30 rows, and pages 35, 36, 37 and 40 all returned zero with stop_reason empty_page. That is roughly 1,020 listings per query. Slice by borough or outcode rather than trusting the headline number.
key_info returns tenure with the lease term spelled out ("Leasehold | 100 yrs left"), the council tax band, and a real broadband figure ("Ultra-fast 1800Mbps") rather than a link to a checker. Alongside it: the full agent description, features, an EPC graph among the images, floorplans, and virtual tour links including Matterport.
Free, and it returns a slug and a display name — but a London query returned exactly one match where the same query against the larger UK portal returned a dozen typed regions. Good enough to resolve a name you already know; not a place picker.
location_search free, search 2 flat credits for up to 30 rows a page, property_detail 3. Nothing is per-row. Search responses came back in a quarter of a second to two seconds, detail under one.
What people build with OnTheMarket
The jobs this data is most often used for.
endpoints
credits per call
Property portals call location_search then search to list properties in an area.
Dashboards use property_detail to enrich a listing.
Analysts use search to track UK inventory over time.
What OnTheMarket data costs
The cheapest call here is 0 credits, so $15/mo (Pro) buys 10,000 of them — $1.50 per 1,000 credits. Credits roll over and never expire, and failed or blocked calls are not charged.
Full pricing →- 1,000 free credits on signup, no card
- One key, all 184 APIs, one credit pool
- Failed and blocked calls are never charged
- Credits roll over and never expire
Call it in two lines
Sign up, get 1,000 credits and one key that works on every engine. Then this is the whole protocol.
curl -X POST https://api.reefapi.com/onthemarket/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"location":"London"}'import requests
r = requests.post(
"https://api.reefapi.com/onthemarket/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"location": "London"
},
)
print(r.json()["data"])Have a question? We got answers.
The questions people actually ask before wiring up OnTheMarket.
Get a free key →Why do some items have a property_id but null for price, address and bedrooms?▾
Those are sponsored developer adverts, not properties, and OnTheMarket mixes them into the results grid. They are easy to spot: the property_id is short ("1689", "2448", "2598" rather than an 8-digit listing id), details_url is null, every listing field is null, and the images sit under an /agent-products/in-market-ads/ path. A Manchester to-rent search returned 2 of them inside a page of 30 on 2026-08-27. Filtering on details_url being non-null removes them cleanly.
Where is the numeric price? I only get "£695,000" as a string.▾
search rows carry price_display and price_short only, and price_amount is not present on them at all. property_detail does return it: the same listing came back with price_display "£695,000" and price_amount 695000 on 2026-08-27. If you are only doing search, parse the display string, or filter server-side with price_min and price_max, which do take integers in GBP.
How is rent represented?▾
As one formatted string holding both periods: price_display "£1,625 pcm (£375 pw)", with price_short "£1,625" carrying the monthly figure alone. For the to-rent channel, price_min and price_max are monthly rent in pounds, not annual and not weekly. price_qualifier was null on every rental row sampled, since qualifiers are a sales-market thing.
What is price_qualifier, and what values can it take?▾
It is the agent's wording next to the asking price. "Guide price" is what showed up on live London flats, and null is common, meaning the agent set no qualifier rather than that the field failed. It is free text from the listing rather than a fixed set, so treat any string as valid and match loosely if you are grouping on it. property_detail returns the same field alongside a price_coming_soon flag.
Is `added` a date I can sort on?▾
No. added is a human phrase. "Added today", "Reduced today", "Added < 14 days" and "Added > 14 days" were all returned on 2026-08-27, and everything older collapses into one bucket. To work with recency, use added_within_days, which maps to the nearest window the site supports (1, 3, 7 or 14 days), and sort with sort: "newest". Anything finer than 14 days is not exposed.
Does the search include Sold STC and Under Offer properties?▾
Not by default. The response echoes back what it asked for in filters_applied, and a plain for-sale search showed "under-offer": false on 2026-08-27. Set include_under_offer to true to bring those listings in. The same echo is useful for debugging generally, since it shows exactly how your location, price, bedroom and property-type filters were translated, including the location slug that was resolved.
Why does total_results not match the number of items I got?▾
total_results is how many listings match your filters across the whole market; count is how many rows this response contains. A London flats search from £400k to £900k with 2+ beds returned total_results 8933 with count 30, and a Manchester 2-bed rental search returned 2071 with count 30. Pages are 30 listings each, max_pages merges up to 42 of them in one call, and stop_reason tells you why fetching stopped, "max_pages" when you hit your own limit.
What happens if I pass a channel value the API does not know?▾
channel is one of the few parameters here that rejects rather than ignores. Sending channel "purchase" returned INVALID_PARAM with detail.allowed listing every accepted value: for-sale, to-rent, buy, sale, rent, lettings. So buy and sale are aliases of for-sale, and rent and lettings of to-rent. Most other filters (price, beds, property_type, radius, sort, keywords) ignore an invalid value, meaning a bad value silently drops the filter and widens your result set instead of erroring.
How do I get the right location slug?▾
Call location_search first and pass a match's location_id into search as location_id. A postcode district resolves cleanly: "SW9" returned exactly one match with location_id "sw9" on 2026-08-27. Broad names are looser, and "London" resolves to the slug "london", which the echoed filters_applied reported with location-type "country". Pair a slug with radius, in miles from the 0, 0.25, 0.5, 1, 3, 5, 10, 15, 20 and 30 set, when you want a ring around a point rather than an administrative area.
What is the OnTheMarket API?▾
OnTheMarket API is a ReefAPI endpoint group for onthemarket It returns live JSON through POST requests under /onthemarket/v1.
Is the OnTheMarket API free to try?▾
Yes. ReefAPI starts with 1,000 free credits, no card required. OnTheMarket calls use the same shared credit balance as every other ReefAPI engine.
Do I need an OnTheMarket login or account?▾
No login to OnTheMarket 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 OnTheMarket data?▾
The page example is captured from a live location_search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the OnTheMarket API use?▾
OnTheMarket actions currently cost 1-3 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
11 Real Estate APIs on the same key
One key, one credit pool, one response envelope. If you are pulling OnTheMarket, you are one call away from the rest of the category — no second contract, no second integration.
Need something this API does not do?
Name the endpoint, the field, or a source we do not carry yet. We ship new APIs every week and you would be first to get the key. Real people read every message and reply the same day.
Try it on your own data before you pay anything
The call above is the real endpoint, not a recording. A free key gives you 1,000 credits, the other 183 APIs, and the same envelope everywhere.
Endpoints, parameters and credit costs on this page are read from the live catalog and cannot drift from what the API accepts. Field notes were captured on 2026-08-28.