# Zoopla API scraper — UK property data: homes for sale and to rent, new-build developments, full property detail (price, beds, baths, receptions, floor area, tenure, EPC, council tax, floorplans, images, coordinates, agent), Land-Registry sold prices by area, per-address sale history and estate-agent branches — no Zoopla account required

> Resolve a free-text place, postcode or station into the canonical Zoopla area path every other action takes. An unambiguous name returns one match; an ambiguous one ('Richmond', 'Kingston') returns every candidate Zoopla offers.
> ReefAPI engine `zoopla` · 7 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/zoopla/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed calls are free except verified SHEIN NOT_FOUND on product/detail and price (4 credits).
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http). Send the key as `Authorization: Bearer <key>`, or put it in the URL (`?key=<key>`) when the client has no header field, as ChatGPT does.

## Endpoints

### POST https://api.reefapi.com/zoopla/v1/location_search — 1 credit
Resolve a free-text place, postcode or station into the canonical Zoopla area path every other action takes. An unambiguous name returns one match; an ambiguous one ('Richmond', 'Kingston') returns every candidate Zoopla offers.

**Parameters:**
- `query` (string, required) — Town, district, postcode, outcode or station name.
- `section` (enum, optional, default "for_sale") — Which Zoopla market to search. The two commercial markets take their own property_type values (offices, retail-premises, industrial-sites, hospitality, land) and carry a floor-area range rather than bedrooms. [one of: for_sale, to_rent, new_homes, commercial_sale, commercial_rent]

**Returns:** matches[] (geo, label, url, exact), count, resolved (the single match when Zoopla resolved the name outright)

**Example request body:**
```json
{
  "query": "Clapham, London"
}
```

### POST https://api.reefapi.com/zoopla/v1/search — 2 credits
Search Zoopla listings in an area — homes for sale, homes to rent, new-build developments, or commercial property for sale or to rent — with the filters the site itself exposes: price band, minimum/maximum beds, minimum baths, property type, floor-area band, search radius, listed-within, free-text keyword, features, sold/auction inclusion and sort order. 25 listings a page.

**Parameters:**
- `geo` (string, required) — Where to look: a Zoopla area path ('sw9', 'surrey/richmond', 'london/clapham'), a full Zoopla URL, or just a place name or postcode ('Leeds', 'SW9 9EF'). Zoopla's own slugs are not guessable — Leeds is 'west-yorkshire/leeds' while Liverpool is plain 'liverpool' — so a plain name is resolved for you and the path used comes back as `area` (with `area_resolved_from` set). Use location_search when a name is ambiguous, e.g. Richmond.
- `section` (enum, optional, default "for_sale") — Which Zoopla market to search. The two commercial markets take their own property_type values (offices, retail-premises, industrial-sites, hospitality, land) and carry a floor-area range rather than bedrooms. [one of: for_sale, to_rent, new_homes, commercial_sale, commercial_rent]
- `page` (integer, optional, default 1) — 1-based page. Zoopla serves 25 listings a page and stops at page 40 (returned as page_cap); narrow the area or the filters to reach more.
- `price_min` (integer, optional) — Minimum price in GBP — the asking price, or the MONTHLY rent when section=to_rent (set price_frequency=per_week to read it as weekly).
- `price_max` (integer, optional) — Maximum price in GBP, same basis as price_min.
- `beds_min` (integer, optional) — Minimum bedrooms (2 means 2 or more; 0 includes studios).
- `beds_max` (integer, optional) — Maximum bedrooms.
- `baths_min` (integer, optional) — Minimum bathrooms (2 means 2 or more).
- `floor_area_min` (integer, optional) — Minimum internal floor area in square FEET (Zoopla's own unit on listings; the sold register uses square metres).
- `floor_area_max` (integer, optional) — Maximum internal floor area in square feet.
- `property_type` (enum, optional) — One property type. The first nine apply to for_sale / to_rent / new_homes; offices, retail-premises, industrial-sites, hospitality and land apply to the two commercial sections. Zoopla takes a single value here — passing two does not union them, so ask for one type per call. [one of: houses, flats, detached, semi_detached, terraced, bungalows, land, park_home, farms_land, offices, retail-premises, industrial-sites, hospitality, land]
- `radius` (enum, optional) — Widen the search around the area, in miles. Zoopla accepts only these steps. [one of: 0, 0.25, 0.5, 1, 3, 5, 10, 15, 20, 30, 40]
- `added` (enum, optional) — Only listings Zoopla published within this window. [one of: 24_hours, 3_days, 7_days, 14_days, 30_days]
- `keywords` (string, optional) — Free-text term Zoopla matches inside the listing (e.g. 'garden', 'balcony', 'garage'). Max 80 characters.
- `features` (array, optional) — Require these features. Several are combined with AND. [one of: has_garden, has_parking_garage, has_balcony_terrace, has_fireplace, has_porter_security]
- `new_homes` (enum, optional) — How new-build listings are treated inside a for-sale search. [one of: include, exclude, only]
- `sort` (enum, optional, default "recommended") — Result ordering. These are the only orders Zoopla offers. [one of: recommended, newest_listings, highest_price, lowest_price, most_reduced]
- `include_sold` (boolean, optional, default false) — Also return Sold STC / Under Offer listings (for-sale only).
- `auctions_only` (boolean, optional, default false) — Only properties going to auction.
- `furnished_state` (enum, optional) — RENT only: furnishing. [one of: furnished, unfurnished, part_furnished]
- `price_frequency` (enum, optional) — RENT only: how price_min/price_max are read. Rows always come back with both the monthly price and Zoopla's own weekly figure. [one of: per_month, per_week]
- `student_only` (boolean, optional, default false) — RENT only: student accommodation.
- `shared_only` (boolean, optional, default false) — RENT only: shared accommodation / rooms.
- `bills_included` (boolean, optional, default false) — RENT only: bills included in the rent.
- `available_from` (string, optional) — RENT only: Zoopla's availability filter (e.g. 'now' or a date).
- `max_rotations` (integer, optional, default 4) — How hard to retry when the site refuses a request.

**Returns:** listings[] (listing_id, url, title, address, price [always on the basis price_display shows — monthly for a letting], price_display, price_frequency, price_quoted + price_quoted_frequency [the agent's own figure and whether it is per week, per month or per annum], price_per_week, bedrooms, bathrooms, receptions, floor_area_sqft (+ floor_area_sqft_max on commercial), property_type, tenure_tags, status, under_offer, listed_on, available_from, latitude, longitude, images[], agent{}), featured[], count, total_results, total_results_capped, page, page_size, page_cap, has_more, filters_applied, area, area_resolved_from, search_url

**Example request body:**
```json
{
  "geo": "sw9"
}
```

### POST https://api.reefapi.com/zoopla/v1/property_detail — 2 credits
One Zoopla listing in full: price (rent in both frequencies), full description, key features, tenure, service charge, ground rent, council tax band, EPC rating and certificate, floorplans, every image, nearest stations with distances, coordinates, postcode and the marketing agent.

**Parameters:**
- `listing_id` (string, required) — Zoopla listing id — the digits in a /details/<id>/ URL, or any search row's listing_id. A full Zoopla listing URL also works.
- `max_rotations` (integer, optional, default 4) — How hard to retry when the site refuses.

**Returns:** listing{} — price, price_display, price_frequency, price_per_week, price_per_month, description, key_features[], tenure, service_charge, ground_rent, council_tax_band, epc_rating, epc_images[], images[], floorplans[], nearest_stations[], latitude, longitude, postcode, uprn, agent{}

**Example request body:**
```json
{
  "listing_id": "74328828"
}
```

### POST https://api.reefapi.com/zoopla/v1/sold_prices — 2 credits
Zoopla's Land-Registry sold-price register for an area: every recorded address with its UPRN, last sale date and price, bedrooms, bathrooms, receptions, tenure and floor area in square metres. 25 addresses a page, newest sale first.

**Parameters:**
- `geo` (string, required) — Where to look: a Zoopla area path ('sw9', 'surrey/richmond', 'london/clapham'), a full Zoopla URL, or just a place name or postcode ('Leeds', 'SW9 9EF'). Zoopla's own slugs are not guessable — Leeds is 'west-yorkshire/leeds' while Liverpool is plain 'liverpool' — so a plain name is resolved for you and the path used comes back as `area` (with `area_resolved_from` set). Use location_search when a name is ambiguous, e.g. Richmond.
- `page` (integer, optional, default 1) — 1-based page. Zoopla serves 25 listings a page and stops at page 40 (returned as page_cap); narrow the area or the filters to reach more.
- `max_rotations` (integer, optional, default 4) — How hard to retry when the site refuses.

**Returns:** properties[] (uprn, address, postcode, latitude, longitude, last_sold_price, last_sold_date, bedrooms, bathrooms, receptions, property_type, tenure, floor_area_sqm, url), count, total_results, page, page_size, has_more, area, area_resolved_from

**Example request body:**
```json
{
  "geo": "sw9"
}
```

### POST https://api.reefapi.com/zoopla/v1/property_history — 1 credit
One address by UPRN: every sale Zoopla holds from the Land Registry with its price, date and percentage change, plus the property's own past Zoopla listings with their asking prices, photos and floorplans, its EPC rating, tenure and floor area.

**Parameters:**
- `uprn` (string, required) — The UPRN from a sold_prices row (or the digits in a /property/uprn/<uprn>/ URL).
- `max_rotations` (integer, optional, default 4) — How hard to retry when the site refuses.

**Returns:** property{} — uprn, address, postcode, latitude, longitude, property_type, tenure, bedrooms, bathrooms, receptions, floor_area_sqm, epc_rating, sales[] (date, price, change_pct, change_amount), past_listings[], live_listings[], sold_prices_source

**Example request body:**
```json
{
  "uprn": "10091117300"
}
```

### POST https://api.reefapi.com/zoopla/v1/agent — 1 credit
One estate-agent branch: address, memberships, opening times, website, its own for-sale and to-rent averages and weeks-on-market, and the properties it currently has listed on Zoopla. A new-homes branch id resolves to the housebuilder's development instead (kind='developer'): its price and bedroom range, its plots and the schools, stations and shops Zoopla lists nearby.

**Parameters:**
- `branch_id` (string, required) — Zoopla branch id (a search row's agent.branch_id) or a full /find-agents/branch/... URL.
- `max_rotations` (integer, optional, default 4) — How hard to retry when the site refuses.

**Returns:** agent{} — kind ('agency' or 'developer'), branch_id, name, address, postcode, latitude, longitude, website, memberships[], opening_times[], description, stats{for_sale,to_rent: listings, average_price, weeks_on_market}, listings{for_sale[], to_rent[]}; for a developer also development_name, company_name, price_min/max, beds_min/max, points_of_interest[] and listings.new_homes[]

**Example request body:**
```json
{
  "branch_id": "48920"
}
```

### POST https://api.reefapi.com/zoopla/v1/agent_search — 1 credit
The estate or letting agents covering an area, with each branch's average asking price, average weeks on market and live listing count.

**Parameters:**
- `geo` (string, required) — Where to look: a Zoopla area path ('sw9', 'surrey/richmond', 'london/clapham'), a full Zoopla URL, or just a place name or postcode ('Leeds', 'SW9 9EF'). Zoopla's own slugs are not guessable — Leeds is 'west-yorkshire/leeds' while Liverpool is plain 'liverpool' — so a plain name is resolved for you and the path used comes back as `area` (with `area_resolved_from` set). Use location_search when a name is ambiguous, e.g. Richmond.
- `kind` (enum, optional, default "all") — Which kind of agent to list. [one of: all, estate, letting]
- `page` (integer, optional, default 1) — 1-based page. Zoopla serves 25 listings a page and stops at page 40 (returned as page_cap); narrow the area or the filters to reach more.
- `max_rotations` (integer, optional, default 4) — How hard to retry when the site refuses.

**Returns:** agents[] (branch_id, name, address, url, logo_url, description, for_sale{listings, average_price, weeks_on_market}, to_rent{...}), count, total_results, page, page_size, page_cap, has_more, area, area_resolved_from

**Example request body:**
```json
{
  "geo": "london"
}
```

## At scale
- **Volume:** 5M+ requests a day, measured at 60 requests a second across the fleet with no
  central bottleneck. Per-key limits are raised for high-volume accounts; volume pricing on request.
- **Missing a source:** tell us a site we do not cover and it becomes an engine. A customer asked
  for bestprice.gr on 21 Sep 2026 and it was in the catalog on 22 Sep.
- **Support:** 2 minute median time from a question in the live chat to the first answer. Setup
  help included, no support tier to buy.
- **One key, one credit pool** across every API. No per-site plans, no separate subscriptions.

## More
- Try it live, no code: https://reefapi.com/playground?engine=zoopla
- Human docs page: https://reefapi.com/docs/zoopla
- Overview page: https://reefapi.com/zoopla-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
