# Zigbang Scraper - Korea Property Listings, Rent & Agents (`abotapi/zigbang-property-scraper`) Actor

Scrape Zigbang property listings across Seoul and South Korea, including one-room, villa and officetel rentals and sales. Extract deposits, monthly rent, prices, floor plans, property details, subway access, locations and agent contacts.

- **URL**: https://apify.com/abotapi/zigbang-property-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Features:** Limited permissions
- **Stats:** 3 total users, 1 monthly users, 97.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 listing records

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

## Zigbang Scraper: Oneroom, Villa and Officetel Listings with Deposits, Rent and Agent Contacts

Zigbang Scraper turns Zigbang, one of South Korea's largest home rental marketplaces, into structured listing data. Search by a subway station, neighbourhood or district, or paste listing links or bare ids directly, and pull oneroom (원룸), villa (빌라) and officetel (오피스텔) listings with deposits, monthly rent, size, floor, address and coordinates. Turn on detail enrichment for the full description, in-room options, elevator, bathroom count, direction, parking, move in date, nearby subway lines and the listing's public agent contact block. Schedule the same search to get only new and changed listings, then export to JSON, CSV or Excel, or read the results straight through the API.

### Why This Scraper?

- **Two ways in.** Resolve a Korean location keyword (a subway station, neighbourhood or district) and read every listing around it, or paste listing URLs or bare numeric ids and read exactly those.
- **Three listing verticals.** Oneroom, villa and officetel, each with server side deal type, deposit, rent, maintenance fee and size filters, so a filtered run reads only the matching slice.
- **Detail enrichment on demand.** Switch on a per listing pass for the full description, options, elevator, bathroom count, direction, parking, move in date, nearby subway lines and the agent's public office contact block, and pay the surcharge only for listings you actually get back.
- **Resume picks up where you left off.** Continue an interrupted pull without paying again for records a previous run already collected.
- **Incremental mode for recurring searches.** Schedule the same search again and again and get every listing classified NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED against your own stored baseline.
- **A refused run fails loudly.** If nothing could be read at all, the run stops with a clear message instead of quietly returning an empty dataset, so "no listings matched" is never confused with "the connection failed."
- **Stable, flat records.** Every listing ships as one flat row with a consistent field contract, ready for JSON, CSV or Excel export, or the API.

### Use Cases

- **Renters and relocation services:** compare oneroom, villa and officetel options around a subway line before booking viewings.
- **Brokerage and property management tools:** refresh a district's market snapshot on a schedule and act only on the listings that actually changed.
- **Market research and pricing analysts:** track deposit and monthly rent ranges by neighbourhood over time.
- **Real estate apps and aggregators:** pull structured listing data, including public agent contacts, behind your own API or app.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `itemId` | `12345678` |
| `url` | `"https://www.zigbang.com/home/oneroom/items/12345678"` |
| `serviceType` | `"원룸"` (oneroom) |
| `title` | `"Newly painted one room with a private entrance"` |
| `salesType` | `"월세"` (wolse, deposit plus monthly rent) |
| `deposit` / `rent` | `1000` / `55` (만원, i.e. 10,000,000 / 550,000 KRW) |
| `manageCost` | `"60000"` (maintenance fee, in KRW) |
| `sizeM2` / `exclusiveAreaM2` | `21.4` / `18.6` |
| `floor` / `buildingFloor` | `"4층"` / `"6"` |
| `registeredAt` | `"2026-08-20T09:15:00+09:00"` |
| `isNew` | `true` |
| `tags` | `["역세권", "반려동물가능"]` |
| `district` / `neighborhood` | `"샘플구"` / `"샘플동"` |
| `thumbnailUrl` | `"https://ic.zigbang.com/ic/items/12345678/1.jpg"` |
| `description` | `"Recently renovated one room with a bright south facing window."` (with detail enrichment) |
| `options` | `["에어컨", "냉장고", "세탁기"]` (with detail enrichment) |
| `elevator` / `bathroomCount` | `true` / `1` (with detail enrichment) |
| `roomDirection` / `parkingAvailableText` | `"남향"` / `"가능"` (with detail enrichment) |
| `moveinDate` / `viewCount` | `"즉시입주"` / `312` (with detail enrichment) |
| `subways` | `[{"name": "샘플역", "lines": "2호선"}]` (with detail enrichment) |
| `agent` | office name, phone, address and registry number (with detail enrichment) |
| `changeType` / `changedFields` | `"UPDATED"` / `["rent"]` (incremental mode only) |
| `firstSeenAt` / `lastSeenAt` | ISO timestamps (incremental mode only) |

The `agent` object carries the listing's public business contact block exactly as the source publishes it on the listing page: an office name, a name, an office phone, an office address and a registry number. It, along with `description`, `options`, `elevator`, `bathroomCount`, `roomDirection`, `parkingAvailableText`, `moveinDate`, `viewCount` and `subways`, ships only when detail enrichment (`fetchDetails`) is on. `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt` ship only when incremental mode is on; without it, records look exactly like the rows above minus those four fields.

### How to Use

1. Pick a **mode**: `search` (resolve a location keyword) or `url` (paste listing links or bare ids).
2. In search mode, choose the **listing type** and narrow with deal type, deposit, rent, maintenance fee and size filters. URL mode ignores these; each pasted listing is read as it is.
3. Turn on **Fetch listing details** if you need the description, options and agent contact block, and set **Max items** to control run size and cost.
4. Click **Start**, then download the dataset as JSON, CSV or Excel, or read it through the API.

**Search around a subway station:**

```json
{
  "mode": "search",
  "query": "강남역",
  "serviceType": "oneroom",
  "subwayRadiusKm": 2,
  "maxItems": 50
}
```

**Narrow with deal type and price bounds (bounds are in 만원 units, i.e. 10,000 KRW):**

```json
{
  "mode": "search",
  "query": "홍대입구",
  "serviceType": "villa",
  "salesTypes": ["wolse"],
  "depositMax": 2000,
  "rentMax": 100,
  "maxItems": 50
}
```

**Search with detail enrichment on:**

```json
{
  "mode": "search",
  "query": "마포구",
  "serviceType": "officetel",
  "fetchDetails": true,
  "maxItems": 30
}
```

**Paste listing URLs or bare ids (url mode):**

```json
{
  "mode": "url",
  "urls": ["https://www.zigbang.com/home/oneroom/items/12345678", "12345679"]
}
```

URL mode accepts these shapes: `m.zigbang.com` or `www.zigbang.com` listing pages for oneroom, villa and officetel (a query string is ignored), the `/v3/items/<id>` API form, bare numeric ids, and a `/v3/search?q=<keyword>` API URL, which is read like search mode with that keyword. Map pages (`/home/oneroom/map`) are not supported; each unsupported entry is skipped with a warning in the run log. A keyword that matches no location fails with an error instead of returning nothing.

| URL shape | Example |
| --- | --- |
| Listing page, oneroom | `https://m.zigbang.com/home/oneroom/items/12345678` |
| Listing page, villa | `https://m.zigbang.com/home/villa/items/12345678` |
| Listing page, officetel | `https://m.zigbang.com/home/officetel/items/12345678` |
| Listing page with query string | `https://m.zigbang.com/home/oneroom/items/12345678?foo=1` |
| Numeric id API form | `https://<api host>/v3/items/12345678` |
| Keyword search API form | `https://<api host>/v3/search?q=%EA%B0%95%EB%82%A8%EC%97%AD` |

**Behaviour change:** in URL mode, leaving the `urls` field untouched now scrapes one example per shape above (duplicates collapse to one listing each) instead of failing. Replace the list with your own URLs for real runs.

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/zigbang-property-scraper").call(run_input={
    "mode": "search", "query": "강남역", "serviceType": "oneroom", "maxItems": 20
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(listing["title"], listing["deposit"], listing["rent"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/zigbang-property-scraper').call({
  mode: 'search', query: '강남역', serviceType: 'oneroom', maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### More fields per listing

Rows also carry won-denominated twins of the prices (`deposit_krw`, `rent_krw`, `currency`, `rent_per_m2_krw`), `address_region`, and, with Fetch details on, `images`, `image_count`, `pnu`, `bjd_code`, `building_id`, `danji_name`, `manage_cost_includes`, `approve_date`, `direction_criterion`, `address_jibun`, `updated_at` and flat `agent_name`, `agent_phone`, `agent_address`. Billing is unchanged: the same events, on the same rows, as before.

#### Search by coordinate, search URL and sort

Besides a keyword, search mode can read around a coordinate (`lat` and `lng`, with `precision` for cell size and `ring` for neighbouring cells) or from pasted `searchUrls` (apis.zigbang.com listing list URLs; price and size filters inside the URL are kept). `sortBy` orders the results after all are read (newest, price or size, ascending or descending), so **Max items** then keeps the first N of the ordered list.

#### Incremental mode and the changes view

With incremental mode on, `ignoreFieldsForChanges` lists fields or dotted paths (for example `agent.phone`) that are left out of change detection and `changedFields` but stay in the output. The run's **Output** tab also offers a **Changes (incremental)** dataset view showing `changeType`, `changedFields` and the timestamps. The first incremental run after this release re-baselines existing listings silently, so the new fields do not make every tracked listing show up as UPDATED.

#### How search scope and Max items interact

One search reads one location scope: a subway radius, or a single geohash cell (a 4 character cell is roughly 39 by 20 km). A dense central area can resolve to thousands of pins in that one scope, so listing cards are then read in batches of up to 15 and the run stops as soon as **Max items** pushed records is reached. Raise **Max items**, or set it to `0` for no limit, to read the whole scope. To cover a whole city, run separate searches per district rather than one very wide scope.

#### Resume and recurring updates, two different mechanisms

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset id and the actor skips pushing every listing id already collected there, whether that run used search or url mode. It still reads through the same search scope again, so a repeated search is read in full, but only the not yet seen listings count against **Max items** and get billed.
- **Incremental mode** (`incrementalMode`) is for scheduling the same search again and again. Each listing is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED`. A run cannot use both mechanisms at once; setting both fails the run before any request is made.
- `EXPIRED` rows are only produced by a scan that read every part of its scope: not resumed, not stopped early by **Max items**, and it saw at least one listing. That same complete scan always updates the stored baseline to mark absent listings gone, whether or not `emitExpired` is on; the flag only controls whether the `EXPIRED` row itself is returned and billed that run. `REAPPEARED` follows from that stored state: a listing a later run sees again after an earlier complete scan marked it gone comes back as `REAPPEARED`, even if `emitExpired` was off on that earlier run.
- `stateKey` names or shares the stored baseline; leave it empty to key the baseline on the search itself. With incremental mode off, output looks exactly like a plain search or url run.

### 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**.

1. Authorize a connector once under **Apify > Settings > Integrations** (Notion, Linear, Airtable, or Apify).
2. Paste the connector's id (or ids) into the **mcpConnectors** input field.
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.
4. **Max items sent to a connector** (`maxNotifyListings`) caps how many of this run's records the export sends, independently of `maxItems`.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third party credentials. Export is best effort: a failing connector never affects the scraped data. Leave `mcpConnectors` empty to skip it.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` resolves a location keyword; `url` reads exactly the listings you paste. |
| `query` | string | `강남역` | Location keyword: a subway station, neighbourhood or district name (search mode). |
| `serviceType` | string | `oneroom` | Listing type: `oneroom` (원룸), `villa` (빌라) or `officetel` (오피스텔). |
| `subwayRadiusKm` | integer | `2` | Radius in km read around a resolved subway station, from 1 to 10. |
| `geohash` | string | (none) | Read this geohash cell directly instead of resolving the keyword (advanced). |
| `lat` | number | (none) | Centre latitude for a coordinate search instead of a keyword; give with `lng`. |
| `lng` | number | (none) | Centre longitude for the coordinate search. |
| `precision` | integer | 6 | Geohash cell size for the coordinate search (4 to 8). |
| `ring` | integer | 1 | Cells read around the centre cell (0 = centre only, 1 = 3x3, up to 3). |
| `searchUrls` | array | (none) | Pasted apis.zigbang.com listing list URLs with a geohash; price and size filters inside the URL are kept. |
| `sortBy` | string | none | Orders results after reading them all: newest, price (deposit plus 100 times monthly rent) or size. Maximum items then keeps the first N of the ordered list. |
| `urls` | array | one example per URL shape (see above) | Listing URLs or bare numeric ids to read directly (url mode). |
| `salesTypes` | array | (none) | Narrow by deal type: `jeonse`, `wolse`, `maede` or `dongi`. Leave empty for all types available on the chosen listing type. |
| `depositMin` | integer | (none) | Lowest deposit to include, in 만원 (10,000 KRW) units. |
| `depositMax` | integer | (none) | Highest deposit to include, in 만원 units. |
| `rentMin` | integer | (none) | Lowest monthly rent to include, in 만원 units. |
| `rentMax` | integer | (none) | Highest monthly rent to include, in 만원 units. |
| `manageCostMin` | integer | (none) | Lowest building maintenance fee to include, in 만원 units. |
| `manageCostMax` | integer | (none) | Highest building maintenance fee to include, in 만원 units. |
| `sizeMin` | number | (none) | Smallest floor size to include, in square meters. |
| `sizeMax` | number | (none) | Largest floor size to include, in square meters. |
| `fetchDetails` | boolean | `false` | Fetch each listing's detail page and bill the detail enrichment surcharge for it. |
| `maxItems` | integer | `20` | Stop after this many listing records. `0` means no limit. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run without paying again for records already collected there. |
| `incrementalMode` | boolean | `false` | Classify listings NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED against a stored baseline of the same search. |
| `stateKey` | string | (none) | Name the incremental baseline yourself; otherwise it is keyed on the search scope. |
| `emitUnchanged` | boolean | `false` | With incremental mode on, also return (and bill) UNCHANGED rows. |
| `emitExpired` | boolean | `false` | With incremental mode on, also return (and bill) EXPIRED rows for listings no longer in scope. |
| `ignoreFieldsForChanges` | array | (none) | Incremental mode only: field names or dotted paths (for example `agent.phone`) left out of change detection and `changedFields`; they stay in the output. `viewCount`, `isNew` and `badges` are always left out of the comparison. |
| `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 records are created. |
| `maxNotifyListings` | integer | `50` | Cap on items sent to a connector export per run. |
| `proxy` | object | `{"useApifyProxy": true}` | Connection settings. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "itemId": 12345678,
  "url": "https://www.zigbang.com/home/oneroom/items/12345678",
  "serviceType": "원룸",
  "title": "Newly painted one room with a private entrance",
  "salesType": "월세",
  "deposit": 1000,
  "rent": 55,
  "manageCost": "60000",
  "sizeM2": 21.4,
  "supplyAreaM2": 25.2,
  "exclusiveAreaM2": 18.6,
  "roomType": "02",
  "roomTypeTitle": "원룸",
  "floor": "4층",
  "buildingFloor": "6",
  "registeredAt": "2026-08-20T09:15:00+09:00",
  "isNew": true,
  "status": true,
  "tags": ["역세권", "반려동물가능"],
  "badges": [],
  "address": "서울시 샘플구 샘플동 12-3",
  "city": "서울시",
  "district": "샘플구",
  "neighborhood": "샘플동",
  "lat": 37.55,
  "lng": 127.0,
  "thumbnailUrl": "https://ic.zigbang.com/ic/items/12345678/1.jpg",
  "description": "Recently renovated one room with a bright south facing window.",
  "options": ["에어컨", "냉장고", "세탁기"],
  "elevator": true,
  "bathroomCount": 1,
  "roomDirection": "남향",
  "parkingAvailableText": "가능",
  "moveinDate": "즉시입주",
  "viewCount": 312,
  "imageUrl": "https://ic.zigbang.com/ic/items/12345678/1.jpg",
  "subways": [{ "name": "샘플역", "lines": "2호선" }],
  "agent": {
    "name": "김민수",
    "officeName": "샘플공인중개사사무소",
    "officePhone": "02-1234-5678",
    "officeAddress": "서울시 샘플구 샘플로 10",
    "officeRegNumber": "12345-2026-00001"
  },
  "changeType": "UPDATED",
  "changedFields": ["rent"],
  "firstSeenAt": "2026-08-01T02:00:00.000Z",
  "lastSeenAt": "2026-09-20T02:00:00.000Z"
}
```

### Plan Requirement

The default connection (Apify datacenter proxy) is enough for the routes this actor reads: the listing API and the mobile listing pages answer a datacenter connection normally, so a run on a paid plan needs no residential proxy and no special setting. The desktop host `www.zigbang.com` refuses datacenter connections, which is why the actor reads listings through the API and mobile hosts and only writes the desktop address into the `url` output field.

On an Apify free plan, which has no proxy access, the actor continues on a backup connection and the log says so:

- Warning: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable. For residential access, upgrade at https://apify.com/pricing or supply your own proxy URLs."
- Info once pages are read that way: "Served over the backup connection (the default connection was unavailable)." The backup connection is part of the run you pay for; there is no separate charge for it.

If nothing at all can be read, the run fails loudly instead of returning an empty dataset, with one of these messages:

- "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required: https://apify.com/pricing. If you entered your own proxy URLs, check that their credentials are valid."
- "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Zigbang pages. Upgrade at https://apify.com/pricing or supply your own proxy URLs, then try again."
- "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

You can supply your own proxy URLs in the **proxy** field at any time.

#### URL shapes

URL mode accepts these shapes, mixed freely in one list:

| Shape | Example | Result |
| --- | --- | --- |
| Mobile listing page | `https://m.zigbang.com/home/oneroom/items/12345678` | That listing |
| Villa or officetel path | `https://m.zigbang.com/home/villa/items/12345678` | That listing (the type segment is ignored) |
| Listing page with query string | `https://m.zigbang.com/home/oneroom/items/12345678?foo=1` | That listing |
| Desktop listing page | `https://www.zigbang.com/home/oneroom/items/12345678` | That listing, read through the mobile and API hosts (not confirmed live from a datacenter connection) |
| Listing API form | `https://apis.zigbang.com/v3/items/12345678` | That listing |
| Bare numeric id | `12345678` | That listing |
| Keyword search API URL | `https://apis.zigbang.com/v3/search?q=...` | Read like search mode for that keyword |

If the **urls** field is left untouched in url mode, it is not empty: it holds one example of each shape above, so the run scrapes those example listings and keyword. Clear the field and enter your own URLs or ids to read only those. A pasted URL that matches no shape is skipped with a warning naming it; if none match, the run fails with a clear message.

### FAQ

#### How much does it cost?

Billing has three events, all shown with their current rates on the **Pricing** tab:

- **Actor start** (`apify-actor-start`): charged when the run starts, one event per GB of run memory (minimum one; the default 512 MB run memory is one event).
- **Listing record** (`apify-default-dataset-item`): once per row actually pushed to the dataset.
- **Detail enrichment** (`detail-enrichment`): once per row that was enriched with **Fetch listing details** and pushed. A listing whose detail page could not be read, or a row that is suppressed (for example an UNCHANGED row in incremental mode), is not charged.

Use **Max items** to cap the cost of any run.

#### Is it legal to scrape Zigbang?

This actor reads only the public listing data Zigbang's own web app serves to every visitor, including agent contact details the source itself publishes on each listing page. Zigbang's robots.txt disallows general crawling outside a small search engine allowlist; this actor is not a search engine crawler, so you are responsible for how you use the data: follow Zigbang's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution.

#### Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Unchanged listings are suppressed and not billed unless you also turn on **Emit unchanged listings**.

#### Can I use Resume and Incremental mode together?

No. They solve two different problems: Resume continues one specific interrupted run, Incremental mode tracks a recurring search over time. Setting both fails the run immediately with a message asking you to use one, not both.

#### Why did my run fail instead of returning an empty dataset?

If every request this run made was refused, the run stops with a clear connection problem message instead of returning an empty dataset, so "no listings matched" is never confused with "the connection failed." A search that genuinely matches nothing, or a pasted id the source does not recognize, still succeeds with an empty result.

#### 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 a summary of results into Notion, Linear or Airtable.

### 🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🧩 <a href="https://apify.com/abotapi/suumo-jp-scraper"><b>Suumo.jp Scraper</b></a><br>Scrape Suumo, Japan's largest property portal: rental rooms with rent, management fees...</td><td>🧩 <a href="https://apify.com/abotapi/naver-land-listings"><b>Naver Land Scraper</b></a><br>Scrape structured property listings from Naver Land map URLs. Extract listing titles...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/quintoandar-scraper"><b>QuintoAndar Scraper</b></a><br>Scrape QuintoAndar property listings across Brazil with 80+ fields, including rent, sale...</td><td>🏠 <a href="https://apify.com/abotapi/realestate-co-nz-scraper"><b>Realestate.co.nz Scraper</b></a><br>Scrape Realestate.co.nz property listings across New Zealand. Search properties for sale...</td></tr>
<tr><td>🏠 <a href="https://apify.com/abotapi/rent-com-scraper"><b>Rent.com Scraper</b></a><br>Extract rental listings from rent.com. Get comprehensive data including monthly rent...</td><td>🏠 <a href="https://apify.com/abotapi/apartments-com-scraper"><b>Apartments.com Scraper</b></a><br>Scrape Apartments.com rentals by city, ZIP, neighborhood, address or URL. Extract rent...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/zigbang-property-scraper/issues/open). 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.

# Actor input Schema

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

search finds listings around a location keyword (a subway station, a neighbourhood, a district). url reads the exact listings you paste.

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

Listing page URLs (m.zigbang.com or www.zigbang.com /home/\<oneroom|villa|officetel>/items/12345678, or the /v3/items/12345678 API form), bare numeric listing ids, or a keyword search API URL (/v3/search?q=...), which is read like search mode. Each pasted listing decides its own scope: the search filters below are ignored here.

## `query` (type: `string`):

A subway station, neighbourhood or district name in Korean, for example 강남역, 홍대입구 or 마포구. The best subway match is used with the radius below; otherwise the area around the resolved location is read.

## `serviceType` (type: `string`):

oneroom = studio/one-room (원룸), villa = low-rise residential (빌라), officetel = live-work (오피스텔).

## `subwayRadiusKm` (type: `integer`):

Used when the location keyword resolves to a subway station: read listings within this many km of the station.

## `geohash` (type: `string`):

Read this geohash cell directly instead of resolving the keyword. A 4-character cell is roughly 39 km by 20 km; leave empty to use the keyword.

## `lat` (type: `number`):

Search around a coordinate instead of a keyword (give lat and lng together). Precision and ring below set the area; a geohash cell, when filled, wins over this.

## `lng` (type: `number`):

Centre longitude for the coordinate search (give with latitude).

## `precision` (type: `integer`):

Cell size of the coordinate search: 4 is about 39 km by 20 km, 6 about 1.2 km by 0.6 km, 8 about 38 m by 19 m. Advanced.

## `ring` (type: `integer`):

Cells read around the centre cell: 0 = centre only, 1 = 3x3 cells, up to 3.

## `searchUrls` (type: `array`):

Pasted apis.zigbang.com listing list URLs (.../house/property/v1/items/onerooms?geohash=wydm6\&depositMax=1000). When filled they define the search scope in search mode; price/size filters inside the URL are kept.

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

The source has no sort, so listings are ordered after reading them all: newest (registered date), price (deposit plus 100 times monthly rent), size (m2). Rows missing the value go last. maxItems then keeps the first N of the ordered list. none keeps the source order.

## `salesTypes` (type: `array`):

Narrow by deal type, one per entry. Valid values: jeonse (전세, lump-sum key-money lease), wolse (월세, deposit plus monthly rent), maede (매매, purchase), dongi (단기, short term). Leave empty for all types available on the chosen listing type. Note: oneroom listings have no sale (maede) type on the source. Unknown values fail the run loudly.

## `depositMin` (type: `integer`):

Lowest deposit (보증금) to include, in 10,000 KRW units.

## `depositMax` (type: `integer`):

Highest deposit (보증금) to include, in 10,000 KRW units.

## `rentMin` (type: `integer`):

Lowest monthly rent (월세) to include, in 10,000 KRW units.

## `rentMax` (type: `integer`):

Highest monthly rent (월세) to include, in 10,000 KRW units.

## `manageCostMin` (type: `integer`):

Lowest building maintenance fee (관리비) to include.

## `manageCostMax` (type: `integer`):

Highest building maintenance fee (관리비) to include.

## `sizeMin` (type: `number`):

Smallest floor size (전용면적) to include, in square meters.

## `sizeMax` (type: `number`):

Largest floor size (전용면적) to include, in square meters.

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

Fetch each listing's detail payload: full description, options (appliances, furniture), elevator, bathroom count, direction, parking, move-in date, nearby subway lines and the agent's public contact block. Adds one request per listing and bills the detail-enrichment surcharge per listing.

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

Stop after this many listing records. 0 means no limit: the run reads every listing in the scope.

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

The source serves every route from the standard datacenter connection pool; no residential proxy is needed. Bring your own proxy if your runs require one.

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

Paste a previous run ID or dataset ID to continue a large pull without returning items already collected there.

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

Remember the previous run of the same search and classify every listing as NEW, UPDATED, UNCHANGED or EXPIRED. Every field is compared (price, deposit, rent, management cost, size, floor, address, photos, tags, options and the rest) except viewCount, isNew and badges, which change without a real listing change and stay in the output but never trigger UPDATED. Unchanged listings are suppressed unless emitUnchanged is on.

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

Name the incremental baseline yourself when you run several searches that you want tracked separately but that share the same inputs. Leave empty to key the baseline on the search itself.

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

With incremental mode on, also return UNCHANGED rows. These are extra billed rows.

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

With incremental mode on, also return EXPIRED rows for listings from the previous baseline that no longer appear. These are extra billed rows. Only a complete, uncapped scan can mark listings expired.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names that must NOT make a listing count as UPDATED, for example "tags" or "agent.phone" (dotted paths reach nested fields). Ignored fields stay in the output and the saved baseline; they are only left out of the comparison and of changedFields. Leave empty to compare every field except viewCount, isNew and badges.

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

Connector ids to export results into (for example a Notion connector id). Optional; results are unaffected when export fails.

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

When a Notion connector is selected, pages are created under this parent page URL.

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

Cap how many of this run's records the connector export sends.

## Actor input object example

```json
{
  "mode": "search",
  "urls": [
    "https://m.zigbang.com/home/oneroom/items/48927059",
    "https://m.zigbang.com/home/villa/items/48927059",
    "https://m.zigbang.com/home/officetel/items/48927059",
    "https://m.zigbang.com/home/oneroom/items/48927059?foo=1",
    "https://apis.zigbang.com/v3/items/48927059",
    "https://apis.zigbang.com/v3/search?q=%EA%B0%95%EB%82%A8%EC%97%AD"
  ],
  "query": "강남역",
  "serviceType": "oneroom",
  "subwayRadiusKm": 2,
  "precision": 6,
  "ring": 1,
  "sortBy": "none",
  "fetchDetails": false,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `changes` (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 = {
    "urls": [
        "https://m.zigbang.com/home/oneroom/items/48927059",
        "https://m.zigbang.com/home/villa/items/48927059",
        "https://m.zigbang.com/home/officetel/items/48927059",
        "https://m.zigbang.com/home/oneroom/items/48927059?foo=1",
        "https://apis.zigbang.com/v3/items/48927059",
        "https://apis.zigbang.com/v3/search?q=%EA%B0%95%EB%82%A8%EC%97%AD"
    ],
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/zigbang-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 = {
    "urls": [
        "https://m.zigbang.com/home/oneroom/items/48927059",
        "https://m.zigbang.com/home/villa/items/48927059",
        "https://m.zigbang.com/home/officetel/items/48927059",
        "https://m.zigbang.com/home/oneroom/items/48927059?foo=1",
        "https://apis.zigbang.com/v3/items/48927059",
        "https://apis.zigbang.com/v3/search?q=%EA%B0%95%EB%82%A8%EC%97%AD",
    ],
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/zigbang-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 '{
  "urls": [
    "https://m.zigbang.com/home/oneroom/items/48927059",
    "https://m.zigbang.com/home/villa/items/48927059",
    "https://m.zigbang.com/home/officetel/items/48927059",
    "https://m.zigbang.com/home/oneroom/items/48927059?foo=1",
    "https://apis.zigbang.com/v3/items/48927059",
    "https://apis.zigbang.com/v3/search?q=%EA%B0%95%EB%82%A8%EC%97%AD"
  ],
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/zigbang-property-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/zigbang-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/8PyxWsaT5t3lMpEsN/builds/6xRp05vuz7HYup20g/openapi.json
