Irmu
Guides11 minIrmu Engineering

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.

rightmove.sh
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.

rightmove_pipeline.py
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))
rightmove.mjs
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.

schema.sql
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

Start building with Irmu today

200 free credits every month, no card required. Every API, every integration, one key.