# Vietnam Property Listings Scraper - Batdongsan.com.vn (`jungle_synthesizer/batdongsan-vietnam-property-scraper`) Actor

Scrapes property listings from Batdongsan.com.vn, Vietnam's largest real estate portal. Returns price, location, bedrooms, legal status, and photos for sale and rental listings nationwide.

- **URL**: https://apify.com/jungle\_synthesizer/batdongsan-vietnam-property-scraper.md
- **Developed by:** [BowTiedRaccoon](https://apify.com/jungle_synthesizer) (community)
- **Categories:** Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 record scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Vietnam Property Listings Scraper — Batdongsan.com.vn

Scrapes property listings from [Batdongsan.com.vn](https://batdongsan.com.vn), Vietnam's largest real estate portal. Returns price, location, bedrooms, legal status, agent contact, and photos for sale and rental listings across the whole country — well over 170,000 active listings at last count.

***

### Vietnam Property Listings Scraper Features

- Parses Vietnamese price shorthand ("36,5 tỷ", "688 tr/m²") into clean numbers, not raw text you'd have to decode yourself.
- Covers both sale and rental listings, nationwide, by default.
- Returns the fields most bundled scrapers drop: facade width, house-facing direction, and legal status (sổ đỏ / sổ hồng).
- Pulls the full listing description, agent name, and every listed photo.
- Resolves latitude and longitude for listings that carry a map pin.
- Filters by transaction type or city with two input fields — no extra setup.

***

### Who Uses Vietnam Property Listing Data?

- **Real estate investors** — track price-per-square-meter across districts before committing capital.
- **Proptech startups** — feed listing data into a valuation model or a comparison tool.
- **Relocation and expat services** — build a rental inventory feed for clients moving to Ho Chi Minh City or Hanoi.
- **Market researchers** — measure inventory and pricing across Vietnam's property market, not just one city.
- **Lead generation teams** — collect agent names and listings tied to active inventory.

***

### How Vietnam Property Listings Scraper Works

1. Pick a transaction type (sale, rent, or both) and, optionally, a city. Leave both blank and it covers all of Vietnam.
2. The scraper works through the listing pages for each selection, collecting every listing it finds.
3. Each listing gets its own pass to pull the fields the search-results page never carries — bedrooms, direction, legal status, the full description, and photos.
4. Results land in your dataset as they're found, so stopping a run early still keeps everything collected up to that point.

***

### Input

```json
{
  "maxItems": 100,
  "transactionTypes": ["sale"],
  "provinces": ["tp-hcm"]
}
```

| Field              | Type    | Default               | Description |
|--------------------|---------|-----------------------|---------------------------------------------------------------------------------------------------------------------------|
| `maxItems`         | Integer | `10`                  | Maximum number of listings to return. |
| `transactionTypes` | Array   | `["sale", "rent"]`    | Sale and/or rental listings. Leave empty to crawl both. |
| `provinces`        | Array   | `[]` (all of Vietnam) | Limit the crawl to specific cities: `tp-hcm`, `ha-noi`, `da-nang`, `binh-duong`, `dong-nai`. Leave empty for the whole country. |
| `resumeCursor`     | String  | —                     | Cursor from a previous run's Output. Leave empty for a fresh crawl. |

Renting instead of buying? Set `"transactionTypes": ["rent"]` and everything else works the same way.

***

### Resuming a large crawl

Every run emits a `resumeCursor` in its Output. If a large crawl stops before it finishes — because it hit `maxItems`, your spend cap (`maxTotalChargeUsd`), or was aborted — start a new run with **the same input** plus that `resumeCursor` to continue from where it left off. The crawl resumes from the queued work the previous run didn't reach.

- You are **not re-charged** for records the earlier run already delivered.
- Resume within your account's run-retention window — on the free tier, roughly your 10 most recent runs. Once the source run is pruned, its `resumeCursor` is no longer valid.
- `resumeCursor` is opaque — supply it unmodified.

***

### Vietnam Property Listings Scraper Output Fields

```json
{
  "listing_id": "46353677",
  "listing_url": "https://batdongsan.com.vn/cho-thue-can-ho-chung-cu-duong-nguyen-huu-canh-phuong-22-vinhomes-central-park/trong-3pn-2-ban-cong-116m2-gia-cuc-tot-27-7tr-view-song-huong-mat-ca-ngay-pr46353677",
  "title": "Trống 3PN 2 ban công, 116.4m2, giá cực tốt 27,7 triệu/tháng view sông, hướng mát cả ngày",
  "price": 27700000,
  "price_text": "27,7 triệu/tháng",
  "currency": "VND",
  "price_per_sqm": null,
  "transaction_type": "rent",
  "property_type": "Căn hộ chung cư (Apartment)",
  "address": "Vinhomes Central Park, Đường Nguyễn Hữu Cảnh, Phường 22, Quận Bình Thạnh, Hồ Chí Minh",
  "district": "Bình Thạnh",
  "city": "Hồ Chí Minh",
  "latitude": 10.79493,
  "longitude": 106.7207,
  "bedrooms": 3,
  "bathrooms": 2,
  "area_sqm": 116.4,
  "facade_width": null,
  "direction": "Tây - Bắc",
  "legal_status": null,
  "description": "3PN - 2WC - 2 ban công view sông cực chill...",
  "agent_name": "Mỹ Chi Vinhomes",
  "agent_phone": "0399 680 ***",
  "image_urls": [
    "https://file4.batdongsan.com.vn/resize/1275x717/2026/09/28/20260928133438-4ee0_wm.jpg",
    "https://file4.batdongsan.com.vn/resize/1275x717/2026/09/28/20260928133438-3d9e_wm.jpg"
  ],
  "listing_date": "2026-09-28T13:45:55.9770000+07:00"
}
```

| Field              | Type   | Description |
|--------------------|--------|-------------------------------------------------------------------------------------------------|
| `listing_id`       | String | Batdongsan's internal listing ID. |
| `listing_url`      | String | Canonical link to the listing. |
| `title`            | String | Listing title as written by the poster. |
| `price`            | Number | Parsed asking price in VND. `null` when the listing says "negotiable" instead of naming a price. |
| `price_text`       | String | The raw price text as shown on the page (e.g. `"36,5 tỷ"` or `"Thỏa thuận"`). |
| `currency`         | String | Always `VND`. |
| `price_per_sqm`    | Number | Parsed price per square meter in VND, where the listing states one. |
| `transaction_type` | String | `sale` or `rent`. |
| `property_type`    | String | Property type — house, apartment, land, shophouse, and similar categories. |
| `address`          | String | Full street address as listed. |
| `district`         | String | District or ward. |
| `city`             | String | City or province. |
| `latitude`         | Number | Latitude, where the listing carries a map pin. |
| `longitude`        | Number | Longitude, where the listing carries a map pin. |
| `bedrooms`         | Number | Number of bedrooms, where listed. |
| `bathrooms`        | Number | Number of bathrooms, where listed. |
| `area_sqm`         | Number | Floor area in square meters. |
| `facade_width`     | Number | Facade width in meters (mặt tiền) — houses and shophouses only. |
| `direction`        | String | House-facing direction (hướng nhà), where listed. |
| `legal_status`     | String | Legal status (sổ đỏ / sổ hồng), where the listing states one. |
| `description`      | String | Full listing description as written by the poster. |
| `agent_name`       | String | Listing agent's name. |
| `agent_phone`      | String | Agent phone number as displayed. Batdongsan partially masks the number on the page — that's what you get back. |
| `image_urls`       | Array  | URLs of every listed photo. |
| `listing_date`     | String | Listing publish date, ISO 8601. |

***

### FAQ

#### How do I scrape Batdongsan.com.vn?

Vietnam Property Listings Scraper needs no account and no API key for Batdongsan. Run it with the defaults and it crawls every sale and rental listing in the country; add `provinces` or `transactionTypes` to narrow it down.

#### What data can I get from Batdongsan property listings?

Price, area, bedrooms, bathrooms, address, legal status, agent contact, photos, and more — 25 fields per listing, covering both the search-results summary and the detail-page specifics that summary never carries.

#### Can I filter by sale or rental listings?

Yes. Set `transactionTypes` to `["sale"]`, `["rent"]`, or leave it as the default `["sale", "rent"]` to get both.

#### Does this cover all of Vietnam, or just Ho Chi Minh City?

All of Vietnam by default. `provinces` narrows the crawl to specific cities when you don't need the whole country.

#### Is the agent's phone number included?

Batdongsan displays agent phone numbers partially masked (e.g. `"0973 034 ***"`) until a visitor clicks to reveal them. The scraper returns exactly what's on the page — the full number isn't there to collect.

***

### Need More Features?

Need custom fields, filters, or a different target site? [File an issue](https://console.apify.com/actors/issues) or get in touch.

### Why Use Vietnam Property Listings Scraper?

- **Nationwide by default** — most listing scrapers make you pick a city first. This one covers the whole country unless you narrow it.
- **Vietnam-specific fields** — facade width, house direction, and legal status are exactly the fields a generic multi-country bundle strips out to keep its schema uniform.
- **Parsed, not raw** — price and area come back as numbers you can sort and filter on, not strings you have to parse yourself.

# Actor input Schema

## `sp_intended_usage` (type: `string`):

What will this data feed? E.g. lead lists, KYB checks, price tracking.

## `sp_improvement_suggestions` (type: `string`):

Provide any feedback or suggestions for improvements.

## `sp_contact` (type: `string`):

We'll personally help with your use case. No spam.

## `resumeCursor` (type: `string`):

Leave empty for a fresh crawl. To CONTINUE a previous run where it stopped — without paying again for records you already received — paste the `resumeCursor` value from that run's Output (the run's OUTPUT key). Resume promptly: the previous run's data expires with your account's retention window (free tier: your ~10 most recent runs).

## `maxItems` (type: `integer`):

Maximum number of items to scrape

## `transactionTypes` (type: `array`):

Sale and/or rental listings. Leave empty to crawl both.

## `provinces` (type: `array`):

Optional province/city filter. Leave empty to crawl all of Vietnam.

## Actor input object example

```json
{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "maxItems": 10,
  "transactionTypes": [
    "sale",
    "rent"
  ],
  "provinces": []
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("jungle_synthesizer/batdongsan-vietnam-property-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("jungle_synthesizer/batdongsan-vietnam-property-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "maxItems": 10
}' |
apify call jungle_synthesizer/batdongsan-vietnam-property-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jungle_synthesizer/batdongsan-vietnam-property-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/eHMuYBPUjkOPTFE3r/builds/qhQUacgQsKIFBrFFg/openapi.json
