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
26
Total users
7
Monthly active users
9 days 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. Search mode (listing type, property type and city) for a quick setup, or URL mode to paste a fully filtered batdongsan.com.vn link, including price, area or bedroom filters applied in your browser.
- 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.
- Resume a large pull. A separate resume feature continues one interrupted run by skipping listings it already saved, so you never pay 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 (derived from priceValue / area when both are known) |
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", ...] |
scrapedAt | "2026-05-26T00:00:00.000Z" |
Each record also carries ward, districtId, cityCode, balconyDirection, furniture, agentId, agentPhone (usually null, see FAQ), agentPhoneToken, agentAvatar, description and listingCode. 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.
How to Use
- Pick a mode:
search(listing type, property type and city) orurl(paste one or more batdongsan.com.vn search links, useful when you need price, area or bedroom filters applied in your browser first). - For Search mode, choose Buy or Rent, a property type and one or more cities. For URL mode, paste the search URLs.
- 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, multiple cities, fast (no detail pages):
{"mode": "search","listingType": "Buy","propertyType": "PrivateHouse","locations": ["ha-noi", "da-nang", "can-tho"],"maxPages": 3,"fetchDetails": false}
Search mode, rentals with a listing cap:
{"mode": "search","listingType": "Rent","propertyType": "Apartment","locations": ["tp-hcm"],"maxPages": 5,"maxListings": 100}
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}
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.
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), and the detail-fetch setting (or on stateKey, when set). 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, and not a resume). 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.
Two fields are excluded from the change comparison because they vary on every scrape regardless of whether the listing itself changed: scrapedAt (the crawl timestamp) and agentPhoneToken (a token the site mints fresh per page view, not a stable per-listing value). Every other field, including priceValue, area, listingTier, postedDate and specs, is real data and correctly triggers UPDATED when it changes.
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) or Rent (Cho thuê). Search mode only. |
propertyType | string | Apartment | All, Apartment, PrivateHouse, TownHouse, Villa, Land, ProjectLand, Office, or Warehouse. Search mode only. Office exists rent-only; Land, Project land and Warehouse exist buy-only on the site. |
locations | array | none (nationwide), UI prefill: ["tp-hcm"] | Province slugs (tp-hcm, ha-noi, da-nang, ...) or common names (Ho Chi Minh, Hanoi), slugified automatically. Left empty, the search covers the whole country. Search mode only. |
urls | array | none, UI prefill: one sample URL | Full batdongsan.com.vn search URLs. URL mode only. A trailing /pN starts that URL at page N. Left empty in URL mode, the run returns an empty dataset instead of guessing a URL. |
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), 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). Adds and bills extra synthetic rows. |
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"],"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. If your plan does not include residential proxy access, 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.
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.
Why can't I filter by price, area or number of bedrooms in Search mode?
Batdongsan.com.vn applies those filters through an on-page form rather than the address bar, so they cannot be set as Search-mode fields. Apply them on the site in your browser, then paste the resulting URL in URL mode.
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. A run fails outright only in narrower cases: 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:
| 🏠 iProperty MY Scraper Scrape property listings from iProperty.com.my for sale or rent: price, PSF, bedrooms... | 🏠 View.com.au Scraper Scrape View.com.au properties for sale, rent and recently sold across Australia. Search... |
| 🏠 591.com.tw Scraper Extract property listings from 591房屋交易網 591.com.tw, Taiwan’s largest real estate... | 🏠 ImmobilienScout24.de Scraper $0.9💰/1K for Gold discount. Extract property listings from immobilienscout24.de... |
| 🏠 Trulia Scraper Scrape Trulia.com property listings by location, filters, or URL. Extract prices, beds... | 🏷️ Tutti.ch Scrape listings from tutti.ch across all 23 categories, including vehicles, property... |
💬 Support & custom scrapers
- 🐞 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 abotapi@proton.me or message Telegram @abotapi.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.