# OnTheMarket Scraper: UK Property Listings & Price Drop Monitor (`tan_carrel_h1q/onthemarket-property-monitor`) Actor

Scrape OnTheMarket.com UK homes for sale, rentals, new homes & commercial property. Beats the 1,020-result cap, tracks new listings and price drops, optional tenure, service charge & council tax. Self-identifying, robots.txt-compliant. Pay per listing.

- **URL**: https://apify.com/tan_carrel_h1q/onthemarket-property-monitor.md
- **Developed by:** [Steve P](https://apify.com/tan_carrel_h1q) (community)
- **Categories:** Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 property listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**OnTheMarket scraper for UK property listings: homes for sale, rentals, new homes and commercial property, plus new-listing and price-drop monitoring.** Pay per listing, no subscription. The Actor identifies itself in its User-Agent and checks `robots.txt` before every request, which is the access route OnTheMarket's own Terms of Use allow for automated programs.

> **Try it in 30 seconds:** keep the default input (Leeds, 30 listings) and press **Start**. Search-only listings cost about **$0.05** for 30. No proxy needed.

#### What does the OnTheMarket Property Monitor do?

Give it UK towns, areas, outcodes (`M1`), postcodes (`SW1A 1AA`) or OnTheMarket search URLs. It returns one clean row per listing:

- **Search data:** price (as a number), price qualifier ("Guide price"), address, bedrooms, bathrooms, property type, tenure, EPC band when stated, map coordinates, features, how recently it was added or reduced, main photo, estate agent branch.
- **Rent data:** monthly rent, weekly equivalent, rent frequency.
- **Commercial data:** price and floor/land area in sq ft (commercial listings link to LoopNet UK).
- **Optional detail-page data:** full description, postcode, tenure and lease years remaining, ground rent, service charge, council tax band, EPC, broadband speed, mobile coverage, nearest stations and schools, floorplan links, agent address and website, average price of similar homes nearby.
- **Optional monitoring:** schedule the same input daily or weekly and get only **new listings** and **price changes**, with `previousPrice`, `priceChange`, `priceChangePct`, `firstSeenAt` and a short price history.

#### Why use this OnTheMarket scraper?

- **Gets past the 1,020-result cap.** OnTheMarket shows at most 34 pages (1,020 listings) per search. When a location has more, the Actor automatically searches its sub-areas as well and removes duplicates, so "Leeds" does not stop at 1,020.
- **Built for monitoring.** Investors, agents and analysts rarely want the same 3,000 rows again tomorrow. Switch on monitoring and you only pay for listings that are new or changed.
- **Polite and compliant by design.** OnTheMarket's Terms of Use (section 3.3) permit automated programs that identify themselves uniquely in the User-Agent and fully comply with `robots.txt`. This Actor does both on every request. Because OnTheMarket's `robots.txt` disallows price, bedroom and sort URL parameters, the Actor applies those filters itself after reading each page instead of sending them to the site. See the legal note below.
- **Cheap to run.** Plain HTTP, no browser, no proxy. A typical run costs you only the per-listing price.
- **Personal-data light.** Output is property and business-level data. The estate agent branch phone number is **off by default** (a toggle adds it).

#### What can you use OnTheMarket data for?

- **Property investors:** find new listings and price cuts in the postcodes you watch; screen leasehold costs (ground rent, service charge, lease length) and council tax band.
- **Estate agents and lettings agents:** track competitors' stock and asking rents by area; see how many listings an agent branch has.
- **Analysts and proptech:** build asking-price and rent datasets, compare areas, feed dashboards in Google Sheets, Looker Studio or a database.
- **Relocation and student housing:** watch rentals by outcode and budget and get only new ones.
- **Commercial property researchers:** pull offices, retail, industrial and land for sale or to let by town.

#### How do I scrape OnTheMarket listings?

1. Open the Actor and enter one or more **Locations** (`Leeds`, `Didsbury`, `LS6`, `SW1A 1AA`), or paste **OnTheMarket search URLs**.
2. Choose **For sale** or **To rent** and the **category** (Residential, New homes, Commercial).
3. Optionally set price, bedrooms, property types and keywords. These are applied by the Actor after fetching.
4. Set **Maximum listings** (start small), press **Start**, then download JSON, CSV, Excel or use the API.

**Tip:** broad filters on a very large location mean the Actor reads many pages that it then filters out. Use specific areas or outcodes for tight filters (for example `LS6` or `Didsbury` instead of `Manchester`). You only pay for listings that are delivered.

#### Input example

```json
{
  "locations": ["Leeds", "M20"],
  "listingType": "for-sale",
  "category": "residential",
  "minPrice": 100000,
  "maxPrice": 300000,
  "minBedrooms": 2,
  "propertyTypes": ["house", "flat"],
  "maxItems": 200,
  "scrapeDetails": true
}
```

**Scheduled monitoring example (run daily):**

```json
{
  "locations": ["Hackney", "Islington"],
  "listingType": "to-rent",
  "maxPrice": 2500,
  "monitoringMode": "new_and_changed",
  "monitorId": "london-rentals",
  "maxItems": 500
}
```

#### Output example

```json
{
  "id": "20642634",
  "url": "https://www.onthemarket.com/details/20642634/",
  "listingType": "for-sale",
  "title": "2 bedroom apartment for sale",
  "address": "11 Lockside Lane, Salford M5",
  "price": 248000,
  "priceText": "£248,000",
  "priceQualifier": "Offers in region of",
  "bedrooms": 2,
  "bathrooms": 2,
  "propertyType": "Apartment",
  "tenure": "Leasehold",
  "leaseYearsRemaining": 991,
  "groundRentPerYear": 325,
  "serviceChargePerYear": 3141,
  "councilTaxBand": "C",
  "postcode": "M5 4YQ",
  "broadbandMaxDownloadMbps": 1000,
  "latitude": 53.47987,
  "longitude": -2.26395,
  "addedOrReducedLabel": "Added yesterday",
  "agentName": "Bee City Living - Salford",
  "monitorStatus": "price_reduced",
  "previousPrice": 260000,
  "priceChange": -12000,
  "priceChangePct": -4.6
}
```

#### Output fields

| Group | Fields |
|---|---|
| Core | `id`, `url`, `listingType`, `category`, `title`, `address`, `price`, `priceText`, `priceQualifier`, `rentFrequency`, `weeklyRent`, `bedrooms`, `bathrooms`, `propertyType`, `tenure`, `epcRating`, `features`, `latitude`, `longitude`, `searchLocation`, `scrapedAt` |
| Freshness | `addedOrReducedLabel`, `isReducedLabel`, `listingAgeDays`, `listingAgeBucket`, `isRecentlyAdded`, `isExclusive`, `hasVirtualTour` |
| Media | `primaryImage`, `imageCount`, `images` (optional) |
| Agent (business) | `agentName`, `agentBranchId`, `agentUrl`, `agentPhone` (optional) |
| Commercial | `sizeSqFt`, `sizeSqFtMin`, `sourceIsExternal` |
| Detail page (optional) | `postcode`, `description`, `leaseYearsRemaining`, `groundRentPerYear`, `serviceChargePerYear`, `councilTaxBand`, `broadband*`, `mobileCoverage`, `nearestStations`, `nearestSchools`, `floorplanUrls`, `areaAveragePrice`, `agentCompany`, `agentBranchAddress`, `agentWebsite` |
| Monitoring (optional) | `monitorStatus` (`new`, `price_reduced`, `price_increased`, `unchanged`), `isNew`, `firstSeenAt`, `previousPrice`, `priceChange`, `priceChangePct`, `priceHistory` |

#### How much does it cost to scrape OnTheMarket?

Pay per event, only for listings actually delivered. No monthly fee. Apify platform usage is included in these prices.

| Event | Price | When |
|---|---|---|
| Property listing | **$0.0015** (about $1.50 per 1,000) | One listing delivered to the dataset |
| Listing detail data | **+$0.0025** (about $2.50 per 1,000) | Only if "Add detail-page data" is on and the detail page was fetched for that listing |

Examples: 1,000 search-only listings ≈ **$1.50**; 1,000 listings with full detail ≈ **$4.00**; a daily monitor that finds 20 new listings costs about **$0.03** a day. Your **Maximum cost per run** setting is respected: the Actor stops before it would exceed it.

> The live prices are shown on this page's pricing panel.

#### Is it legal to scrape OnTheMarket?

*This is general information, not legal advice.* Scraping publicly available web pages is generally lawful in the UK, but there are rules you must follow:

- **Terms of Use.** OnTheMarket's Terms (section 3.3) forbid automated access unless the program identifies itself uniquely in the User-Agent and is fully compliant with the Robots Exclusion Protocol. This Actor sends a unique User-Agent (`OTMPropertyMonitorActor/1.0 …`) and checks every URL against the live `robots.txt`; URLs that `robots.txt` disallows are skipped. You remain responsible for how you use the data.
- **Copyright and database rights.** Photos, descriptions and floorplans belong to OnTheMarket and the agents. Use the data for analysis and monitoring. Do not republish photos or descriptions commercially without permission.
- **Personal data (UK GDPR).** The Actor returns property and business-level data. The estate agent branch phone is optional and off by default. If you combine the data with anything that identifies individuals (for example vendors), UK GDPR and PECR apply to you. Do not use the data for unsolicited marketing to individuals.
- Be considerate: the Actor uses low concurrency and short delays by default. This Actor is not affiliated with or endorsed by OnTheMarket.com Ltd.

#### FAQ

**Do I need a proxy?** No. OnTheMarket pages load without one. If your runs ever show blocked (403) responses, turn on Apify Proxy in the input.

**Why are my price and bedroom filters not sent to the website?** OnTheMarket's `robots.txt` disallows URLs with `min-price`, `max-price`, bedroom, radius and sort parameters. To stay compliant, the Actor reads normal search pages and filters the rows itself. If you paste a search URL that contains such parameters, price and bedroom values are converted into filters and the rest are ignored.

**Can I get more than 1,020 listings for one town?** Yes. With "Split searches over 1,020 results" on (default), the Actor also searches the town's sub-areas, up to two levels deep, and removes duplicates. Very large cities may still not be 100% complete because listings can sit in overlapping areas; spot-check against the site's own total.

**How does monitoring work?** Every listing's price and first-seen date are stored in a named key-value store in your own Apify account (`otm-monitor-<monitorId>`). The next run with the same Monitor ID compares against it. Use a different Monitor ID per watch list. History older than 120 days is dropped.

**Can I schedule it?** Yes: save a Task and add a Schedule (for example daily at 07:00). Pair it with an integration to send new listings to Slack, email or Google Sheets.

**Why does a commercial listing have no detail data?** Commercial listings on OnTheMarket are hosted on LoopNet UK, which this Actor does not crawl.

**How fresh is the data?** It is read live from OnTheMarket at run time.

**Something broke or I need a field.** Open an issue on the Actor's page. Website layouts change; fixes are usually quick.

#### Related searches

OnTheMarket scraper, OnTheMarket API, scrape OnTheMarket listings, UK property data, UK property scraper, UK rental listings data, property price drop alerts, UK asking prices dataset.

# Actor input Schema

## `locations` (type: `array`):

UK towns, areas, outcodes or postcodes, e.g. "Leeds", "Didsbury", "M1", "SW1A 1AA", "Greater Manchester". One search is run per location.

## `searchUrls` (type: `array`):

Paste OnTheMarket search result URLs instead of (or as well as) locations. Price and bedroom parameters in the URL are applied as filters; other parameters that OnTheMarket's robots.txt disallows are ignored.

## `listingType` (type: `string`):

Search properties for sale or to rent. (New homes are for sale only.)

## `category` (type: `string`):

Residential homes, new-build developments, or commercial property (offices, retail, industrial, land). Commercial listings link out to LoopNet UK, so detail enrichment is not available for them.

## `minPrice` (type: `integer`):

Only keep listings priced at or above this (per month for rentals). Applied by the Actor, not the website.

## `maxPrice` (type: `integer`):

Only keep listings priced at or below this (per month for rentals).

## `minBedrooms` (type: `integer`):

Only keep listings with at least this many bedrooms.

## `maxBedrooms` (type: `integer`):

Only keep listings with at most this many bedrooms.

## `propertyTypes` (type: `array`):

Only keep these types. Leave empty for all.

## `keywords` (type: `array`):

Keep a listing only if its title, address or key features contain at least one of these words (case-insensitive), e.g. "garden", "investment", "chain free".

## `excludeKeywords` (type: `array`):

Drop listings that contain any of these words, e.g. "retirement", "shared ownership".

## `recentlyAddedOnly` (type: `boolean`):

Keep only listings flagged by OnTheMarket as recently added (about the last 7 days).

## `reducedOnly` (type: `boolean`):

Keep only listings whose label says they were reduced.

## `maxItems` (type: `integer`):

Hard limit on listings delivered (and charged) in this run. Start small, e.g. 30.

## `scrapeDetails` (type: `boolean`):

Open each listing and add full description, postcode, tenure and lease years, ground rent, service charge, council tax band, EPC, broadband speed, nearest stations and schools, floorplan links, agent address and website. Charged as an extra event per listing (see pricing).

## `splitLargeSearches` (type: `boolean`):

OnTheMarket only shows 34 pages (1,020 listings) per search. When a location has more, the Actor also searches its sub-areas to get past the cap. Duplicates are removed.

## `maxPagesPerSearch` (type: `integer`):

30 listings per page; the website allows at most 34.

## `includeAllImages` (type: `boolean`):

Add an `images` array with every thumbnail URL from the search result. By default only the main image is returned. Images and descriptions are the property of OnTheMarket and the agents; do not republish them without permission.

## `includeAgentPhone` (type: `boolean`):

Add the estate agent branch's business telephone number. Off by default to keep personal-data exposure low; only enable if you have a lawful use for it.

## `monitoringMode` (type: `string`):

For scheduled runs. The Actor remembers every listing it has seen (in a named key-value store in your own account). "New listings only" returns just listings not seen before; "New listings and price changes" also returns listings whose asking price went up or down. Off returns everything.

## `monitorId` (type: `string`):

Name of this watch list. Use different IDs for different searches (e.g. "leeds-rentals"). Runs with the same ID share history.

## `maxConcurrency` (type: `integer`):

How many pages are fetched at once. Keep it low: the Actor is polite by design.

## `requestDelayMs` (type: `integer`):

Average pause before each request, per worker.

## `proxyConfiguration` (type: `object`):

Normally not needed: OnTheMarket pages load without a proxy. Enable Apify Proxy only if you see blocked (403) responses.

## Actor input object example

```json
{
  "locations": [
    "Leeds"
  ],
  "searchUrls": [],
  "listingType": "for-sale",
  "category": "residential",
  "propertyTypes": [],
  "keywords": [],
  "excludeKeywords": [],
  "recentlyAddedOnly": false,
  "reducedOnly": false,
  "maxItems": 30,
  "scrapeDetails": false,
  "splitLargeSearches": true,
  "maxPagesPerSearch": 34,
  "includeAllImages": false,
  "includeAgentPhone": false,
  "monitoringMode": "off",
  "monitorId": "default",
  "maxConcurrency": 3,
  "requestDelayMs": 250,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `listings` (type: `string`):

One row per listing: price, address, bedrooms, type, agent, location, monitoring fields and optional detail-page data.

## `summary` (type: `string`):

Pages fetched, listings seen, duplicates, filtered, delivered, locations not found, data volume.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "locations": [
        "Leeds"
    ],
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("tan_carrel_h1q/onthemarket-property-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "locations": ["Leeds"],
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("tan_carrel_h1q/onthemarket-property-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "locations": [
    "Leeds"
  ],
  "maxItems": 30
}' |
apify call tan_carrel_h1q/onthemarket-property-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tan_carrel_h1q/onthemarket-property-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/VEOezOhe7cAwLcwZz/builds/PMAEvQXvueQSWa8E2/openapi.json
