Batdongsan.com.vn - Property Listings, Prices & Agents
Pricing
from $1.00 / 1,000 results
Batdongsan.com.vn - Property Listings, Prices & Agents
From $1/1K. Scrape Batdongsan.com.vn property listings into clean JSON. Search by listing type, property category, city, or search URL. Automatically paginate results and extract prices, GPS coordinates, property specs, agent details, listing dates, and high-resolution photos.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
29
Total users
7
Monthly active users
a day ago
Last modified
Categories
Share
Batdongsan.com.vn Scraper: Vietnam Property Listings, Prices and Agent Details
Batdongsan.com.vn Scraper turns Vietnam's largest property portal into clean structured data. Pick a listing type, property category and city in Search mode, or paste any batdongsan.com.vn search URL in URL mode, and pull prices, area, bedrooms, GPS coordinates, the full Vietnamese specification table, agent details and photo galleries for apartments, houses, land, offices and more. Walk hundreds of pages automatically, then export the results to JSON, CSV or Excel, or pull them straight into your app through the API.
Why This Scraper?
- Two input modes, real filters. Search mode takes a listing type, property type and city plus min/max price, min/max area, bedrooms, house-facing direction, a keyword and a sort order, all applied by batdongsan.com.vn itself so only matching listings are fetched and billed. URL mode takes any batdongsan.com.vn link you already refined in your browser.
- Buy and rent in one run, 16 property types. Including mini apartments, shophouses, condotels, farms and resorts, rooms for rent and shops or kiosks.
- Detail enrichment on demand. Turn it on for GPS coordinates, the agent's name and profile, the full Vietnamese specification table, posted and expiry dates and the description, or off for a faster run with just the listing-card fields.
- Vietnamese price parsing. "2 tỷ", "1,5 tỷ" and "25 triệu/tháng" are parsed into numeric VND alongside the original text, plus a derived price per square meter.
- Automatic pagination and deduplication. Give it a city and a page budget and it walks page by page, de-duplicating listings by id across pages and across multiple cities or URLs in one run.
- Built for schedules. Incremental mode tracks one search across scheduled runs and returns only new and changed listings, and unchanged ones are not billed. A separate resume feature continues one interrupted run without paying twice for the same walk.
- Send results into your apps. Optionally pipe scraped listings into Notion, Linear, Airtable or another MCP-connected tool right after the scrape finishes.
Use Cases
- Real estate agencies and investors: track new listings, price changes and inventory across cities to spot opportunities early.
- Market researchers and analysts: build historical pricing datasets by city, district and property type for trend analysis.
- Proptech builders and listing aggregators: feed a property search app or comparison tool with structured Vietnamese listing data.
- Lead generation teams: monitor new listings together with agent contact details for outreach.
- Academic and journalism research: study housing supply, pricing and market segmentation across Vietnam's provinces.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
id | "45763512" |
url | "https://batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm/sample-listing-pr45763512" |
listingType | "Buy" (also "Rent") |
propertyType | "Apartment" |
title | "Sample listing title, 2 bedroom apartment near the river" |
priceText / priceUnit | "3 tỷ" / "tỷ" ("Thỏa thuận" when the price is negotiable) |
priceValue | 3000000000 (numeric VND, null when negotiable) |
pricePerM2 | 88235294 (see below - a per-m² rate when the site itself quotes one, otherwise derived from priceValue / area) |
area | 34 |
bedrooms / bathrooms | 2 / 2 |
direction | "Đông - Nam" (detail fetch only) |
locationText | "Sample Ward, Sample District, Hồ Chí Minh" |
city / district | "Hồ Chí Minh" / "Sample District" |
latitude / longitude | 10.77 / 106.70 (detail fetch only) |
project | "Sample Project" (detail fetch only) |
listingTier | "Tin VIP Kim Cương" |
postedDate / expiryDate | "20/05/2026" / "04/06/2026" (detail fetch only) |
agentName | "Sample Agent" (detail fetch only) |
agentProfileUrl | "https://guru.batdongsan.com.vn/pa/sampleagent" (detail fetch only) |
specs | { "Diện tích": "34 m²", "Số phòng ngủ": "2 phòng", "Hướng nhà": "Đông - Nam", ... } (detail fetch only) |
imageCount / images | 24 / ["https://file4.batdongsan.com.vn/.../sample.jpg", ...] |
isVerified / isProAgent / hasVideo | true / false / true (the site's "Tin xác thực" badge, whether the poster has a professional-agent page, whether the listing has a video) |
legalStatus / frontageWidth / roadWidth / floors | "Sổ đỏ/ Sổ hồng" / 5 / 8 / 4 (frontage and access road in metres, detail fetch only) |
searchUrl / pageNumber / position | the results page the listing was found on, its page number and its rank on that page |
scrapedAt | "2026-05-26T00:00:00.000Z" |
priceValue and pricePerM2 - two price forms, one pair of fields. Batdongsan.com.vn quotes a listing's price in one of two shapes, and both are normalized into the same two fields: a total price ("3 tỷ") gives priceValue directly, and pricePerM2 is then derived as priceValue / area; a per-square-metre rate ("112 triệu/m²", common on land and project listings) gives pricePerM2 directly from the site's own rate, and priceValue is then derived as rate × area. Either way, whichever number is missing because the listing has no area comes back null, as does a negotiable price ("Thỏa thuận"). pricePerM2Text carries the site's own per-m² text exactly as shown (null on a total-priced listing). Prices are in Vietnamese dong only; no USD conversion is added, because there is no exchange rate on the site to stamp it with.
Each record also carries ward, districtId, cityCode, newProvince (the post-2025 merged-province name the site now shows alongside the legacy city/district/ward breakdown, where the site exposes one - null on pages that only print the pre-merger address), balconyDirection, furniture, agentId, agentPhone (usually null, see FAQ), agentPhoneToken, agentAvatar, description, listingCode, areaText (the area exactly as shown), thumbnailUrl, postedLabel (the site's relative "Đăng 3 ngày trước" text), isNegotiable, rentPeriod ("month" for monthly rents), currency ("VND"), pricePerM2Text (above), address (the posted address line exactly as shown, detail fetch only), developer (the project's developer name, from the listing's own project card, detail fetch only), postedAt (the listing's publish date as an ISO-8601 UTC timestamp, detail fetch only), listingTierCode (a short machine-readable code for listingTier, e.g. "vip_diamond"; null when the listing carries no promoted tier) and isVip (true for any paid/promoted tier, false for an ordinary listing, null when the tier itself is unknown), and with detail fetch projectId, projectUrl and videos (video file links). district and ward are null when the listing's address line only gives the merged-province form (no legacy district/ward segment to parse); newProvince fills in for those rows. When Incremental mode is on, every record additionally carries changeType (NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED), changedFields, firstSeenAt and lastSeenAt, described under "Resume and recurring updates" below.
What changed in this release
priceValue/pricePerM2fixed for per-m² listings. A per-m²-rate listing used to have its rate stored as the total and then divided by area again, under-pricing those rows; both fields now come from the correct side of the calculation described above (see the price-form note).district/wardfixed for abbreviated and merged addresses. A ward-level address segment (e.g. "P. Bến Nghé") used to be misread as a district; it is now parsed as a ward, withnewProvince/districtfilled from the fuller detail-page address where the SERP card alone is ambiguous.- Six additive fields.
pricePerM2Text,address,developer,postedAt,listingTierCodeandisVip(all above) are new in this release - existing integrations are unaffected; these fields simply appear alongside the ones you already read. - Unsupported URLs are always reported. In URL mode, a broker/company/project-directory URL is now warned about and listed in the run's
OUTPUTsummary up front, even whenmaxListingsis reached before the run gets to it, and it no longer takes a share of the listing budget from your supported URLs. With theurlsfield left unedited, URL mode scrapes the 12 example URLs (one per supported shape). - One-time incremental baseline migration. If you have Incremental mode state saved from before this release, the first run after upgrading recomputes every tracked listing's fingerprint under the corrected price/location logic. This is a zero-wave migration: it will not emit a burst of spurious
UPDATEDrows for listings whose real-world data hasn't changed, but a listing whose price or district genuinely reads differently now (because it was wrong before) is correctly reported asUPDATEDonce.
Output views
The dataset's Output tab offers three views: Listings (the default, every field above), Detail (GPS, description, the Vietnamese specs table, legal status, frontage/road width/floors, agent and project fields - populated only with Detail fetch on), and Changes (id, url, title, price fields, changeType, changedFields, firstSeenAt, lastSeenAt - populated only with Incremental mode on). No new record kind is added by these views; they are the same rows with different columns shown.
How to Use
- Pick a mode:
search(listing type, property type, city and optional filters) orurl(paste one or more batdongsan.com.vn links - search/category, project, or a single listing's detail page). - For Search mode, choose Buy, Rent or both, a property type, one or more cities and any filters or sort order. For URL mode, paste the URLs - see the table below for every shape it accepts.
- Set Max pages and Max total listings to control run size and cost, and turn Detail fetch on or off.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search mode, apartments for sale in Ho Chi Minh City, with detail enrichment:
{"mode": "search","listingType": "Buy","propertyType": "Apartment","locations": ["tp-hcm"],"maxPages": 5,"fetchDetails": true}
Search mode, 3-4 bedroom houses for 3-8 billion VND, 40-120 m², cheapest first:
{"mode": "search","listingType": "Buy","propertyType": "PrivateHouse","locations": ["tp-hcm"],"minPrice": 3000000000,"maxPrice": 8000000000,"minArea": 40,"maxArea": 120,"bedrooms": ["3", "4"],"sortBy": "PriceLowToHigh","maxListings": 100}
Search mode, shophouses for sale and for rent in one run, matching a keyword:
{"mode": "search","listingType": "BuyAndRent","propertyType": "Shophouse","locations": ["ha-noi"],"keyword": "vinhomes","fetchDetails": false}
URL mode, paste browser-filtered URLs:
{"mode": "url","urls": ["https://batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm","https://batdongsan.com.vn/cho-thue-van-phong-ha-noi/p2"],"maxPages": 4,"fetchDetails": true}
Property types
propertyType | Site category | Buy | Rent |
|---|---|---|---|
All | Tất cả nhà đất | yes | yes |
Apartment | Căn hộ chung cư | yes | yes |
MiniApartment | Chung cư mini, căn hộ dịch vụ | yes | yes |
PrivateHouse | Nhà riêng | yes | yes |
TownHouse | Nhà mặt phố | yes | yes |
Villa | Nhà biệt thự, liền kề | yes | yes |
Shophouse | Shophouse, nhà phố thương mại | yes | yes |
Land | Đất | yes | - |
ProjectLand | Đất nền dự án | yes | - |
Condotel | Condotel | yes | - |
FarmResort | Trang trại, khu nghỉ dưỡng | yes | - |
Office | Văn phòng | - | yes |
Warehouse | Kho, nhà xưởng (rent: kho, nhà xưởng, đất) | yes | yes |
Room | Nhà trọ, phòng trọ | - | yes |
ShopKiosk | Cửa hàng, ki ốt | - | yes |
Other | Loại bất động sản khác | yes | yes |
A combination marked - does not exist on batdongsan.com.vn: a Buy-only or Rent-only run with one fails straight away with a clear message, and a BuyAndRent run skips the missing side.
URL mode: every supported shape
URL mode accepts any of the shapes below, and you can mix shapes freely in one run - each URL is routed automatically. A trailing /pN on a search/category URL starts that search at page N; ?utm_.../?vrs=... tracking params and the m.batdongsan.com.vn mobile host are stripped/followed transparently and never need editing out.
| Shape | Example | What you get |
|---|---|---|
| City/district/ward search (Buy/Rent × property type) | batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm | The same paginated search a Search-mode run would build |
| All property types for a city | batdongsan.com.vn/nha-dat-ban-ha-noi / .../nha-dat-cho-thue-ha-noi | Every property type mixed together, Buy or Rent |
| Price-filtered search (applied in your browser) | batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm/gia-tu-1-ty-den-2-ty | A narrower search - the path segment IS the filter |
| Area-filtered search | batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm/dt-tu-100m2-den-150m2 | Same idea, filtered by floor area |
| Bedroom-count-filtered search | batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm/1pn | Same idea, filtered by bedroom count |
| Filter-panel query URL | batdongsan.com.vn/ban-nha-rieng-tp-hcm?gtn=3-ty&gcn=8-ty&dtnn=40&dtln=120&rs=3,4&dirs=1&k=vinhomes | What the site's own filter panel produces; any combination of price, area, bedrooms, direction and keyword |
| Map view / sort query params | ...?tpl=map, ...?sortValue=8 | Passed through as-is; a sort is kept on every page |
| Project page | batdongsan.com.vn/du-an-can-ho-chung-cu-quan-2/the-sun-avenue-pj2065 | Every listing for sale/rent inside that project |
| Topic (tag) page | batdongsan.com.vn/tags/ban/ban-can-ho-mini-tp-hcm / .../tags/cho-thue/... | The site's curated topic listing pages, paginated like a search; each listing's property type is taken from its own URL |
| Single listing's detail page | batdongsan.com.vn/.../some-title-pr46279173 | That one listing, fetched directly - no pagination, no wasted retries on a shape that was never a search page |
| Mobile host | m.batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm | Redirects to the same page as the apex host; works identically |
| Two stacked filter segments, or an unknown category | .../tp-hcm/gia-tu-1-ty-den-2-ty/dt-tu-50m2-den-80m2 | Not supported by the site - it redirects to the whole-country listing page. Detected and skipped with a warning instead of returning unrelated listings. Use the query-string form above to combine filters |
| Broker/developer/company profile page | batdongsan.com.vn/dau-tu-du-an/...-ep3102, .../tu-van-moi-gioi-bat-dong-san/...-ep2967, /nha-moi-gioi, /doanh-nghiep, or any guru.batdongsan.com.vn/pa/... agent-profile link | Not supported - this is a company/agent profile, not a listing or a search. Skipped with a logged warning and no proxy spend, never scraped as a silent empty page |
| Project directory page | batdongsan.com.vn/du-an-can-ho-chung-cu, .../du-an-bat-dong-san, .../du-an-can-ho-chung-cu-quan-2 | Not supported - it lists projects, not listings. Skipped with a logged warning and no proxy spend; paste one project page (ending in -pj plus digits) or a search URL instead |
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/batdongsan-com-vn-scraper").call(run_input={"mode": "search","listingType": "Buy","propertyType": "Apartment","locations": ["tp-hcm"],})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["title"], listing["priceText"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/batdongsan-com-vn-scraper').call({mode: 'search',listingType: 'Buy',propertyType: 'Apartment',locations: ['tp-hcm'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
How Max pages and Max total listings cap a run
Max pages bounds how many result pages the walk visits per city or URL (roughly 20 to 30 listings per page); left at 0 it is unlimited and the walk stops naturally once a page comes back empty, once every listing on a page has already been collected, or once the site's own pagination ends. Max total listings is the one soft cap that applies across every city or URL in the run; it defaults to 20 so a first run stays small. When several cities or URLs are given, the budget is split proportionally so every one of them gets some representation instead of the first search using it all.
Resume and recurring updates
There are two different features here, pick the one that matches what you're doing:
| Need | Use |
|---|---|
| A crawl stopped partway and should continue | resumeFromRunId |
| Run the same search on a schedule and get only what changed | incrementalMode |
| Keep separate campaigns for similarly configured searches | distinct stateKey values |
| A normal, full one-off pull | leave both off |
Resume (resumeFromRunId) continues one specific interrupted or previous large pull: paste a run ID or dataset ID and the actor loads the ids it already saved there and skips them, so this run only appends new listings. Rows marked EXPIRED in that dataset are not treated as already collected, so a listing that has come back is scraped again. Combined with Incremental mode on a search that has no saved state yet, the resumed rows also become the starting baseline, so the next scheduled run reports them as UNCHANGED or UPDATED against what was actually saved.
Incremental mode (incrementalMode) is for a schedule, for example daily: the actor remembers the previous run of the same search by itself, keyed on mode, listing type, property type, location(s) or URL(s), any filters or sort you set, and the detail-fetch setting (or on stateKey, when set). Searches you set up before the filter inputs existed keep their saved state, and the output fields added since are not counted as a change. The first run for a search returns every listing as NEW. Later runs classify each listing as NEW, UPDATED (with changedFields listing what changed), or UNCHANGED (suppressed, and not billed, unless emitUnchanged is on). Turning on emitExpired adds EXPIRED rows for listings that were tracked before but are no longer found, and only once a run has completely scanned the tracked search - not capped by Max total listings, not a resume, and not a pasted URL that started at /pN with N>1 (that only scans the tail of the catalogue). EXPIRED rows are dataset items too, so they count toward Max total listings: when the limit is reached, the remaining gone listings stay tracked and are reported on a later run. A listing only comes back as REAPPEARED after it was previously marked EXPIRED in that same tracked state and is then found again; with emitExpired left off (the default), listings are never marked gone in the first place, so REAPPEARED will not appear. With Incremental mode off entirely, output is exactly as in a normal run, with no extra fields.
Six fields are excluded from the change comparison by default because they vary from run to run regardless of whether the listing itself changed: scrapedAt (the crawl timestamp), agentPhoneToken (a token the site mints fresh per page view, not a stable per-listing value), searchUrl, pageNumber and position (where the listing happened to sit in this run's results, which moves whenever anything above it changes), and postedLabel (the site's relative "posted N days ago" text). They are still returned in every row. Every other field, including priceValue, area, listingTier, postedDate and specs, is real data and correctly triggers UPDATED when it changes. Use ignoreFieldsForChanges to exclude additional fields of your own on top of those defaults - see the table below.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each record, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search = pick listing type, property type and city below. url = paste batdongsan.com.vn search URLs. |
listingType | string | Buy | Buy (Bán), Rent (Cho thuê), or BuyAndRent (one Buy and one Rent search per city in the same run; each record keeps its own Buy/Rent label). Search mode only. |
propertyType | string | Apartment | One of the 16 categories in the table below. Search mode only. |
locations | array | none (nationwide), UI prefill: ["tp-hcm"] | Any of Vietnam's 63 provinces/cities: the site's own slug (tp-hcm, ha-noi, da-nang, ba-ria-vung-tau, ...), the Vietnamese name with or without diacritics, a common alias (Saigon, HCM, Hanoi, Huế), the site's province code (SG, HN), or the name with Thành phố/Tỉnh/City/Province around it - resolved against the site's own province list, never guessed. For a district or ward, use URL mode with the search URL from the site. An unrecognized name fails the run with a clear error rather than silently returning zero results. Left empty, the search covers the whole country. Search mode only. |
minPrice | integer | none | Lowest total price in VND (monthly rent for Rent searches), e.g. 1000000000 = 1 tỷ. Applied by the site; negotiable-price listings are excluded once a price filter is set. Search mode only. |
maxPrice | integer | none | Highest total price in VND (monthly rent for Rent searches). Search mode only. |
negotiablePriceOnly | boolean | false | Only listings whose price is "Giá thỏa thuận" (negotiable, no published price). Cannot be combined with minPrice/maxPrice. Search mode only. |
minArea | integer | none | Smallest area in m². Search mode only. |
maxArea | integer | none | Largest area in m². Search mode only. |
bedrooms | array | none | Any of "1"-"5" ("5" = the site's 5+ bucket); several are combined. Search mode only. |
directions | array | none | House-facing direction: East, West, South, North, NorthEast, NorthWest, SouthWest, SouthEast. Many listings don't state one. Search mode only. |
keyword | string | none | Free-text keyword matched by the site's own search (project, street, ...). Search mode only. |
sortBy | string | Default | Default, VerifiedFirst, PriceLowToHigh, PriceHighToLow, PricePerM2LowToHigh, PricePerM2HighToLow, AreaSmallToLarge or AreaLargeToSmall. Search mode only. |
urls | array | one example of each distinct URL shape (12 URLs, used when URL mode is selected and the field is left unedited); the UI prefill adds the mobile-host and sort-query variants | batdongsan.com.vn search/category, project, or single-listing URLs - see the URL mode table above for the full list. URL mode only. A trailing /pN on a search/category URL starts that URL at page N. A single listing's detail page (-prNNNNNN) is fetched directly, one record, no pagination. A broker/developer/company profile page is skipped with a logged warning, never silently scraped as empty. Left unedited in URL mode, the run scrapes the 12 example URLs (one per distinct shape); clear the field and add your own to scrape something else. Search mode ignores this field. |
maxPages | integer | 0 | Result pages to walk per city or URL. Roughly 20 to 30 listings per page. 0 means unlimited: the walk stops naturally once a page comes back empty or repeats listings already collected, or sooner once Max total listings is reached. |
maxListings | integer | 20 | The one soft cap on the run: stop after this many listings across all cities and URLs combined. 0 means unlimited, bounded only by the natural stop on Max pages. |
resumeFromRunId | string | none | Paste a previous run ID or dataset ID to continue one specific interrupted or large pull: listings already saved there are skipped, so this run only appends new ones. For recurring monitoring of the same search, use Incremental mode instead. |
incrementalMode | boolean | false | Turn on for scheduled, recurring monitoring of this same search. See "Resume and recurring updates" above. |
stateKey | string | none | Incremental mode only. Name this monitoring campaign, or deliberately share state across differently configured runs. Left empty, a key is derived automatically from mode, listing type, property type, location(s) or URL(s), filters, and the detail-fetch setting. |
emitUnchanged | boolean | false | Incremental mode only. Also return listings unchanged since the last run, marked UNCHANGED. Adds and bills extra rows you already have. |
emitExpired | boolean | false | Incremental mode only. Also return listings from a previous run that are no longer found, marked EXPIRED, once a run has fully scanned the tracked search (not capped, not a resume, not a /pN-started URL). Adds and bills extra synthetic rows. |
ignoreFieldsForChanges | array | none (always excluded: scrapedAt, agentPhoneToken, searchUrl, pageNumber, position, postedLabel) | Incremental mode only. Extra fields to exclude from change detection, on top of the always-excluded defaults (the crawl time, a per-render contact token, where the listing sat in this run's results, and the relative "posted N days ago" label). Accepts a top-level field name (agentAvatar) or a dotted path into a nested field (specs.Ngày đăng). Listed fields still appear in the output - only excluded from deciding UPDATED vs UNCHANGED and from changedFields. |
fetchDetails | boolean | true | Visit each listing's detail page for GPS, description, agent details, the specification table, posted and expiry dates, and the listing code. Turn off for a faster, cheaper run with just the listing-card fields. |
proxy | object | Apify Residential, country VN | Proxy configuration. A Vietnam-reachable residential connection is required for reliable results; left empty, the actor applies this same configuration by itself. |
mcpConnectors | array | none | Optional: send a summary of each record to apps you authorized under Integrations. |
notionParentPageUrl | string | none | Notion connector only: page under which item pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. Does not affect the dataset. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"id": "45763512","url": "https://batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm/sample-listing-pr45763512","listingType": "Buy","propertyType": "Apartment","title": "Sample listing title, 2 bedroom apartment near the river","priceText": "3 tỷ","priceValue": 3000000000,"priceUnit": "tỷ","pricePerM2": 88235294,"area": 34,"bedrooms": 2,"bathrooms": 2,"direction": "Đông - Nam","balconyDirection": null,"furniture": "Không nội thất","locationText": "Sample Ward, Sample District, Hồ Chí Minh","city": "Hồ Chí Minh","district": "Sample District","ward": "Sample Ward","latitude": 10.77,"longitude": 106.7,"cityCode": "SG","districtId": "1000","project": "Sample Project","listingTier": "Tin VIP Kim Cương","postedDate": "20/05/2026","expiryDate": "04/06/2026","listingCode": "45763512","agentName": "Sample Agent","agentId": "0000000","agentPhone": null,"agentProfileUrl": "https://guru.batdongsan.com.vn/pa/sampleagent","description": "Sample description text describing the property.","specs": {"Diện tích": "34 m²","Số phòng ngủ": "2 phòng","Số phòng tắm, vệ sinh": "2 phòng","Hướng nhà": "Đông - Nam"},"imageCount": 24,"images": ["https://file4.batdongsan.com.vn/resize/1275x717/sample.jpg"],"areaText": "34 m²","currency": "VND","isNegotiable": false,"rentPeriod": null,"legalStatus": "Sổ đỏ/ Sổ hồng","frontageWidth": null,"roadWidth": null,"floors": null,"projectId": "0000","projectUrl": "https://batdongsan.com.vn/du-an-can-ho-chung-cu-sample/sample-project-pj0000","isVerified": false,"isProAgent": true,"hasVideo": false,"videos": [],"thumbnailUrl": "https://file4.batdongsan.com.vn/crop/200x140/sample.jpg","postedLabel": "Đăng 3 ngày trước","searchUrl": "https://batdongsan.com.vn/ban-can-ho-chung-cu-tp-hcm","pageNumber": 1,"position": 1,"scrapedAt": "2026-05-26T00:00:00.000Z"}
Plan Requirement
Batdongsan.com.vn only serves traffic from Vietnam-reachable connections, so a Vietnam residential proxy is required for reliable results. The default proxy configuration uses Apify Residential pinned to country VN, and applies automatically if the field is left empty; the actor automatically rotates exit IPs and falls back to a backup connection type when a request is refused. On a plan without residential proxy access the run continues on that backup connection and says so in the log; if the backup connection cannot load the pages either, the run ends as failed with a clear message instead of returning an empty dataset. You can also uncheck "Use Apify proxy" in the Connection field and paste your own Vietnam residential proxy URLs instead.
If a run returns zero listings, the exit IP was likely flagged for this site: re-run for a fresh IP, and keep the proxy country pinned to VN. Large Search-mode runs (many cities, high page budgets, or Detail fetch on for thousands of listings) take proportionally longer; if a run approaches your Apify timeout setting before finishing, raise the run's Timeout (Actor input "Run options") or split the city list across more than one run - whatever was collected before a timeout is still saved to the dataset.
FAQ
How much does it cost?
You pay per listing returned. Turning on Detail fetch adds a small surcharge per detail page actually fetched, on top of the standard per-result pricing. The Pricing tab on the actor page shows current rates. Use Max total listings to cap the cost of any run.
Is it legal to scrape Batdongsan.com.vn?
This actor collects only publicly available listing data. You are responsible for how you use it: follow batdongsan.com.vn's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing photos and agent contact details may carry their own rights or privacy considerations, so review them before republishing.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new and updated listings for that search, and unchanged ones are not billed. Only turn on Emit expired if you also want gone-missing listings reported, and only then can a later reappearance ever come back as REAPPEARED; with it off for the whole life of that tracked search (and Emit unchanged off), you will only ever see NEW and UPDATED.
Can I filter by price, area, bedrooms or direction, and sort the results?
Yes, in Search mode: set Min/Max price, Only negotiable-price listings, Min/Max area, Bedrooms, House-facing direction, Keyword and Sort by. They are sent to batdongsan.com.vn in the same form its own filter panel uses, so the site does the filtering: only matching listings are fetched and billed, and the sort holds across every page. The site also mixes a carousel of about ten promoted "verified" listings into each results page that ignores your filters; whenever a filter is set, those are dropped so every row matches what you asked for. In URL mode, apply the filters on the site and paste the resulting URL. One limit comes from the site itself: stacking two filter path segments (.../gia-tu-1-ty-den-2-ty/dt-tu-50m2-den-80m2) makes it redirect to the whole-country listing page, which the actor detects and skips with a warning. Use the query-string form (?gtn=1-ty&gcn=2-ty&dtnn=50&dtln=80) to combine filters.
What happens if I paste a URL that isn't a listing or search page?
A broker, developer or company profile page (or an agent's guru.batdongsan.com.vn profile link) isn't a listing or a search, so it can never produce dataset rows. The actor recognizes these shapes up front and skips them with a logged warning and zero proxy spend - it never burns a retry loop treating them as an empty search result.
Why is agentPhone often null?
The agent's phone number is revealed on the site only after a click, not printed directly in the page, so it is frequently null. agentName, agentId and agentProfileUrl are still captured whenever the site shows them, and the profile URL page lists the agent's other contact details.
Why did my run fail instead of returning an empty dataset?
Most read problems show up as an empty or partial dataset with a warning in the log, not a failed run: if a page is rejected on every attempt, that search stops early and whatever was collected before that point is still returned. Pasting an unsupported URL (a broker/company page) is the same - a logged warning, not a failure. A run fails outright only in narrower cases: an unrecognized location name in Search mode's locations (fixed by using one of the site's 63 provinces - see the error message for the exact spelling it expects), a property type the site does not have for the chosen Buy/Rent direction (see the property-type table), a minimum price or area higher than the maximum, a resumeFromRunId that does not point at a run or dataset your Apify account can read, turning on Incremental mode together with resumeFromRunId when that search already has tracked Incremental state, or, rarely, when the proxy configuration itself cannot be set up at all. In every other case, "no listings today" simply means the search had none, or the exit IP needs a re-run.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more real estate data?
Pair this actor with these related scrapers from the same team:
| 🏠 SeLoger Scraper Scrape SeLoger.com properties for sale and rent. Search by location with price, room and... | 🏠 Immowelt.de Scraper Scrape detailed property listings from Immowelt.de into clean structured data. Extract... |
| 🏠 Otodom.pl Scraper Scrape Otodom.pl properties for sale and rent, including apartments, houses, plots... | 🏠 Zumper Scraper Scrape Zumper.com rental listings: apartments, houses, condos and rooms. Pulls 60+ fields... |
| 🏠 Property24 From $1/1K. Extract structured property listings from Property24, South Africa’s largest... | 🏠 DotProperty & Lamudi Real Estate Scrape Philippines property listings from dotproperty.com.ph and lamudi.com.ph with exact... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.