Rightmove API Alternative: How to Scrape Rightmove Listings (2026)
Rightmove's data feeds are built for estate agents, not analysts. Here is how to collect UK listing prices, sizes and price changes with a single crawl endpoint, plus code and a schema for tracking them.
Why people look for a Rightmove API
Rightmove is the default starting point for anyone who wants to understand UK housing: asking prices, how long homes sit on the market, how often they get reduced. It is also one of the portals people most often search an API for, and come away disappointed.
This guide treats Rightmove as what it is for a data team: a public website with a predictable structure. We will turn search pages into a list of property IDs, turn each property page into a clean row, and keep a history so you can see reductions as they happen.
Is there an official Rightmove API?
Rightmove publishes an Automated Datafeed (ADF) and a Real Time Datafeed, but they exist so estate agents and their software providers can send, update and remove their own listings, not so third parties can pull the portal's data out. If you are not an agent or a Rightmove partner, there is no general self-serve API you can sign up for and call.
That leaves two options: license data from a commercial provider, or collect the public pages yourself. This guide covers the second, and the legal section later covers what you should and should not keep.
What a listing actually contains
Before writing code, decide what you need. A typical Rightmove listing page carries more than price and size, and the extra fields (especially the EPC rating) are often what makes an analysis useful.
UK listings mix square feet and square metres, and prices often carry a qualifier ('Offers in excess of', 'Guide price') that changes what the number means. Keep the qualifier as its own field instead of throwing it away.
- Asking price, and whether it is shown as 'Offers over', 'Guide price' or a fixed amount
- Property type, bedrooms, bathrooms and, where shown, floor area in sq ft or sq m
- Tenure (freehold, leasehold, share of freehold) and, for leaseholds, years remaining when listed
- Address as displayed, outcode/postcode district, and coordinates if the page exposes them
- Date added or reduced, and the listing's own price history when shown
- Council tax band and EPC rating
- Agent branch name (a business, not a person) and the listing ID
How search pages and listing pages fit together
Search pages are driven by query parameters: a location identifier, filters like `minBedrooms` or `maxPrice`, a sort order, and an `index` offset that moves in steps of 24 (we saw 24 results per page plus a featured listing). Copy a search from your browser, then change only `index` to paginate. Treat these parameter names as something to confirm in your own browser, since portals change them.
Property pages live at `/properties/{id}`, and that numeric ID is the natural key for deduplication. The pipeline is always the same: crawl search pages to discover listing URLs, then crawl each listing once to get its details, and re-crawl listings on a schedule to catch price changes.
Fetching a listing with Irmu
Irmu's GET /crawl endpoint loads the page in a real browser and can return structured JSON through `ai_query`. For Rightmove, three settings matter: `premium=true` for residential IPs, `country=gb` so the visit comes from the UK, and `wait_until=domcontentloaded` so you don't wait on endless tracking scripts.
The prompt is written in English, the language of the page, and explicitly excludes personal data. The answer comes back in the `data` field. If the page is slow, Irmu answers `202` with a `job_id` (already paid for); collecting it with GET /crawl/{job} is free. Keep prompts under 1,000 characters, which is the `ai_query` limit.
curl -G https://app.irmu.com/api/crawl \
-H "Authorization: Bearer $IRMU_API_KEY" \
--data-urlencode "url=https://www.rightmove.co.uk/properties/000000000" \
--data-urlencode "premium=true" \
--data-urlencode "country=gb" \
--data-urlencode "wait_until=domcontentloaded" \
--data-urlencode "ai_query=Return JSON with: listing_id, price_gbp (number), price_qualifier, property_type, bedrooms, bathrooms, floor_area_sqft, tenure, lease_years_remaining, address, postcode_district, council_tax_band, epc_rating, added_or_reduced, agent_branch. Use null when a value is not on the page. Do not include phone numbers or names of people."The URL above is a placeholder. Open a real listing in your browser, check which fields are visible, and adjust the prompt to match before running it at scale.
From search results to a dataset
The Python version below walks through search pages, collects listing URLs, dedupes them by ID and fetches each listing. It stops paginating when a page returns no new IDs, and it checks for a key field before saving, because a block page can still come back with a 200.
import json, re, time, requests
API = "https://app.irmu.com/api"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
BASE = {"premium": "true", "country": "gb", "wait_until": "domcontentloaded"}
ID_RE = re.compile(r"/properties/(\d+)")
def crawl(url, ai_query):
r = requests.get(f"{API}/crawl", headers=HEADERS, timeout=200,
params={**BASE, "url": url, "ai_query": ai_query})
if r.status_code == 202: # still running: collect it
job = r.json()["job_id"]
for _ in range(60):
time.sleep(5)
r = requests.get(f"{API}/crawl/{job}", headers=HEADERS, timeout=60)
if r.status_code != 202:
break
r.raise_for_status()
answer = r.json().get("data") # the ai_query answer lives in "data"
if isinstance(answer, str):
try:
answer = json.loads(answer)
except ValueError:
answer = None
return answer or {}
SEARCH_PROMPT = """Return JSON {\"listings\": [{\"url\", \"price_gbp\", \"bedrooms\", \"address\"}]} for every property card in the results. Ignore featured/premium adverts that repeat elsewhere on the page."""
LISTING_PROMPT = """Return JSON with: listing_id, price_gbp (number), price_qualifier, property_type, bedrooms, bathrooms, floor_area_sqft, tenure, lease_years_remaining, address, postcode_district, council_tax_band, epc_rating, added_or_reduced, agent_branch. Use null when a value is not on the page. Do not include phone numbers or names of people."""
def search_urls(search_url, max_pages=20):
seen = {}
for page in range(1, max_pages + 1):
url = search_url.format(page=page * 24 - 24) # {page} marks the page parameter
data = crawl(url, SEARCH_PROMPT)
cards = next(iter(data.values()), []) if isinstance(data, dict) else []
new = 0
for c in cards:
m = ID_RE.search(c.get("url") or "")
if m and m.group(1) not in seen:
seen[m.group(1)] = c["url"]
new += 1
if new == 0:
break
time.sleep(2) # be polite
return seen
for listing_id, url in search_urls("https://www.rightmove.co.uk/property-for-sale/find.html?locationIdentifier=REGION%5E87490&minBedrooms=2&sortType=6&index={page}").items():
row = crawl(url, LISTING_PROMPT)
if not row: # likely a block or removed page
print("skip", listing_id)
continue
print(json.dumps({"id": listing_id, **row}, ensure_ascii=False))const API = "https://app.irmu.com/api";
const headers = { Authorization: `Bearer ${process.env.IRMU_API_KEY}` };
export async function crawl(url, aiQuery) {
const qs = new URLSearchParams({
url, ai_query: aiQuery, premium: "true", country: "gb", wait_until: "domcontentloaded",
});
let res = await fetch(`${API}/crawl?${qs}`, { headers });
if (res.status === 202) {
const { job_id: job } = await res.json();
do {
await new Promise((r) => setTimeout(r, 5000));
res = await fetch(`${API}/crawl/${job}`, { headers });
} while (res.status === 202);
}
if (!res.ok) throw new Error(`Irmu ${res.status}`);
const { data } = await res.json(); // the ai_query answer
if (typeof data !== "string") return data ?? {};
try { return JSON.parse(data); } catch { return {}; }
}With `ai_query`, the answer comes back in the `data` field of the JSON reply; a slow crawl answers `202` with a `job_id` that you collect for free from GET /crawl/{job}. `{page}` in the search URL marks the page parameter; braces already in the URL are doubled for Python's `format`.
Storing listings and price changes
Keep two tables: one row per listing with its latest details, and an append-only table with one row each time you see a price. Reductions, relistings and time on market all fall out of the second table.
create table listings (
portal text not null default 'rightmove',
listing_id text not null,
url text not null,
property_type text,
bedrooms numeric,
area_m2 numeric,
postcode text,
energy_class text,
details jsonb, -- everything else, in the page's own terms
first_seen timestamptz not null default now(),
last_seen timestamptz not null default now(),
status text, -- active, under offer, removed
primary key (portal, listing_id)
);
create table price_snapshots (
portal text not null,
listing_id text not null,
seen_at timestamptz not null default now(),
price numeric,
currency text not null default 'GBP',
price_note text, -- qualifier or unit as shown
primary key (portal, listing_id, seen_at)
);Rightmove details that trip people up
- Featured listings can appear at the top of several pages; dedupe on the property ID, not on the URL you found it at.
- New homes developments often group several units under one page with a price range. Store the range as two numbers, not one.
- 'Under offer' and 'Sold STC' listings are still shown; record the status so they don't skew asking-price averages.
What it costs
A premium, rendered crawl is 25 credits and `ai_query` adds 5, so a fully-extracted page is 30 credits.
Say you track 5,000 London listings. A weekly pass over the property pages is about 20,000 requests a month; at 30 credits each (premium render plus `ai_query`) that is 600,000 credits, inside the Standard plan's 1M. Searching weekly to discover new IDs adds a few hundred requests on top.
On billing: if www.rightmove.co.uk returns a 404 or a block page, that still counts as a successful, billed request, because Irmu fetched what the site served. Only failures on Irmu's side, and domains Irmu doesn't cover, are not charged. A slow, narrow crawl is cheaper as well as more polite.
Legal and ethical limits
Read Rightmove's terms of use before you start, and get legal advice for commercial use. Facts such as price, size and location are generally treated differently from creative content: descriptions and photos are protected by copyright, so don't republish them.
Agent and private-seller contact details are personal data under UK GDPR and the Data Protection Act 2018. The prompts above deliberately leave them out. Keep request rates low, crawl only the areas you need, and stop if the site asks you to.
Does Rightmove have a public API?
Not one that third parties can sign up for. Its integrations are for estate agents and partners who send listings to the portal.
Can I get sold prices this way?
Sold prices in England and Wales come from HM Land Registry's Price Paid Data, which is published as open data. Combine that with asking prices from listings instead of scraping sold-price pages.
Which country should I route through?
Use `country=gb`. UK portals expect UK visitors, and residential UK IPs (`premium=true`) are what a real visitor looks like.
Where to go next
Start with one listing: run the curl command, compare the JSON with what you see in your browser, then fix the prompt. Once ten listings look right, schedule the pipeline. For a wider view of European portals, see our Idealista guide; for a market-wide approach, see the real estate data API guide and the Real Estate solution page.
Endpoints used in this guide
- GET /crawlCrawl a urlScraping API →
- GET /crawl/{job}Collect a crawlScraping API →
Keep reading
How to Give Claude, ChatGPT and Cursor Live Web Access with an MCP Server
Connect the Irmu MCP server once and your AI assistant can fetch pages, take screenshots, check Google AI Overviews and pull Booking.com prices — with setup for every client and real prompts.
GuidesBuild Your Own AI Overview Tracker: Monitor Google AI Overviews by Keyword (2026)
Track which of your keywords trigger a Google AI Overview, what it says and whether your pages are cited — across countries, on a schedule, with Python, Node and SQL.
GuidesChatGPT Brand Monitoring: Track What AI Tells Your Customers (2026)
Build your own LLM visibility tracker: run a fixed set of buyer questions through ChatGPT on a schedule, record whether your brand is mentioned, where, and which sources are cited — with Python, Node and SQL.
Start building with Irmu today
200 free credits every month, no card required. Every API, every integration, one key.