# URA Singapore Property Scraper — Prices, Transactions & Rentals (`rl1987/ura-property-data`) Actor

Scrape Singapore private property data from URA's own PMI service: unit-level sale transactions with floor range, area and price PSF, quarterly median and 25th/75th percentile rents, and project summaries with market segment and tenure. By project, street, district 01-28 or island-wide.

- **URL**: https://apify.com/rl1987/ura-property-data.md
- **Developed by:** [R.L.](https://apify.com/rl1987) (community)
- **Categories:** Real estate
- **Features:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 output rows

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## URA Singapore Property Scraper — Prices, Transactions & Rentals

Pull live Singapore private residential market data straight from the same backend that powers URA's official [PMI Mobile app](https://www.ura.gov.sg/property-market-information) — project price and rental summaries, unit-level sale transaction history, and quarterly rental statistics — for any project in Singapore, any street, an entire URA postal district, or the whole island at once. Run it manually, on a schedule, or from the Apify API to keep a property-market dataset continuously up to date. No URA account or access key of any kind is needed.

### What can you use Singapore property data for?

- **Market research & investment analysis** — track price/rental trends for specific projects or districts over time.
- **Portfolio monitoring** — watch transaction and rental activity for buildings you own or manage.
- **Real estate proptech & data products** — feed structured Singapore property data into dashboards, models, or apps without maintaining your own scraper.
- **Comparative analysis** — pull an entire district's worth of projects in one run to compare price/rental performance.

### How do I look up a project, street or district?

1. Click **Try for free** or set up input in the **Input** tab.
2. Pick a lookup mode -- `projectNames`, `streetNames`, and `districts` are all searchable dropdowns pre-populated from URA's own project index, so there's no free-text typing or guessing exact names:
   - **Specific projects** — pick one or more from the `projectNames` dropdown (e.g. "THE SAIL @ MARINA BAY").
   - **Specific streets** — pick one or more from the `streetNames` dropdown.
   - **A district** — pick one or more from the `districts` dropdown to fetch every known project in those districts.
   - **Free-text search** — type into `searchQuery` to reproduce the app's own search box (case-insensitive substring match against project and street names, e.g. "sail" finds "THE SAIL @ MARINA BAY", "RIVERSAILS", "VERSAILLES"). Combine with `districts` to scope it.
   - **The whole index** — set `crawlAll` to `true` to fetch all ~3,200 projects island-wide (use `maxProjects` to cap this for a first test run).
3. Choose which data types to include: project summary, transaction history, rental history.
4. Run the Actor and download results from the **Dataset** tab, or read them via the Apify API/CLI.

Modes combine: project names, street names, a `searchQuery` and districts in one run are all resolved into a single, de-duplicated target list.

### What input does it take?

| Field | Type | Default | Description |
|---|---|---|---|
| `projectNames` | array of strings | — | Exact/near-exact project names to fetch directly |
| `streetNames` | array of strings | — | Street names to fetch directly (best-effort) |
| `searchQuery` | string | — | Free-text substring search over project/street names, same as the app's search box |
| `districts` | array of strings | — | URA district codes (`01`–`28`) to browse via the bundled project index |
| `crawlAll` | boolean | `false` | Fetch every project in the bundled index |
| `maxProjects` | integer | `0` | Cap on resolved projects/streets actually fetched (0 = no cap) |
| `includeProjectSummary` | boolean | `true` | Fetch project-level summary (project-name lookups only) |
| `includeTransactions` | boolean | `true` | Fetch sale transaction history |
| `includeRentals` | boolean | `true` | Fetch rental history |
| `transactionMonths` | integer | `240` | Months of sale history to request (URA caps actual results at the project's available history) |
| `rentalQuarters` | integer | `60` | Quarters of rental history to request |
| `concurrency` | integer | `5` | Parallel project fetches |
| `requestDelayMs` | integer | `300` | Pause between calls within one project's fetch |

Example input:

```json
{
  "districts": ["09", "10"],
  "includeProjectSummary": true,
  "includeTransactions": true,
  "includeRentals": true,
  "maxProjects": 100
}
```

### What does each row contain?

Each dataset item is one record — a project summary, a single sale transaction, or a single quarter of rental stats — tagged with `dataType` and the project/street/district it belongs to.

```json
{
  "dataType": "transaction",
  "projectName": "THE SAIL @ MARINA BAY",
  "streetName": null,
  "districtCode": "01",
  "districtName": "Raffles Place, Cecil, Marina, People's Park",
  "floorRange": "01 to 05",
  "areaSqft": 883.0,
  "areaSqm": 82.05,
  "price": 1608000.0,
  "refPeriod": "JUL 2026",
  "unitPricePsf": 1822.0,
  "unitPricePsm": 19611.87
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Data fields

URA reports area and price rates in imperial units (sqft, PSF) only -- this Actor derives the metric equivalents (sqm, PSM) itself using 1 sqm = 10.7639 sqft, so every row carries both.

| Field | Applies to | Description |
|---|---|---|
| `dataType` | all | `projectSummary`, `transaction`, or `rental`. |
| `projectName`, `streetName`, `districtCode`, `districtName` | all | Project/location context |
| `lastTransactedRefPeriod`, `lastTransactedValuePsf`, `lastTransactedValuePsm` | projectSummary | Most recent transacted price rate and period |
| `lastRentalRefPeriod`, `lastRentalValuePsf`, `lastRentalValuePsm` | projectSummary | Most recent rental rate and period |
| `lastMedianUnitRentalPeriod`, `lastMedianUnitRentalValuePsf`, `lastMedianUnitRentalValuePsm` | projectSummary | Most recent median unit rental rate and period |
| `marketSegment`, `tenure`, `propertyType` | projectSummary | Segment (CCR/RCR/OCR), tenure, property type code |
| `floorRange`, `areaSqft`, `areaSqm`, `price`, `unitPricePsf`, `unitPricePsm`, `refPeriod` | transaction | Per-unit sale record |
| `medianPsfPerMonth`, `medianPsmPerMonth`, `p25PsfPerMonth`, `p25PsmPerMonth`, `p75PsfPerMonth`, `p75PsmPerMonth`, `refPeriod` | rental | Quarterly rental price distribution |

### Which Singapore districts can you scrape?

`districts` takes URA's postal district codes. These are the 28 districts, the areas each covers,
and how many distinct projects the bundled index resolves for each (the figures below come from the
index itself, so they tell you how big a district run will be before you start it):

| District | Areas it covers | Projects in the bundled index |
|---|---|---|
| `01` | Raffles Place, Cecil, Marina, People's Park | 21 |
| `02` | Anson, Tanjong Pagar | 29 |
| `03` | Queenstown, Tiong Bahru | 33 |
| `04` | Telok Blangah, Harbourfront | 39 |
| `05` | Pasir Panjang, Hong Leong Garden, Clementi New Town | 131 |
| `06` | High Street, Beach Road (City) | 3 |
| `07` | Middle Road, Golden Mile | 34 |
| `08` | Little India | 81 |
| `09` | Orchard, Cairnhill, River Valley | 257 |
| `10` | Ardmore, Bukit Timah, Holland Road, Tanglin | 428 |
| `11` | Watten Estate, Novena, Thomson | 230 |
| `12` | Balestier, Toa Payoh, Serangoon | 128 |
| `13` | Macpherson, Braddell | 82 |
| `14` | Geylang, Eunos | 231 |
| `15` | Katong, Joo Chiat, Amber Road | 540 |
| `16` | Bedok, Upper East Coast, Eastwood, Kew Drive | 139 |
| `17` | Loyang, Changi | 57 |
| `18` | Tampines, Pasir Ris | 46 |
| `19` | Serangoon Garden, Hougang, Punggol | 316 |
| `20` | Bishan, Ang Mo Kio | 91 |
| `21` | Upper Bukit Timah, Clementi Park, Ulu Pandan | 115 |
| `22` | Jurong | 27 |
| `23` | Hillview, Dairy Farm, Bukit Panjang, Choa Chu Kang | 93 |
| `24` | Lim Chu Kang, Tengah | — (none) |
| `25` | Kranji, Woodgrove, Woodlands | 23 |
| `26` | Upper Thomson, Springleaf | 36 |
| `27` | Yishun, Sembawang | 41 |
| `28` | Seletar, Yio Chu Kang | 67 |

District `24` (Lim Chu Kang, Tengah) holds no private residential projects in the index, so
selecting it resolves nothing. Counts are of *indexed* projects, not of projects that still have
live data — a project with no recent activity simply returns fewer rows.

### What do the URA terms mean?

- **PSF and PSM** — price per square foot and per square metre. URA publishes rates per square foot only; the Actor converts each one at 1 sqm = 10.7639 sqft and gives you both, so `unitPricePsm` is always `unitPricePsf × 10.7639`.
- **Market segment (CCR / RCR / OCR)** — URA's three-way split of the island. **CCR** is the Core Central Region (the prime central districts), **RCR** the Rest of Central Region (the city fringe), and **OCR** Outside Central Region (the suburbs). The `marketSegment` field carries URA's own code for the project.
- **Tenure** — how long the land is held for, as URA states it: freehold, or a lease of a fixed number of years (99-year leases are the common case in Singapore) with its commencement. It arrives as text, not a number of years.
- **Floor range** — a sale transaction is reported for a band of floors (`"01 to 05"`), never a unit number. URA aggregates it this way deliberately, so unit-level identification is not possible from this data.
- **Reference period** (`refPeriod`) — the period a row belongs to: a month like `JUL 2026` for sale transactions, and a quarter for rental statistics. It is the period URA filed the record under, not the date you ran the scraper.
- **Median, 25th and 75th percentile rent** — a rental row is a *distribution* for one quarter, not one lease: the median is the middle rent per square foot per month, and `p25`/`p75` are the rents a quarter of leases fell below and above. The gap between them tells you how spread the project's rents were.
- **Project summary vs. transaction vs. rental** — a `projectSummary` row is one per project, holding its latest headline rates, segment and tenure. A `transaction` row is one sale. A `rental` row is one quarter of rental statistics. They share the location fields and are told apart by `dataType`.

### How much does it cost to scrape URA property data?

The current price of every event is on the **Pricing** tab of this Actor's page in Apify Store. That tab is generated from the Actor's own pricing record, so it is always current, and your plan's rung is applied automatically. A figure repeated here would be a second copy that drifts out of date.

You can cap what any single run may spend with **Max total charge**.

### How do I keep a run fast and polite?

- Start with a `maxProjects` cap when exploring `districts` or `crawlAll` mode.
- Project names must match URA's own naming closely — check `data.gov.sg`'s URA Property Transaction dataset or the PMI app if a lookup returns nothing.
- Keep `concurrency` modest (default 5) — this is a public government service, not a bulk API.

### FAQ

**Do I need a URA account, login, or API key?** No. The Actor calls the public, unauthenticated web service that URA's own PMI Mobile app uses, with read-only queries. It does not use URA's data.gov.sg data service either, so there is no access key to obtain and nothing to configure.

**What does one row contain?** One of three things, marked by `dataType`: a `projectSummary` (one per project, with its latest transacted and rental rates, market segment and tenure), a `transaction` (one sale: floor range, area, price, PSF), or a `rental` (one quarter's median and 25th/75th percentile rent). Every row also carries the project, street, district code and district name.

**How far back does the history go?** As far as URA keeps for that project. The Actor asks for 240 months of sale transactions and 60 quarters of rentals by default (raise or lower `transactionMonths` / `rentalQuarters`), and URA returns whatever of that it actually holds — an older project yields more rows than a recent launch.

**Can I schedule it?** Yes, with Apify **Schedules**. A weekly or monthly run over the districts you care about keeps a market time-series growing; the summary and the latest `refPeriod` rows are what change between runs.

**Is the project list up to date?** The live market data is, but the browsable index is not. The bundled project/street/district index is a static snapshot of the PMI app's own offline database, dated 2014, and it is used only to decide *which* projects exist for `districts` / `crawlAll` / `searchQuery` browsing. Projects launched after that are missing from it — look those up by name through `projectNames`, which goes straight to URA and is not limited by the index.

**Why did my street lookup return nothing?** URA's street search is stricter than its project search, so some street names legitimately return no rows — try the project name instead. Note also that `includeProjectSummary` only applies to project-name lookups; a street-only target returns transactions and rentals but no summary row.

**Does it cover HDB flats?** No. This is URA's private residential market data — condominiums, apartments and landed private housing. HDB resale transactions are not part of the PMI service this Actor reads.

**Does the data identify units, buyers or sellers?** No. Sales are reported by floor band (`"01 to 05"`) with area and price; there are no unit numbers, addresses beyond the street, or any personal data of buyers or sellers.

**How is the cost bounded?** Rows are charged individually, `maxProjects` caps how many targets are fetched, and **Max total charge** stops the run pushing further rows once your limit is hit. Turning off `includeTransactions` is the single biggest saving, since transaction history is what produces most rows.

### Disclaimers and support

This Actor calls a public, unauthenticated web service used by URA's own PMI Mobile app to serve property market data to the public. It performs read-only queries and does not require credentials. Data accuracy and availability depend entirely on URA; this Actor does not modify, cache-poison, or resell URA data beyond what the source service already returns.

Found a bug or have a feature request? Use the Issues tab on this Actor's page.

### Singapore real estate data toolkit

Part of the **Singapore real estate data toolkit** — Singapore property data across the island's major listing portals and the government's own transaction records:

- [PropertyGuru Singapore API Scraper](https://apify.com/rl1987/propertyguru-sg-api-scraper) — Extract structured property listing data from PropertyGuru Singapore — price, address, district, beds/baths, agent contact.
- [99.co Singapore API Scraper](https://apify.com/rl1987/99co-sg-api-scraper) — Extract structured property listing data from 99.co Singapore by location, district, or property type.
- [EdgeProp Singapore API Scraper](https://apify.com/rl1987/edgeprop-sg-api-scraper) — Extract structured property listing data from EdgeProp Singapore by location, district, or property type.
- [OhMyHome Singapore API Scraper](https://apify.com/rl1987/ohmyhome-sg-api-scraper) — Extract structured property listing data from OhMyHome Singapore by listing/property type, price, bedrooms.

### Did you find this useful?

Rate this actor on Apify! Your feedback helps other users find it and helps us keep improving it.

# Actor input Schema

## `projectNames` (type: `array`):

Pick specific private residential projects to fetch directly. List is sourced from URA's own project index.

## `streetNames` (type: `array`):

Pick specific streets to fetch transactions/rentals for (best-effort; URA's street search is stricter than project search and may return no rows for some streets).

## `searchQuery` (type: `string`):

Free-text search, same as the search box in the URA PMI app -- case-insensitive substring match against project names and street names in the bundled index (e.g. "sail" finds "THE SAIL @ MARINA BAY"). Combine with districts to scope the search to specific districts.

## `districts` (type: `array`):

Pick one or more URA postal districts. Every project in these districts (per the bundled project index) is resolved and fetched. Combine with maxProjects to cap run size.

## `crawlAll` (type: `boolean`):

Fetch every project in the bundled index (~3,200 projects island-wide) instead of a specific list/district. This is a long, heavy run -- use maxProjects to cap it, and prefer scoping by district when possible.

## `maxProjects` (type: `integer`):

Safety cap on the number of resolved projects/streets to actually fetch (0 = no cap). Recommended when using districts or crawlAll.

## `includeProjectSummary` (type: `boolean`):

Fetch project-level summary: last transacted price, last median rental, market segment, tenure. Only works with project name lookups (not street name).

## `includeTransactions` (type: `boolean`):

Fetch unit-level sale transaction history (floor range, area, price, PSF, period).

## `includeRentals` (type: `boolean`):

Fetch quarterly rental price statistics (median, 25th/75th percentile PSF/month).

## `transactionMonths` (type: `integer`):

How many months of sale transactions to request, counting back from the current month. URA appears to cap actual results at the project's full available history regardless of this value.

## `rentalQuarters` (type: `integer`):

How many quarters of rental statistics to request, counting back from the current quarter.

## `concurrency` (type: `integer`):

How many projects to fetch in parallel. Keep modest -- this is a public government service, not built for bulk traffic.

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

Pause between each SOAP call within a single project's fetch, to stay polite to URA's servers.

## Actor input object example

```json
{
  "projectNames": [],
  "streetNames": [],
  "searchQuery": "",
  "districts": [],
  "crawlAll": false,
  "maxProjects": 0,
  "includeProjectSummary": true,
  "includeTransactions": true,
  "includeRentals": true,
  "transactionMonths": 240,
  "rentalQuarters": 60,
  "concurrency": 5,
  "requestDelayMs": 300
}
```

# Actor output Schema

## `results` (type: `string`):

Project summary, sale transaction, and rental history rows.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("rl1987/ura-property-data").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("rl1987/ura-property-data").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 '{}' |
apify call rl1987/ura-property-data --silent --output-dataset

```

## MCP server setup

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

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/3TXQheGULgJjFNWvB/builds/SLZIK0Hci0ZUXFd0U/openapi.json
