99.co Singapore Scraper avatar

99.co Singapore Scraper

Under maintenance

Pricing

from $1.20 / 1,000 dataset items

Go to Apify Store
99.co Singapore Scraper

99.co Singapore Scraper

Under maintenance

Scrape Singapore property listings from 99.co for sale and rent. Prefer Start URLs copied from the site, or search by location with the same filters as the website (condo/HDB/landed, price, size, tenure). Optionally enrich with listing details and agent profiles.

Pricing

from $1.20 / 1,000 dataset items

Rating

0.0

(0)

Developer

Farhan Ali

Farhan Ali

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

99.co Singapore Scraper creates a structured dataset of property listings collected from 99.co in Singapore. Each dataset item is one sale or rent listing and can include asking price in Singapore dollars, price per square foot, floor area, bedrooms, tenure, neighbourhood, and the listing agent. Query with a 99.co search, listing, or agent URL, or a location such as Orchard, and set listing type, segment, price, size, and bedroom filters. Control the result limit with maxItems. Retrieve records through the Apify Dataset API or export them as JSON, CSV, Excel, XML, or another supported format.

99.co Singapore Scraper — input to structured data

Dataset at a glance

PropertyValue
Source99.co (www.99.co), Singapore
Record unitOne property listing
Input methodsStart URL (search, listing, or agent) or a location search query
Main identifiersid, url
DeliveryApify Dataset and API
Export formatsJSON, CSV, Excel, XML, and other Apify dataset exports
Update modelFresh records per Actor run
PricingPay per event: dataset-item $0.001; optional listing-details and agent-info $0.001 each

Coverage and available records

The Actor collects public 99.co sale and rent listings. A start URL is used as copied from the browser.

  • A search URL is paginated as that search. Filters in the input apply to search queries, not to a pasted search URL.
  • A listing URL returns that listing.
  • An agent URL collects listings from that agent profile.
  • searchQueries accepts locations or keywords such as Orchard, Tampines, District 9, or Punggol MRT. With no start URL and no query, the Actor runs one search for the selected listing type and filters without a location string.
  • listingType is sale or rent. It is ignored when the start URL already contains /sale or /rent.
  • propertySegments accepts residential, commercial, industrial, or land. The stored sample is residential.
  • Property group and subtype filters accept the condo, HDB, and landed values in the input schema. Landed subtypes are sent as a landed subtype; other subtypes are sent as a subcategory.
  • Price bounds are Singapore dollars. For sale they are the total asking price. For rent they are monthly rent.
  • Bedroom 0 means studio and bedroom 5 means 5 or more. Bathroom 5 means 5 or more.
  • Floor size is in square feet. Tenure accepts Freehold, 99 yrs, 999 yrs, and 103 yrs. Furnishing accepts unfurnished, partial, and fully.
  • rentalType applies to rent searches: entire unit or room.
  • newLaunch limits the search to new-launch projects.
  • Sort options are relevance, newest, price, price per square foot, and size.
  • maxItems of 0 does not cap the result count. A positive value stops after that many saved listings.
  • scrapeListingDetails opens the listing page and, when parsing succeeds, merges description, photos, coordinates, and unit fields onto the same record. That adds a listing-details charge.
  • scrapeAgentInfo opens the agent profile and, when parsing succeeds, adds an agent object plus agency fields. That adds an agent-info charge.
  • The run requires a proxy configuration. The input default is Apify residential with country SG. Without a proxy, the Actor stops before scraping.

Search-card records omit keys whose values are empty. Detail and agent fields below are conditional. They were not present on the five stored search-card records. A blocked listing page is skipped for enrichment; the card record can still be saved. The Actor does not keep a historical archive.

Data dictionary

Fields below are the keys observed on stored search-card records. Types and the example values come from those records. A key is nullable when at least one stored record omitted it or set it to null.

FieldTypeNullableDescriptionExample
idstringNo99.co listing id, also the last segment of the listing URL.npntbSGVfMgek7jRqgc2yd
urlstringNoAbsolute listing URL on www.99.co.https://www.99.co/singapore/sale/property/eight-riversuites-condo-npn...
titlestringNoProject or address title shown for the listing.Eight Riversuites
listingTypestringNoSource listing type. Observed value: sale.sale
listingTypeTextstringNoLabel derived by the Actor. sale becomes Buy; rent becomes Rent.Buy
pricenumberNoAsking price as a number. Sale prices are the total; rent prices are monthly.1299999.0
priceTextstringNoFormatted asking price from the source, including the S$ symbol.S$ 1,299,999
currencystringNoCurrency code set by the Actor. Observed value: SGD.SGD
psfnumberNoPrice per square foot as a number.1857.0
psfTextstringNoFormatted price per square foot from the source.S$ 1,857 psf
bedroomsintegerNoBedroom count. Studio listings use 0 when the source sends it.2
bathroomsintegerNoBathroom count.2
floorAreaSqftnumberNoInternal floor area in square feet.700.0
floorAreaTextstringNoFormatted floor area from the source, such as 517 sqft.700 sqft
propertyTypestringNoDisplay property type, such as Condo or HDB 5 Rooms.Condo
propertyTypeCodestringNoSource subtype code, such as generic_condo.generic_condo
propertyTypeGroupstringNoSource type group, such as condo.condo
propertyNamestringNoProject or building name.Eight Riversuites
addressstringNoStreet address line from the card, including the postal code when the source includes it.8 Whampoa East 338520
neighbourhoodstringNoNeighbourhood name. Omitted on some cards.Bendemeer
regionstringNoPlanning region, such as North Region. Omitted on some cards.Central Region
tenurestringNoTenure text, such as 99 yrs.99 yrs
builtYearintegerNoYear the project was built, when the card includes it.2016
builtYearTextstringNoFormatted built-year text from the source.Built: 2016
latitudenumberNoListing latitude in decimal degrees.1.3216293792249243
longitudenumberNoListing longitude in decimal degrees.103.86445663693168
photoCountintegerNoPhoto count from the card.8
floorPlanCountintegerNoFloor-plan count from the card.1
videoCountintegerNoVideo count from the card.0
listedOnTextstringNoRelative listed-time text from the source, such as 1s.1s
postedAtintegerNoSource posted-time integer, copied as returned.1786701960252279
isMustSeebooleanNoTrue when the card is marked Must See.true
mrtstringNoNearest MRT label from the card. Omitted when absent.Boon Keng MRT
statusCodestringNoSource status code, such as must_see.must_see
propertySegmentstringNoSource segment. Observed value: residential.residential
sourceUrlstringNoStart URL or search URL that produced the record.https://www.99.co/singapore/sale
scrapedAtstringNoUTC timestamp written by the Actor when the record is saved, ISO-8601.2026-08-14T10:07:15.948796+00:00
thumbnailUrlstringYesCard image URL. Omitted when the card has no photo.https://pic2.99.co/v3/VeBpwpwaaUryeAcLzouyTf?sampling=lanczos&version...
agentIdstringYesAgent id from the card. Omitted when the card has no agent id.ovaEwyAwFz6S4ypZYdXJW6
agentNamestringYesAgent name from the card.Thiru P
agentPhonestringYesAgent phone from the card.+6591847685
agentWhatsappstringYesWhatsApp number from the card.+6591847685
agentIsPremiumbooleanYesTrue when the card marks the agent as premium.true

Listing-detail fields

These keys are produced by the listing-page parser when scrapeListingDetails is true and the page parses. Empty values are omitted. They are not in the stored search-card sample, so the example column is not filled from that sample.

FieldTypeNullableDescriptionExample
headlinestringYesListing subtitle from the detail page.Not in the stored sample
descriptionstringYesMeta description text from the listing page.Not in the stored sample
floorAreaSqmnumberYesFloor area in square metres when the card payload includes it.Not in the stored sample
landAreaSqftnumberYesLand area in square feet.Not in the stored sample
streetNamestringYesStreet name.Not in the stored sample
streetNumberstringYesStreet number.Not in the stored sample
postalCodestringYesPostal code.Not in the stored sample
districtNumberintegerYesDistrict number.Not in the stored sample
furnishingstringYesFurnishing text from the listing payload.Not in the stored sample
imageUrlsarrayYesPhoto URLs. Detail pages keep up to 30 pic2.99.co images.Not in the stored sample
availableFromstringYesAvailability text from the listing.Not in the stored sample
unitConfigurationstringYesUnit configuration text.Not in the stored sample
isVerifiedbooleanYesMust-see verified flag.Not in the stored sample
isOwnerListingbooleanYesTrue when the detail payload marks an owner listing.Not in the stored sample
isNewListingbooleanYesTrue when the detail payload marks a new listing.Not in the stored sample
isNewLaunchbooleanYesTrue when the detail payload marks a new launch.Not in the stored sample
agentLicensestringYesCEA number from the listing or agent block.Not in the stored sample
agentUrlstringYesAgent profile URL built from the CEA number and name.Not in the stored sample
agentPhotoUrlstringYesAgent photo URL.Not in the stored sample
clusterIdstringYesCluster id from the detail payload.Not in the stored sample
agencyNamestringYesAgency name, filled from the agent profile when that profile is fetched.Not in the stored sample
agencyLicensestringYesAgency license number from the agent profile.Not in the stored sample

Agent profile object

When scrapeAgentInfo succeeds, the record gains an agent object. The parser omits empty keys. Nested list and object values are copied from the profile payload. No stored sample includes this object.

FieldTypeNullableDescriptionExample
idstringYesCEA agent number.Not in the stored sample
namestringYesAgent name.Not in the stored sample
licensestringYesSame CEA number as id.Not in the stored sample
jobTitlestringYesJob title.Not in the stored sample
phonestringYesRaw or masked phone from the profile.Not in the stored sample
emailstringYesEmail from the profile.Not in the stored sample
photoUrlstringYesProfile photo URL.Not in the stored sample
descriptionstringYesProfile biography.Not in the stored sample
agencyNamestringYesAgency name.Not in the stored sample
agencyLicensestringYesAgency number.Not in the stored sample
agencyLogoUrlstringYesAgency logo URL.Not in the stored sample
areaExpertisearrayYesArea expertise list from the profile.Not in the stored sample
listingsBreakdownobjectYesListings breakdown object from the profile.Not in the stored sample
transactionHistoryobjectYesTransaction-history widget from the profile.Not in the stored sample
isCorporatebooleanYesCorporate-agent flag.Not in the stored sample
profileUrlstringYesAgent profile URL.Not in the stored sample

Use id as the deduplication key for a listing. It is stable on the listing URL.

Example dataset record

The record below is a stored search-card item from a sale search at https://www.99.co/singapore/sale. scrapeListingDetails and scrapeAgentInfo were not required for these fields. Empty keys were omitted by the parser.

{
"id": "eAxBcoKvWVRPCSa9yo7GwS",
"url": "https://www.99.co/singapore/sale/property/the-miltonia-residences-condo-eAxBcoKvWVRPCSa9yo7GwS",
"title": "The Miltonia Residences",
"listingType": "sale",
"listingTypeText": "Buy",
"price": 695000.0,
"priceText": "S$ 695,000",
"currency": "SGD",
"psf": 1344.0,
"psfText": "S$ 1,344 psf",
"bedrooms": 1,
"bathrooms": 1,
"floorAreaSqft": 517.0,
"floorAreaText": "517 sqft",
"propertyType": "Condo",
"propertyTypeCode": "generic_condo",
"propertyTypeGroup": "condo",
"propertyName": "The Miltonia Residences",
"address": "520 Miltonia Close 768104",
"neighbourhood": "Lower Seletar",
"region": "North Region",
"tenure": "99 yrs",
"builtYear": 2015,
"builtYearText": "Built: 2015",
"latitude": 1.4170849196563045,
"longitude": 103.84818889236466,
"thumbnailUrl": "https://pic2.99.co/v3/VeBpwpwaaUryeAcLzouyTf?sampling=lanczos&version=1&height=120&width=120&mode=fill&quality=70&signature=fdadf666f0f6c2ca06589849254bf4893e60316d",
"photoCount": 16,
"floorPlanCount": 1,
"videoCount": 0,
"listedOnText": "1s",
"postedAt": 1786701960962578,
"isMustSee": true,
"mrt": "Khatib MRT",
"agentId": "ovaEwyAwFz6S4ypZYdXJW6",
"agentName": "Thiru P",
"agentPhone": "+6591847685",
"agentWhatsapp": "+6591847685",
"agentIsPremium": true,
"statusCode": "must_see",
"propertySegment": "residential",
"sourceUrl": "https://www.99.co/singapore/sale",
"scrapedAt": "2026-08-14T10:07:16.672060+00:00"
}

Query and input reference

InputTypeRequiredDefaultAccepted valuesDescription
startUrlsarrayNo—99.co search, listing, or agent URLsPreferred source. Paginated as copied. Filters below do not rewrite these URLs.
searchQueriesarray of stringsNo—Location or keyword textMapped to the 99.co name search. Example: Orchard.
listingTypestringNosalesale, rentBuy or rent. Ignored when the start URL already contains /sale or /rent.
propertySegmentsstringNoresidentialresidential, commercial, industrial, landProperty segment.
mainCategoryarray of stringsNo—condo, hdb, landedHigh-level type group. Empty means all, or the groups implied by subtypes.
propertySubTypesarray of stringsNo—Condo, HDB room counts, and landed subtypes in the input schemaSpecific types. Landed values are sent as landed subtype.
minPriceintegerNo—0 or greater, SGDMinimum asking price. Sale is the total. Rent is monthly.
maxPriceintegerNo—0 or greater, SGDMaximum asking price.
bedroomsarray of stringsNo—0, 1, 2, 3, 4, 50 is studio. 5 is 5 or more.
bathroomsarray of stringsNo—1, 2, 3, 4, 55 is 5 or more.
minSizeintegerNo—0 or greaterMinimum floor area in square feet.
maxSizeintegerNo—0 or greaterMaximum floor area in square feet.
leaseTypearray of stringsNo—Freehold, 99 yrs, 999 yrs, 103 yrsTenure filter.
furnishingarray of stringsNo—unfurnished, partial, fullyFurnishing filter.
rentalTypestringNoemptyempty, all, unit, roomRent searches only. Entire unit or room.
newLaunchbooleanNofalsetrue, falseNew-launch projects only when true.
sortBystringNorelevancerelevance, newest, price_asc, price_desc, psf_asc, psf_desc, size_desc, size_ascSearch result order.
scrapeListingDetailsbooleanNofalsetrue, falseMerge listing-page fields. Charges listing-details when details are attached.
scrapeAgentInfobooleanNofalsetrue, falseAttach the agent profile. Charges agent-info when agent data is attached.
maxItemsintegerNo00 or greaterMaximum listings to save. 0 means no cap.
maxConcurrencyintegerNo51 to 8Parallel listing and agent page workers.
proxyConfigurationobjectNoApify residential, country SGApify proxy inputRequired for a run. The Actor returns before scraping when no proxy is configured.

Minimal request:

{
"searchQueries": ["Orchard"],
"listingType": "sale",
"maxItems": 20
}

Request with listing-page and agent enrichment:

{
"startUrls": [{"url": "https://www.99.co/singapore/sale"}],
"listingType": "sale",
"mainCategory": ["condo"],
"bedrooms": ["2", "3"],
"maxItems": 50,
"scrapeListingDetails": true,
"scrapeAgentInfo": true,
"maxConcurrency": 5,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"],
"apifyProxyCountry": "SG"
}
}

Retrieve the data through the API

This is the Apify Actor API. 99.co does not provide this endpoint.

  1. Start datascrapers/99-co-scraper with a JSON input.
  2. Wait until the run finishes, or poll the run status.
  3. Read items from the run's default dataset.
  4. Page the dataset with offset and limit, or export it from the run page.
from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("datascrapers/99-co-scraper").call(run_input={
"searchQueries": ["Orchard"],
"listingType": "sale",
"maxItems": 20,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)

Other languages are listed on the Actor's API tab in Apify Console. Do not put a real token in a URL or in source control.

Export and pipeline examples

DestinationRecommended methodTypical use
PostgreSQL or SupabaseDataset API or a webhook consumerStore id as the listing key
Google SheetsApify integrationShare a price and area extract
Amazon S3 or other cloud storageScheduled dataset export or an integrationArchive each run's JSON or CSV
A custom applicationDataset items endpointPage through a large search

Data quality and record handling

Search-card fields come from the 99.co listing payload. currency is set to SGD by the Actor. listingTypeText is derived from listingType. Other card fields are copied from the source. Empty strings, empty lists, and nulls are dropped before the card is saved, so a missing key means the source value was empty or the card did not include it.

Within one run, discovery skips a listing id that was already yielded. The recommended key across runs is id. url identifies the same listing page.

listing-details is charged only when a listing page is merged onto the record. agent-info is charged only when an agent profile is attached. A failed detail or agent request does not remove the card. A listing with neither id nor url is not saved.

postedAt is the source integer as returned. scrapedAt is the Actor's UTC save time. listedOnText is relative text from the page, not a normalized date. Coordinates are decimal degrees when the card includes them.

The stored sample does not include commercial, industrial, land, or rent rows. Those segments are accepted by the input. Field presence still depends on what 99.co returns for that search.

Pricing and cost examples

The Actor uses pay per event. Every saved listing charges one dataset-item event at $0.001. A listing that also receives parsed listing-page fields charges one listing-details event at $0.001. A listing that also receives an agent profile charges one agent-info event at $0.001. The same prices apply on every plan tier. There is no separate Actor-start event in this Actor.

Platform usage, including compute and residential proxy traffic, is billed to the run account in addition to these events. Estimates below are event charges only.

RecordsEventsEstimated event cost
1,000 listing cards1,000 × dataset-item$1.00
1,000 cards with details and agent profiles1,000 × each of the three events$3.00
10,000 listing cards10,000 × dataset-item$10.00
10,000 cards with details and agent profiles10,000 × each of the three events$30.00

A listing that is not saved does not create a dataset-item charge. A detail or agent request that adds nothing does not create that extra charge. maxItems caps saved listings and therefore the dataset-item count.

Limitations and responsible data use

The Actor reads publicly available 99.co pages and the listing fields those pages expose. It does not return a historical archive. Each run reflects the site at the time of the request. Save datasets yourself if you need a time series.

Values can be missing when 99.co omits them, when a filter matches nothing, or when a detail or agent page does not parse. A residential proxy in Singapore is required for the run to start scraping. Site markup and availability can change.

Agent phone numbers and similar contact fields are present only when the listing or profile exposes them. You are responsible for using the dataset in line with 99.co terms, privacy rules, and any contract that applies to your project. This page does not provide a legal conclusion about a particular use.

Dataset questions

What does one dataset item represent?

One 99.co listing. Search-card runs contain the fields in the main dictionary. Detail and agent runs add the conditional fields on that same listing record.

Which field should I use as a unique identifier?

Use id. It is the listing id on the 99.co URL. url identifies the same page.

Are fields nullable or conditional?

Card keys are omitted when the source value is empty. Description, postal code, district, furnishing, and the agent object are conditional on the enrichment switches and on a successful page parse.

Can I retrieve the records as CSV or JSON?

Yes. Download JSON, CSV, Excel, XML, or another format from the Apify Console export menu, or read JSON through the Dataset API.

How do I paginate large datasets?

The Actor pages through a 99.co search until maxItems or the search ends. After the run, page the Apify dataset with offset and limit.

Does the Actor return historical data?

No. A record is the listing state observed during that run. Earlier prices are available only if you stored an earlier dataset.

What counts as a billable result?

Each saved listing is one dataset-item event at $0.001. Each listing whose detail page is merged is an additional listing-details event at $0.001. Each listing that receives an agent profile is an additional agent-info event at $0.001. Platform usage is charged separately.

Data Scrapers support

Need an additional field, record type, or export workflow? Contact Data Scrapers at stardustspotlight@gmail.com. Include a sample source URL, required fields, expected record volume, and preferred delivery format.