99.co Scraper - Singapore & Indonesia Property Listings
Pricing
from $2.50 / 1,000 listings
99.co Scraper - Singapore & Indonesia Property Listings
Extract 99.co property listings in Singapore and Indonesia: prices, floor areas, photos and available agent contacts. Pay per delivered listing row plus a start fee; platform usage is separate.
Pricing
from $2.50 / 1,000 listings
Rating
0.0
(0)
Developer
kane liu
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Security
Limited permissionsShare
99.co Scraper
Extract property listings from 99.co in Singapore and Indonesia as structured rows you can export and compare.
- Sale and rental listings for homes, rooms and commercial property.
- Asking prices, floor areas, bedrooms, bathrooms and listing links.
- Available agent contacts, agency details, photos and Singapore MRT information.
- Search by market and property type, use Singapore locations, or supply 99.co URLs.
- Pay $2.50 per 1,000 delivered rows plus a small start fee; platform usage is separate under the current configuration.
What does 99.co Scraper do?
99.co Scraper collects listings from www.99.co/singapore and www.99.co/id. It has one search mode, selected automatically from the target fields you fill. Each row represents one property listing with rowType: "listing"; agent information enriches that row rather than creating another billed row.
- Singapore: HDB flats, condos, executive condos, landed homes, room rentals, offices, retail and industrial property.
- Indonesia: houses (
rumah), apartments (apartemen), shop-houses (ruko), land (tanah) and other categories in the input form. - URL input: search pages, individual listings and supported project pages.
- Optional details:
scrapeDetailsis off by default.scrapeAgentProfileis on by default and adds available Singapore agent email and agency information to listing rows.
What data can you extract from 99.co?
The table describes listing rows. Examples are illustrative, not a live sample. Availability varies by market, listing and detail settings.
| Field | Description | Example |
|---|---|---|
id | Listing identifier | example-SG-001 |
url | Link to the listing | https://www.99.co/singapore/sale/property/example-SG-001 |
sourceCountry | Market code | SG |
channel | Sale or rental selection | BUY |
propertyType | Property category returned by the source | condo |
title | Listing headline | Example condominium |
description | Listing description when collected | Two-bedroom apartment |
price | Numeric asking price when available | 1500000 |
priceCurrency | Currency of the asking price | SGD |
pricePeriod | Price period | total |
pricePsf | Price per square foot when shown | 1500 |
bedrooms | Bedroom count or source display label | 2 |
bathrooms | Bathroom count when available | 2 |
floorAreaSqft | Floor area in square feet | 1000 |
floorAreaSqm | Floor area in square metres | 92.9 |
tenure | Ownership tenure when shown | Freehold |
builtYear | Completion year when available | 2015 |
furnishingStatus | Furnishing information when shown | Fully furnished |
mrt | Singapore station, walking time and distance | {"name":"Newton MRT","walkMinutes":8} |
address | Available property address components | {"full":"123 Example Street"} |
geo | Coordinates when available | {"lat":1.30,"lng":103.83} |
person | Agent identity | {"name":"Jane Doe","role":"agent"} |
org | Agency information | {"name":"Example Agency"} |
phones | Available phone numbers and types | [{"value":"+1 555 0100","type":"mobile"}] |
emails | Available contact emails | [{"value":"name@example.com","type":"email"}] |
web | Available website and social links | {"website":null,"socials":[]} |
media.photos | Up to 20 photo URLs | [] |
listingTags | Tags shown on the listing | [] |
leadAbsences | Reasons for missing contact fields | {} |
scrapedAt | Collection timestamp | 2026-10-24T08:00:00+00:00 |
Room counts can retain display strings such as 3+; do not assume every value is an integer. leadAbsences uses source-supported reason codes: 本站无 means unavailable on the site, 闸门锁 means access is restricted, and 未验证 means not verified.
How to scrape 99.co
- Open 99.co Scraper in Apify Console.
- Fill a target such as
market,locationorstartUrls. - Set
maxItemsto a small number such as3. - Click Start and wait for completion.
- Download the dataset or read it through the Apify API.
An empty input starts the form's Singapore condo-sale example with up to 3 rows. This is a billed example, not a free preview. Filling only filters such as channel or price also invokes the example when no target is present. Filling one target selects the search mode automatically.
Search with markets and filters
Use market: "SG" or market: "ID", then select a property type and channel. Non-empty markets and propertyTypes replace their singular counterparts. In Singapore, propertyType: "all" searches residential homes, not every commercial category. In Indonesia, all searches the available categories within the row allowance.
Use query for Singapore text search or location for an area such as bedok. locations accepts several areas and combines with location; a resolved location takes precedence over the text query. These location and text filters do not apply to Indonesia.
Search with URLs
Put 99.co search, listing or project URLs in startUrls. They replace the searches built from market and category selections. Prefer canonical www.99.co URLs. For Singapore price, room and location filtering, use the input fields rather than assuming every search-URL query parameter is preserved.
What to fill in
| Field | What to enter | Default |
|---|---|---|
startUrls | 99.co page URLs, as strings or objects with url | Empty; example URL in form |
market | SG, ID or all | SG when omitted |
markets | Several market codes; replaces market | Empty |
propertyType | A category such as condo, hdb, rooms or rumah | condo when omitted |
propertyTypes | Several categories; replaces propertyType | Empty |
channel | BUY or RENT | BUY |
query | Singapore place, project or keyword | Empty |
location | Singapore area or region, such as bedok or east-region | Empty |
locations | Several Singapore areas; combines with location | Empty |
maxItems | Rows per URL or selected market, from 1 to 5,000 | 70; form example 3 |
rooms | Singapore bedroom count, from 0 to 20 | Unset |
priceMin | Minimum Singapore asking price in SGD | Unset |
priceMax | Maximum Singapore asking price in SGD | Unset |
floorAreaMin | Minimum Singapore floor area in square feet | Unset |
floorAreaMax | Maximum Singapore floor area in square feet | Unset |
tenure | Singapore freehold or 99-year tenure | Unset |
furnishing | Singapore fully, partial or unfurnished | Unset |
sort | Singapore relevance, price,asc, price,desc or floorarea,desc | Unset |
supplyMode | Leave as search; URL type determines collection | search |
scrapeDetails | Add available listing details to each row | false |
scrapeAgentProfile | Add available Singapore agent email and agency | true |
maxPages | Advanced page setting; not a reliable run-wide cap; use maxItems | 1 |
requestDelayMillis | Delay between applicable page loads, in milliseconds | 2000 |
Singapore HDB rental search:
{"market": "SG","channel": "RENT","propertyType": "hdb","location": "bedok","maxItems": 3}
Indonesia apartment search:
{"market": "ID","channel": "BUY","propertyType": "apartemen","maxItems": 3}
URL search:
{"startUrls": [{"url": "https://www.99.co/singapore/sale/condos-apartments"}],"maxItems": 3}
maxItems is the canonical row limit. maxResults and limit are aliases; an alias wins when maxItems is absent or still 70. An explicit non-default maxItems wins instead.
Unknown fields do not fail the run: close misspellings can be corrected, and other unknown fields are ignored with a note. Unsupported filter values can also be ignored. Check the default key-value store's INPUT_REPORT record for corrections, ignored fields and example-run selection.
Input rejected before collection produces a field-specific message such as INVALID_INPUT: startUrls — use 99.co page URLs. No listing rows are charged in that case; the start event still applies. Not every collection error is an input error.
Output example
This illustrative Singapore row uses placeholder identifiers and contact information.
{"rowType": "listing","id": "example-SG-001","url": "https://www.99.co/singapore/sale/property/example-SG-001","source": "99.co","sourceCountry": "SG","channel": "BUY","propertyType": "condo","title": "Example condominium","price": 1500000,"priceCurrency": "SGD","pricePeriod": "total","bedrooms": 2,"bathrooms": 2,"floorAreaSqft": 1000,"phones": [{"value": "+1 555 0100", "type": "mobile"}],"emails": [{"value": "name@example.com", "type": "email"}],"person": {"name": "Jane Doe", "role": "agent"},"org": {"name": "Example Agency", "type": "agency"},"geo": null,"media": {"photos": []},"scrapedAt": "2026-10-24T08:00:00+00:00"}
Export the dataset as JSON, CSV, Excel, XML, RSS or an HTML table. OUTPUT and RUN_SUMMARY provide counts and warnings; ERROR_SUMMARY records failures when available.
Why use this 99.co scraper?
- Row-based charges: listing charges apply to delivered rows, not attempted pages.
- Usable partial output: rows already written to the dataset remain available after a stop or timeout. Stopping before delivery can leave an empty dataset.
- Missing values stay missing: absent contact details or property attributes are not invented.
- Several inputs: combine URLs or markets, with a row limit for each and a spending cap for the run.
- No proxy setup: you do not need to set up proxies.
How much does it cost to scrape 99.co?
The start event costs $0.005 per GB of run memory, with a minimum of one event. At the default 512 MB, one start costs $0.005. Each delivered listing row costs $0.0025.
| Apify plan | Start at 512 MB | Price per row | Price per 1,000 rows |
|---|---|---|---|
| Free | $0.005 | $0.0025 | $2.50 |
| Starter | $0.005 | $0.0025 | $2.50 |
| Scale | $0.005 | $0.0025 | $2.50 |
| Business | $0.005 | $0.0025 | $2.50 |
These are event charges. Platform usage is billed separately under the current Actor pricing configuration.
maxItemsapplies to each start URL, or each selected market without URLs. Property types and locations share that input's allowance.- The default is 70 rows per input; larger allowances are collected in parts of 70.
- Free-plan runs are capped at 200 rows in total and 10 runs of this Actor. These limits are set by the publisher.
- Set Maximum cost per run (
maxTotalChargeUsdin the API) to cap event charges across the whole run. It is not an additional row allowance for each category.
Example: two URLs delivering 3 rows each produce 6 rows. At 512 MB, event charges are 6 × $0.0025 + $0.005 = $0.020, plus platform usage. Fewer delivered rows mean fewer row charges.
What happens when the run timeout is too short
Some 99.co collections, such as several startUrls or markets with scrapeDetails: true or a large maxItems, can take longer than the run timeout you chose. In that case the run does not fail. It ends early with the status message "Queued on the collection backend; this run will resume automatically when results are ready (about N s)." and shows as ABORTED for a while.
When the results are ready, the same run starts again by itself, reports "Resumed: delivering N rows.", writes the rows to the same dataset and finishes as SUCCEEDED. Rows that were already delivered are skipped, so no row is delivered or charged twice. You do not need to start another run.
- This can happen at most 3 times per run. To avoid it, keep the default timeout of 3600 seconds.
- The synchronous API endpoints (
run-sync,run-sync-get-dataset-items) wait at most 300 seconds and can return an empty dataset while the run is paused. For larger jobs, start the run asynchronously and read the dataset after the run reaches SUCCEEDED, or use a webhook on the "succeeded" event.
Use with AI agents and the API
Apify MCP server
Add this Actor to a remote MCP client such as Claude, ChatGPT, Cursor or VS Code:
https://mcp.apify.com?tools=lentic_clockss/ninetynine-co-sg-scraper
Sign in with Apify through OAuth, or send Authorization: Bearer <YOUR_APIFY_TOKEN>. The tool starts a run and returns run and dataset IDs; the agent can read the rows with get-dataset-items.
For clients without remote MCP support, run npx @apify/actors-mcp-server --tools lentic_clockss/ninetynine-co-sg-scraper with APIFY_TOKEN set.
Example prompt: “Collect up to 3 Singapore HDB rental listings in Bedok using market: SG, channel: RENT, propertyType: hdb, location: bedok and maxItems: 3.”
Apify API
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("lentic_clockss/ninetynine-co-sg-scraper").call(run_input={"market": "SG", "propertyType": "hdb", "maxItems": 3})for row in client.dataset(run["defaultDatasetId"]).iterate_items():print(row)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('lentic_clockss/ninetynine-co-sg-scraper').call({ market: 'SG', propertyType: 'hdb', maxItems: 3 });const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
For HTTP, send JSON input to POST https://api.apify.com/v2/acts/lentic_clockss~ninetynine-co-sg-scraper/runs with an Authorization: Bearer <YOUR_APIFY_TOKEN> header. After completion, read GET https://api.apify.com/v2/datasets/<datasetId>/items?format=json.
Integrations
Use the Actor's Integrations tab to connect results to Google Sheets, Make, Zapier, n8n, Slack, Airtable or a webhook. Use Apify Schedules for recurring runs.
FAQ
Is it legal to scrape 99.co?
Public availability is not blanket permission to reuse data. You are responsible for the site's terms and applicable data protection law. Processing personal contact information requires a lawful basis; obtain legal advice for your use case if needed.
Why did my run end as ABORTED?
The run was stopped before normal completion. Check its status message and log for the reason; the status alone does not establish whether any rows were delivered. Inspect the dataset before rerunning. Do not assume it will restart automatically.
How many results can I get?
maxItems accepts 1–5,000 rows per input, subject to available listings, spending limits and the free-plan cap. It is a ceiling, not a promise that the site has that many matching listings.
Why are some fields empty?
Not every listing exposes every field. Details are off by default, public agent profiles may omit email, and Indonesia phone numbers may be masked. Inspect leadAbsences where provided.
How do I scrape room rentals or commercial property?
For Singapore rooms, use channel: "RENT" and propertyType: "rooms". For commercial searches, choose offices, retail, industrial, warehouse, shophouse or commercial. The categories rooms and Indonesia's kios are rental-only.
Why did my free-plan run stop after 10 runs?
The publisher limits free-plan users to 10 runs of this Actor. RUN_SUMMARY reports FREE_TIER_LIMIT when the allowance is exhausted. Paid plans are not subject to that publisher run-count limit.
What if a search returns zero rows?
Check the market, category and URL, then inspect OUTPUT or RUN_SUMMARY for warnings. A confirmed empty search can succeed; a collection failure is not proof that no listings exist.
Do I need a 99.co API key?
No 99.co API key is an input. Use an Apify account for Console runs and an Apify token for API access.
Limitations
- Only 99.co Singapore and Indonesia are covered, not sister brands on other domains.
- Singapore text, location, price, bedroom, area, tenure, furnishing and sort filters are not applied to Indonesia searches.
maxPagesdoes not reliably limit current Actor pagination; usemaxItemsand a spending cap.- Unresolved Singapore locations are skipped with a warning, so review the summary before treating results as location-filtered.
- Not every URL query parameter is retained; contact availability and project inventory are not guaranteed.
- Singapore
warehouseshares the industrial filter;shophouseshares the retail filter. Theroomsselection can include broader residential rental inventory; check listings when you need a narrow category. - Masked Indonesia phone numbers are not revealed. Unit numbers, coordinates, agency names and contact emails can be missing.
- Photo lists are capped at 20 URLs per row. Source display values and missing fields may differ between markets.
Related scrapers
More real estate and property data scrapers from the same publisher:
- Zumper Scraper
- Booli Scraper
- Craigslist Scraper
- PropertyGuru Scraper
- OpenSooq Scraper
- Apartments.com Rental Scraper
- Dot Property Scraper
- FINN.no Scraper
Browse all of our scrapers at apify.com/lentic_clockss.
Support
Report a problem or request a field through the Issues tab of this Actor's page. Include the run ID and the input you used.