# Hipflat Scraper \[$0.80/1k💰] Thailand Property & Projects (`abotapi/hipflat-scraper`) Actor

Scrape hipflat.co.th property listings and new-project data across Thailand: price, price per m², beds, baths, area, floor, type, address, GPS, amenities, photos, full description and project facts. Buy and rent; search and URL modes; project records with available units; sorts and filters.

- **URL**: https://apify.com/abotapi/hipflat-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Real estate
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 property or project results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Hipflat Scraper — Thailand Property & Projects

Scrape property listings and new-project market data from **hipflat.co.th** across Thailand — buy and rent, condos, houses, villas, townhouses, apartments, land, offices, commercial and hotels — with rich, structured fields per record.

Two ways to run it, one clean dataset out.

### What you get

Per **listing** record:

- Identity: `listingId`, `url`, `title`, `dealType` (buy/rent), `propertyType`
- Price: `price`, `priceText`, `pricePerArea`, `currency`
- Size & layout: `bedrooms`, `bathrooms`, `area` (m²), `floor`
- Location: `address`, `latitude`, `longitude`
- Content: `description`, `images` (every photo), `mainImage`, `imageCount`
- With **Fetch details** on: full amenity list, floor, price per m², GPS, the full untruncated description, the parent-project facts (`projectName`, `projectDetails`) and the advertiser (`agencyName`, `agencyLogo`)

Per **project** record (project directories and project pages):

- `projectName`, `projectSlug`, `address`, `latitude`, `longitude`
- `saleFromPrice`, `rentFromPrice`, `availableUnitsCount`
- `units` (available units with price, price per m², layout), `amenities`, `facilities`, `nearby`, full `description`

Common rival field names are mirrored as aliases (e.g. `record_type`, `record_id`, `price_min`, `image_urls`, `coordinates`, `property_types`) so existing pipelines keep working.

### Modes

- **Search** — pick a deal type (buy/rent), property type and location, add optional price / bedroom / bathroom / area filters and a sort order. Location accepts English names for major provinces (Bangkok, Pattaya, Phuket, Chiang Mai, Chonburi, …) or a Thai province name; leave it empty to search all of Thailand.
- **URL** — paste any hipflat URL: a listing search (`/ขายคอนโด/กรุงเทพฯ`), a single listing (`/ads/...`), a project directory (`/thailand-projects/...`) or a project page (`/projects/...`). The actor detects the shape and scrapes it. Filter fields are ignored in this mode.

### Filters & sorting (search mode)

- Deal type: buy, rent
- Property type: condo, house, villa, townhouse, apartment, land, office, commercial, hotel
- Price range (`minPrice` / `maxPrice`, THB)
- Bedrooms, minimum bathrooms, area range (`minArea` / `maxArea`, m²)
- Sort: relevance, price ↑/↓, bedrooms ↑/↓, area ↑/↓, newest first

### Limits

- **Max items** — the single run cap (default 20; set 0 for unlimited).
- **Max pages** — optional per-search page ceiling; unlimited by default, defers to Max items.

### Output caps only

The actor manages its own reliability internally. The only limits you set are the output caps above — there are no concurrency or retry knobs to tune.

### Input parameters

| Field | Description |
| --- | --- |
| resumeFromRunId | Continue one specific interrupted run — paste a previous run or dataset ID and this run skips records already collected there. |
| incrementalMode | Turn this on for daily/recurring monitoring of the same search. The first run returns everything as `NEW`; later runs return only `NEW`/`UPDATED`/`REAPPEARED` by default. |
| stateKey | Optional. Name a monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty for an automatic key derived from the search/URL and detail settings. |
| emitUnchanged | Off by default. Also return `UNCHANGED` records (incremental mode) — returns and bills extra rows you already have. |
| emitExpired | Off by default. Also return `EXPIRED` records no longer found since the last run (incremental mode) — only produced after a complete, non-resumed, non-capped scan. Returns and bills extra synthetic rows. |

### Resume & recurring updates

Two different features, both optional:

- **Resume** (`resumeFromRunId`) continues one specific interrupted crawl: paste a previous run or dataset ID and this run skips every record already collected there. No state is kept between unrelated runs.
- **Incremental mode** (`incrementalMode`) is for scheduling this same search daily/weekly and getting only what changed. The actor remembers its own baseline for the search (keyed by `stateKey`, or an automatic key derived from the search/URL and detail settings) and classifies every record as `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED`, or `EXPIRED`. By default only `NEW`/`UPDATED`/`REAPPEARED` records return; turn on `emitUnchanged`/`emitExpired` to also get those (both return — and bill — extra rows). `EXPIRED` records are only produced after a run that scanned its full search with no cap hit and no Resume.

Output then carries `changeType`, `changedFields`, `firstSeenAt`, and `lastSeenAt`. Change detection ignores `scrapedAt` (a per-run timestamp) and every field that only exists once the optional detail fetch has run (e.g. `floor`, `pricePerArea`, the full `description`, GPS coordinates, the advertiser and parent-project facts on listings; `description`, `availableUnitsCount`, `facilities`, `units`, `nearby`, `marketStats`, `developer` and GPS coordinates on projects) — none of those can be compared across runs where detail fetching was toggled on or off. Price, area, bedrooms/bathrooms, and every other core field **do** trigger `UPDATED` when they change.

### Send results into your apps (MCP connectors)

Optionally pipe each record into the apps you already use via Model Context Protocol connectors — Notion, Linear, Airtable or Apify. Authorize a connector once under **Apify → Settings → Integrations**, then select it in the input. The connector receives a condensed, human-readable summary per item; the full record always stays in the dataset.

### Plan requirement

Runs on any Apify plan, including the free tier. hipflat serves results only to a Thailand connection, so keep the default Thailand proxy for reliable results. On a limited or free connection the site may return no results; the input warns you when that is the case.

### Pricing

Pay per event: one charge when the run starts, one per record pushed, and — only when **Fetch details** is on — one per listing whose detail page is fetched. Runs with detail fetching off are billed only for the records they return.

# Actor input Schema

## `mode` (type: `string`):

search = build searches from a location + filters. url = paste hipflat listing searches, single listing pages, project directories or project pages.

## `locations` (type: `array`):

Locations to search, e.g. Bangkok, Pattaya, Phuket, Chiang Mai, Chonburi. English names for major provinces are mapped automatically; a Thai province name also works. Leave empty to search all of Thailand. Only applies when mode = search.

## `dealType` (type: `string`):

Buy or rent. Only applies when mode = search.

## `propertyType` (type: `string`):

Property category to search. Only applies when mode = search.

## `minPrice` (type: `integer`):

Minimum price in Thai baht (for-sale total, or monthly rent). Only applies when mode = search.

## `maxPrice` (type: `integer`):

Maximum price in Thai baht (for-sale total, or monthly rent). Only applies when mode = search.

## `bedrooms` (type: `integer`):

Number of bedrooms (0 = studio, 4 = 4 or more). Only applies when mode = search.

## `minBathrooms` (type: `integer`):

Minimum number of bathrooms. Only applies when mode = search.

## `minArea` (type: `integer`):

Minimum usable area in square metres. Only applies when mode = search.

## `maxArea` (type: `integer`):

Maximum usable area in square metres. Only applies when mode = search.

## `sortBy` (type: `string`):

Result ordering. Only applies when mode = search.

## `urls` (type: `array`):

Paste one or more hipflat URLs. Supported: listing searches (e.g. /ขายคอนโด/กรุงเทพฯ), single listing pages (/ads/...), project directories (/thailand-projects/...) and project pages (/projects/...). Filter fields above are ignored in this mode.

## `includeProjectDetails` (type: `boolean`):

When scraping a project directory URL, also fetch each project page to add its available units, facilities, developer, GPS and full description. Adds one request per project and is billed one detail-enrichment event per enriched project.

## `fetchDetails` (type: `boolean`):

Fetch each listing page to add floor, price per m², GPS, the full amenity list, the full untruncated description and the parent-project facts. The search results already include title, price, bedrooms, bathrooms, area, property type, description and photos, so leave this off for the leanest, cheapest run. Adds one request per listing.

## `maxPages` (type: `integer`):

Optional safety stop on how many result pages to read per location/URL (about 30 records per page). This does NOT cap the run: leave it empty (or 0) for unlimited pages and the run is bounded solely by Max items. Set a value only if you also want a per-search page ceiling.

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

The run's single hard cap: maximum total records across all searches. Defaults to 20. Set 0 for unlimited. This is the only default limit; Max pages is unlimited by default and defers to this.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large crawl without returning or charging for records already collected there. Use this after an interrupted run. For recurring monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching records as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED records. Turn on "Emit unchanged" or "Emit expired" only when you also want those records returned (and billed). State is kept separately for each search/URL and detail-mode setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search/URL and detail settings — different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return records that have not changed since the last run, marked UNCHANGED. This returns — and bills — extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return records that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search — not when Max items capped it or when Resume was used. This returns — and bills — extra synthetic rows, so leave it off unless you need expiry tracking.

## `proxy` (type: `object`):

A Thailand residential proxy is required: hipflat serves results only to Thailand residential connections. The actor defaults the country to Thailand.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify, Settings, Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON; the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "locations": [
    "Bangkok"
  ],
  "dealType": "buy",
  "propertyType": "condo",
  "sortBy": "relevance",
  "urls": [
    "https://www.hipflat.co.th/ขายคอนโด/กรุงเทพฯ"
  ],
  "includeProjectDetails": false,
  "fetchDetails": false,
  "maxItems": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "TH"
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "locations": [
        "Bangkok"
    ],
    "urls": [
        "https://www.hipflat.co.th/ขายคอนโด/กรุงเทพฯ"
    ],
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "TH"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/hipflat-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 = {
    "mode": "search",
    "locations": ["Bangkok"],
    "urls": ["https://www.hipflat.co.th/ขายคอนโด/กรุงเทพฯ"],
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "TH",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/hipflat-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "mode": "search",
  "locations": [
    "Bangkok"
  ],
  "urls": [
    "https://www.hipflat.co.th/ขายคอนโด/กรุงเทพฯ"
  ],
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "TH"
  }
}' |
apify call abotapi/hipflat-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/hipflat-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/iQVIIizvMP0pFC9jq/builds/1e0XYjG4fVoEj0jcN/openapi.json
