# 99.co Scraper – Singapore Property for Sale & Rent (`claude_code_reviewer/99co-scraper-en`) Actor

Scrape 99.co (Singapore property) listings for sale and rent without an API: price, floor area, bedrooms, bathrooms, district, region and property type. Bulk input; export JSON/CSV/Excel. Great for property market research and price comparison.

- **URL**: https://apify.com/claude\_code\_reviewer/99co-scraper-en.md
- **Developed by:** [Chad](https://apify.com/claude_code_reviewer) (community)
- **Categories:** Agents, E-commerce, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 property results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 99.co Scraper | Singapore Property Search, HDB, Condo & Landed Property Listings for Sale & Rent (No API, No Login)

One actor to scrape 99.co Singapore property search: **keyword search** (scrape HDB, condo, and landed property listings for sale or rent, filtered by your criteria). No login required, no API token, and bulk-pastes of multiple search queries are handled natively. It hits 99.co's public JSON endpoints directly — no browser, fast and cheap.

Built for: people tracking Singapore property prices, market analysts covering specific districts, anyone comparing rental rates across neighborhoods, property market researchers, students needing a dataset for a thesis, and engineers building their own side projects.

**Planned for v1.1**: listing detail mode (fetch full details of a single listing by ID/URL) — not yet available in this version.

***

### What you get

Every listing includes the fields below (missing values are always returned as `null` — fields are never dropped):

| Field               | Description                            | Example                                                        |
| ------------------- | --------------------------------------- | --------------------------------------------------------------- |
| `listing_id`        | 99.co listing ID                        | `PkhHsqk7hrY7DPXGtPVr6L`                                       |
| `title`             | Listing name (project name or address)  | `Commonwealth HDB Estate`                                       |
| `listing_type`      | Transaction type                        | `sale`, `rent`                                                  |
| `main_category`     | Property type                           | `hdb`, `condo`, `landed`                                        |
| `property_segment`  | Property sub-category                   | `hdb`, `non-landed`, `landed`                                   |
| `price`             | Sale price or monthly rent (SGD)        | `850000`                                                        |
| `currency`          | Currency (always SGD)                   | `SGD`                                                           |
| `price_per_sqft`    | Price per square foot (SGD)             | `589.24`                                                        |
| `bedrooms`          | Number of bedrooms                      | `5`                                                              |
| `bathrooms`         | Number of bathrooms                     | `2`                                                              |
| `floor_area_sqft`   | Floor area (square feet)                | `1442`                                                           |
| `address`           | Address                                 | `123 Commonwealth Avenue`                                        |
| `district`          | District number (D1–D28)                | `3`                                                              |
| `region`            | Region name                             | `Central Region`                                                 |
| `latitude`          | Latitude                                | `1.2985`                                                         |
| `longitude`         | Longitude                               | `103.8027`                                                       |
| `tenure`            | Tenure type                             | `99-year leasehold`, `freehold`                                  |
| `agent_name`        | Agent name                              | `Tan Wei Ming`                                                   |
| `agent_id`          | Agent ID                                | `user_abc123`                                                    |
| `agency`            | Agency name                             | `PropNex Realty Pte Ltd`                                         |
| `posted_at`         | Listing posted time (ISO 8601)          | `2026-06-15T10:30:00.000Z`                                       |
| `image_urls`        | Array of listing image URLs             | `["https://cdn.99.co/photos/abc123_main.jpg"]`                   |
| `listing_url`       | Full listing URL                        | `https://www.99.co/singapore/sale/map/hdb/...`                   |
| `source_mode`       | Which mode this record came from        | `search`                                                         |
| `source_query`      | The query used to fetch this record     | `hdb queenstown sale`                                            |
| `scraped_at`        | Time of scraping (ISO 8601)             | `2026-07-04T05:23:28.617Z`                                       |

***

### Modes

| Mode          | What it does                                    | Billing event             | Price  | Status |
| ------------- | ------------------------------------------------ | -------------------------- | ------ | ------ |
| 🔎 **search** | Keyword search, scrape a listing list by criteria | `search-listing` (per item) | $0.002 | v1.0   |
| 📦 **detail** | Fetch full details of a single listing by ID/URL  | `product-detail` (per item) | $0.008 | v1.1   |

Regardless of which mode you run, every run start incurs a one-time `apify-actor-start` fee of **$0.005**. All charges are only applied **after** data has been successfully written to the dataset — the run log prints `total_items / charged / skipped` at the end so you can reconcile.

***

### Input fields

| Field                  | Type     |      Required      |  Default   | Description                                                                                                                                        |
| ---------------------- | -------- | :-----------------: | :--------: | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`                  | enum     |     recommended     |  `search`  | Only `search` (search listing) is available currently; `detail` is planned for v1.1.                                                                    |
| `queries`               | string\[] | required for `search` |     —      | Search queries, one per line, e.g. `hdb queenstown sale`, `condo orchard rent`. Up to **50** per run.                                                    |
| `listingType`           | enum     |       optional       |   `sale`   | `sale` or `rent`.                                                                                                                                        |
| `mainCategory`          | enum     |       optional       |     —      | `hdb` / `condo` / `landed`. Leave empty to scrape all types.                                                                                              |
| `bedrooms`              | integer  |       optional       |     —      | Specify number of bedrooms (1+). Leave empty for no limit.                                                                                                |
| `priceMin`              | integer  |       optional       |     —      | Only keep listings priced (sale/rent) at or above this amount (SGD). Leave empty for no limit.                                                            |
| `priceMax`              | integer  |       optional       |     —      | Only keep listings priced (sale/rent) at or below this amount (SGD). Leave empty for no limit.                                                            |
| `bulkInput`             | string   |       optional       |     —      | **One entry per line**, press Enter to add a new line, no quotes or commas. Used as search queries in search mode — merged automatically into `queries` at run time. Fastest way is to paste a whole column copied from Google Sheets / Excel. |
| `maxItems`              | integer  |       optional       |    `50`    | Maximum number of listings to scrape per search query (1-500). Higher values cost more.                                                                   |
| `proxyConfiguration`    | object   |       optional       |     —      | 99.co restricts non-Singapore IPs. We recommend enabling Apify Proxy (Residential or RESIDENTIAL\_SG) to avoid 429 blocks.                                 |

***

### Examples

**🔎 Basic search: Queenstown HDB**

```json
{
    "mode": "search",
    "queries": ["hdb queenstown sale"],
    "maxItems": 20
}
```

**🏠 Price range + bedroom count**

```json
{
    "mode": "search",
    "queries": ["condo orchard rent"],
    "listingType": "rent",
    "mainCategory": "condo",
    "bedrooms": 2,
    "priceMin": 3000,
    "priceMax": 8000,
    "maxItems": 50
}
```

**📋 Bulk search across multiple neighborhoods**

```json
{
    "mode": "search",
    "bulkInput": "hdb toa payoh sale\nhdb bishan sale\nhdb ang mo kio sale",
    "maxItems": 100
}
```

***

### Notes

- **Public data only**. No login required, no 99.co account needed, and no content requiring authentication is accessed.
- **No login / no API token needed**. Fill in the input and run — there are no credentials to configure.
- **The same listing hit by multiple queries produces one row per `source_query` (billed per query)**. For example, if both `hdb queenstown sale` and `hdb central sale` match the same listing, it will appear as two rows, each billed as a separate `search-listing`.
- **Cross-page deduplication**: within the same search query, if the same listing appears on multiple pages, it's only counted once (built-in deduplication).
- **Polite to the site**. Concurrency per domain is capped at 5, failed requests are retried up to 3 times with exponential backoff — the actor won't hammer 99.co's servers.
- **Listing detail mode (detail mode) is planned for v1.1**. Only search mode is supported currently.

***

### FAQ

**Q: How do I write search queries?**
One per line, formatted as closely as possible to what a user would type into 99.co's search box, e.g. `hdb queenstown sale`, `condo river valley rent`, `landed serangoon`. You can omit sale/rent (use the `listingType` field to control it instead), or include it — the scraper passes it through as a query tag either way.

**Q: Why is `price_per_sqft` null?**
Some 99.co listings don't list a price-per-square-foot figure, or the API doesn't return an `area_ppsf` field for them. In that case we always return `null` rather than estimating a value.

**Q: How is billing calculated?**
Each run start incurs a one-time `apify-actor-start` charge ($0.005); `search` mode charges `search-listing` ($0.002) per listing returned. Both are only charged after data is successfully written to the dataset — the run log prints the actual number of billed items.

**Q: What if I don't get any data?**
First check your search query spelling, or try a more common neighborhood name or property type. The scraper stops itself and logs the reason after 5 consecutive requests return no new data, so it won't burn compute for nothing. If you're hitting 429 blocks, we recommend enabling the proxy setting.

**Q: When will detail mode (full listing details) be available?**
Planned for v1.1. At that point you'll be able to bulk-fetch full listing details by listing ID or 99.co listing URL.

***

### Deployment

```bash
apify login              # first time only: paste your Apify token (never committed to the repo)
apify push               # build and upload to Apify Store
```

After publishing, configure PPE event prices in the Apify Console under Monetization:
`apify-actor-start` $0.005, `search-listing` $0.002.

***

### Disclaimer

This scraper only collects **publicly visible** property listing data from 99.co. Data is intended for research and personal use only, not for any unauthorized commercial resale. Please confirm compliance with 99.co's terms of service and applicable local regulations before use. This actor makes no guarantees regarding the accuracy, timeliness, or completeness of the data — refer to 99.co's official site for current property prices and listing status.

***

*99.co scraper, Singapore property, SG real estate, property for sale Singapore, property for rent SG, HDB condo listings, Singapore housing data*

# Actor input Schema

## `mode` (type: `string`):

What do you want to scrape? Pick a mode, then fill in the fields below it.

## `queries` (type: `array`):

Query tags for search mode, e.g. hdb queenstown sale, condo orchard rent. One query per line.

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

Sale or rent.

## `mainCategory` (type: `string`):

Choose a property type. Leave empty to scrape all types.

## `bedrooms` (type: `integer`):

Specify number of bedrooms (1-5+). Leave empty for no limit.

## `priceMin` (type: `integer`):

Only keep listings priced (sale/rent) at or above this amount. Leave empty for no limit.

## `priceMax` (type: `integer`):

Only keep listings priced (sale/rent) at or below this amount. Leave empty for no limit.

## `listingIds` (type: `array`):

99.co listing IDs, e.g. PkhHsqk7hrY7DPXGtPVr6L (the last segment of the listing URL). Used by detail mode.

## `listingUrls` (type: `array`):

Full 99.co listing URLs, e.g. https://www.99.co/singapore/sale/map/hdb/foo-PkhHsqk7hrY7DPXGtPVr6L. The ID is extracted automatically.

## `bulkInput` (type: `string`):

**One entry per line**, press Enter to add a new line, no quotes or commas. Used as search queries in search mode, or listing IDs/URLs in detail mode — merged automatically into the corresponding field above at run time. Fastest way is to paste a whole column copied from Google Sheets / Excel.

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

Maximum number of listings to scrape per search query (search mode). Higher values cost more. Not applied in detail mode (limited by number of IDs).

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

99.co restricts non-Singapore IPs. We recommend enabling Apify Proxy (Residential or RESIDENTIAL\_SG) to avoid 429 blocks.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "hdb queenstown sale"
  ],
  "listingType": "sale",
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {
    "mode": "search",
    "queries": [
        "hdb queenstown sale"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("claude_code_reviewer/99co-scraper-en").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 = {
    "mode": "search",
    "queries": ["hdb queenstown sale"],
}

# Run the Actor and wait for it to finish
run = client.actor("claude_code_reviewer/99co-scraper-en").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 '{
  "mode": "search",
  "queries": [
    "hdb queenstown sale"
  ]
}' |
apify call claude_code_reviewer/99co-scraper-en --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,claude_code_reviewer/99co-scraper-en"
        }
    }
}

```

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/SyMcHZwCoxscprjeS/builds/AElI86C2HIV8L3SIc/openapi.json
