28Hse Scraper – Hong Kong Property Listings, FSBO & Details
Pricing
from $2.00 / 1,000 property listings
28Hse Scraper – Hong Kong Property Listings, FSBO & Details
Scrape 28Hse, a Hong Kong property portal, without an API or login: sale and rental listings plus full property details — price, saleable area, unit price, layout, district and estate, with agent vs owner (FSBO) marker. Unique owner asking-price data for HK property valuation. Export JSON/CSV/Excel.
Pricing
from $2.00 / 1,000 property listings
Rating
0.0
(0)
Developer
Chad
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
28Hse Scraper – Hong Kong Property Listings & FSBO Details (No API)
Scrape 28Hse.com (Hong Kong's property portal): property listings and full single-property detail, via public HTML endpoints (SSR + JSON-LD) — no login, no token. Structured output, and FSBO (owner-listed) detection is this Actor's core feature — a data dimension agency-only sites don't expose, useful for valuation models, proptech, or agency research teams looking to fill out asking-price data.
Complements the same author's centaline-scraper (Centaline transaction records): centaline covers completed transactions, this Actor covers active listing detail and FSBO filtering.
Output fields
Property listings (listing mode)
| Field | Type | Description |
|---|---|---|
| property_id | string | Property ID |
| title | string | null | Title |
| deal_type | string | null | buy or rent |
| property_type | string | null | Property type, e.g. apartment |
| price | number | null | Price (HKD). The raw text "售 $830 萬元" is converted to 8300000 |
| saleable_area | number | null | Saleable area (sqft) |
| gross_area | number | null | Gross floor area (sqft). Listing cards don't show gross area, so this field is always null in listing mode — only detail mode populates it |
| unit_price | number | null | Unit price per saleable sqft (HKD) |
| district | string | null | District, e.g. "Tsuen Wan" |
| estate | string | null | Estate name |
| rooms | number | null | Room count. Open-plan units (no separate rooms) honestly return null instead of a guess |
| bathrooms | number | null | Bathroom count |
| is_owner | boolean | FSBO (owner-listed) detection. The page marking "業主盤" / "業主自讓" (owner listing) = true; agent listing or no marking = false |
| agent_name | string | null | Agency name; for FSBO listings, the owner contact name shown on the page |
| posted_at | string | null | Posted time (ISO 8601). The raw value is relative ("36 seconds ago", "N days ago"), converted to an absolute timestamp |
| listing_url | string | null | Full listing URL |
| source_mode | string | Always listing |
| scraped_at | string | Scrape time (ISO 8601) |
Property detail (detail mode, all fields above + the following)
| Field | Type | Description |
|---|---|---|
| address | string | null | Full address |
| floor_size_sqft | number | null | Saleable area (sqft), same source as saleable_area |
| num_rooms | number | null | The raw room-count field from the listing page's own JSON-LD. Note: this is the site's own JSON-LD structured value, and in testing it doesn't always match the "N 房" (N rooms) shown on the page (the rooms field above) — see Notes below. Both fields are kept, each honestly reporting its own source |
| description | string | null | Full listing/estate description |
| agent_address | string | null | Agency (or owner contact) address |
| agent_url | string | null | Agency (or owner) page link |
| date_published | string | null | Posted time (ISO 8601, with timezone) |
| date_modified | string | null | Last-updated time (ISO 8601, with timezone) |
| image_count | number | null | Total number of listing images (including floor/unit plans) |
Modes
| Mode | Input | Output | Billing | Status |
|---|---|---|---|---|
| 📋 listing | dealType + propertyType (or listingUrls) | Property listings | search-listing $0.002/item | v1.0 |
| 🏠 detail | propertyIds or propertyUrls | Full single-property fields | product-detail $0.008/item | v1.0 |
| 📈 transactions | estateUrls (estate transaction records) | Transaction records | review-item $0.003/item | Not yet available in v1.1 — selecting this mode returns an error |
All charges happen only after data is successfully pushed to the dataset. The run's end-of-run log prints total_items / charged / skipped so you can reconcile.
Input fields
| Field | Type | Default | Description |
|---|---|---|---|
| mode | enum | listing | listing / detail / transactions (not yet available, see table above) (required) |
| dealType | enum | buy | buy or rent. Used by listing mode; also needed in detail mode when using propertyIds (instead of propertyUrls) |
| propertyType | enum | apartment | Property type. Only "Apartment" has been fully tested; other types use the same URL pattern but haven't each been individually verified |
| listingUrls | array | — | Listing mode only, directly specify listing page URLs (overrides dealType/propertyType), multiple allowed, each paginated and counted independently |
| propertyIds | array | — | Detail mode, property IDs (numeric only), needs dealType/propertyType to build the URL |
| propertyUrls | array | — | Detail mode, full property URLs, safest since deal/type info is already in the URL |
| bulkInput | textarea | — | Bulk paste, one item per line, merged into the matching field based on the current mode |
| ownerOnly | boolean | false | Listing mode only: only keep FSBO listings, filtering out agent listings. FSBO listings are usually rarer within a single category — if 5 consecutive pages return no matches, the run stops early (see Notes below) |
| maxItems | integer | 50 | Listing mode: max items per listing source; detail mode: max properties to scrape. Maximum 500 |
| dateFrom | string | — | Listing mode only, filters by listing posted time. Format 3d, 12h, 2w (relative) or MM/DD/YY (absolute date). When set, paging also stops early, which keeps scheduled monitoring cheaper. Not applicable to detail mode |
| proxyConfiguration | proxy | Apify datacenter proxy | On by default; without a proxy your run shares egress IPs with other runs and gets rate-limited. Turn off only for your own proxy or local testing |
Input examples
1. Property listings: apartments for sale
{"mode": "listing","dealType": "buy","propertyType": "apartment","maxItems": 50}
2. Property detail: a single property
{"mode": "detail","propertyUrls": ["https://www.28hse.com/buy/apartment/property-3948689"]}
3. FSBO only, rentals
{"mode": "listing","dealType": "rent","propertyType": "apartment","ownerOnly": true,"maxItems": 100}
4. Bulk-scrape multiple property IDs
{"mode": "detail","dealType": "buy","propertyType": "apartment","bulkInput": "3948689\n3924956\n3990751"}
5. Scheduled monitoring: only listings posted in the last 3 days
{"mode": "listing","dealType": "buy","propertyType": "apartment","dateFrom": "3d","maxItems": 200}
Notes
- Only scrapes public content — no account or token required, uses public HTML endpoints (SSR + JSON-LD), no browser, fast and low-cost
- Price parsing: the raw text "售 $830 萬元" ("萬" = 10,000) is automatically converted to a number (× 10,000 = 8,300,000); rental prices usually have no "萬" unit and are taken as-is
- FSBO (
is_owner) detection: only returnstruewhen the page explicitly marks a listing as "業主盤" / "業主自讓" (owner-listed). A title that merely mentions "業主" (owner) in passing (e.g. "friend of the owner, price negotiable") is not mistaken for FSBO — that's still an agent listing, andis_ownercorrectly returnsfalse ownerOnlycost-protection mechanism: FSBO listings are usually much rarer than agent listings within a single category (e.g. "buy · apartment"). To avoid the crawler paging forever burning cost in a category with no FSBO listings at all, this Actor follows the rule "stop after 5 consecutive requests with no matching data" — withownerOnlyenabled, if 5 consecutive pages have no FSBO listings, the run stops immediately, even if the category theoretically still has more (agent-only) listings left. If FSBO listings happen to be clustered on later pages, some may be missed as a result; narrower filters (splitting runs bydealType/propertyType) increase FSBO density and reduce this risk- Why
roomsandnum_roomsare two separate fields:rooms(shared by listing and detail mode) comes from the "N 房" (N rooms) text shown on the page (reliable, matches what's displayed);num_rooms(detail mode only) is the raw room-count field from the site's own JSON-LD structured data. In testing, the two can disagree for the same property (e.g. a listing page displays "3 房" but its JSON-LDnumberOfRoomsis1) — this is a discrepancy in 28Hse's own data source, not a parsing error by this Actor. Both fields are kept as-is, each honestly reporting its own source, with no field overriding the other - Open-plan units (no separate room partitions) honestly return
nullforroomsrather than guessing 0 or 1 gross_area(gross floor area) is only populated in detail mode — listing cards only show saleable area, not gross area- Paging stops automatically after 5 consecutive pages with no new data, to avoid burning cost for nothing; if
dateFromis set, paging also stops as soon as an entire page of listings is older thandateFrom(further reduces cost, listing mode only) - Estate transaction records (transactions mode) are not yet available, planned for v1.1. Selecting this mode currently returns an error message immediately and consumes no charge
- 28Hse currently shows no anti-bot challenge (no CAPTCHA/JS challenge) against the datacenter proxy this Actor uses; if the site tightens defenses in the future, the fallback plan is to switch to a residential proxy — not over-engineered in advance
FAQ
Q: Why are district/estate/price null for some listings?
A: A small number of listing cards have an incomplete layout (e.g. carparks, non-residential listings, or the listing itself is missing data). This Actor honestly returns null rather than substituting a guessed value.
Q: ownerOnly returns no FSBO listings at all — is that normal?
A: Yes. FSBO listings are naturally rarer than agent listings; if 5 consecutive pages have no matches, the run stops early (see Notes above) — this is a deliberate cost-protection mechanism, not a bug. Try a different dealType/propertyType combination, or check the 28Hse site directly to confirm whether that category currently has any active FSBO listings.
Q: What's the difference between propertyIds and propertyUrls?
A: propertyUrls (full URLs) already carry deal type and property type info, making them the safest choice; propertyIds (numeric IDs only) require dealType/propertyType to build the correct URL. The two can also be mixed.
Q: Why is gross_area (gross floor area) always null in listing mode?
A: 28Hse's listing cards only show saleable area, not gross floor area; gross floor area only appears on the property detail page, so only detail mode output has a value. If you need gross floor area, use detail mode for the relevant properties.
Q: When will estate transaction records (Land Registry data) be available?
A: Planned for v1.1. 28Hse's transaction records go through an AJAX endpoint, and the full set of request parameters hasn't been confirmed yet — this will be added once confirmed, at which point mode will simply gain another available option, with no need to switch to a different Actor.
Disclaimer
This tool is intended for academic research and lawful business analysis only. Users must comply with 28Hse's terms of service and applicable laws. Do not use this tool to infringe on others' privacy, spread false information, or engage in any other unlawful activity. The author is not responsible for any misuse.
28Hse scraper, Hong Kong property, HK real estate data, FSBO listings, property listings, HK property prices