# 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)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 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 Property Scraper

Scrape Korea's largest home-finding marketplace: oneroom (원룸), villa (빌라) and
officetel (오피스텔) listings across Seoul and the rest of the country, with
deposits, monthly rents, floor info, addresses, subway access and public agent
contacts.

### Why this actor

- **Search by location**: resolve a subway station, neighbourhood or district
  (강남역, 홍대입구, 마포구...) and read every listing around it, with server-side
  deposit, rent, maintenance-fee, size and deal-type filters.
- **URL mode**: paste listing URLs or bare listing ids and read exactly those.
- **Detail enrichment**: an optional per-listing pass adds the full
  description, options (appliances and furniture), elevator, bathroom count,
  direction, parking, move-in date, nearby subway lines and the agent's
  public contact block.
- **Resume and incremental monitoring**: continue an interrupted pull, or
  schedule the same search daily and get only NEW / UPDATED / EXPIRED rows.
- **Structured output**: flat records with a stable field contract (see the
  Output tab views).

### Quick start

Search mode (default). The keyword resolves through the source's own location
search; when it names a subway station the actor reads everything within the
radius, otherwise the area around the resolved location:

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

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

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

URL mode: paste listing URLs or bare ids from any listing type:

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

URLs in this README are fictional examples shaped like the real ones.

### Deal types

| value | Korean | meaning |
|---|---|---|
| `jeonse` | 전세 | lump-sum key-money lease |
| `wolse` | 월세 | deposit plus monthly rent |
| `maede` | 매매 | purchase |
| `dongi` | 단기 | short term |

Oneroom listings on the source have no sale (매매) type; sale listings live in
the villa and officetel verticals. Apartment-complex (아파트) resale listings are
not part of this actor's scope today.

### Filters

The filter section (deal types, deposit, rent, maintenance fee, size) applies
in search mode only; a pasted listing is read as it is. All filter bounds are
optional and sent to the source server-side, so a filtered run reads exactly
the matching slice.

### Output

Every record carries the listing card: id, url, title, deal type, deposit,
rent, maintenance fee, sizes, floor, address parts, coordinates, registration
date, tags and thumbnail. With `fetchDetails` on, each record also carries the
description, options, elevator, bathroom count, direction, parking,
move-in date, view count, nearby subway lines and the agent block (office
name, public office phone, office address and registry number). Agent contact
data is public business contact information the source itself publishes on
every listing page.

Incremental runs add `changeType` (NEW / UPDATED / UNCHANGED / REAPPEARED /
EXPIRED), `changedFields`, `firstSeenAt` and `lastSeenAt`.

### Resume and recurring updates

- **Resume**: paste a previous `runId` or dataset id into `resumeFromRunId`;
  records already collected there are skipped.
- **Incremental**: set `incrementalMode` and schedule the same search; the
  actor remembers its own baseline (keyed on the search scope) and classifies
  every listing. Suppressed unchanged rows are not returned and not billed;
  `emitUnchanged` and `emitExpired` return (and bill) those extra rows.

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

Set `mcpConnectors` to one or more connector ids and the run's records are
exported through the Apify MCP proxy after the dataset is written (Notion
pages with `notionParentPageUrl`; at most `maxNotifyListings` records).
Export is best-effort: a failing connector never affects the scraped data.

### Notes and limits

- The source's robots.txt asks crawlers to stay out except named search
  engines; this actor reads only the public listing data the site's own web
  app serves to every visitor. Respect the data and any applicable terms in
  what you do with it.
- One search reads one location scope (a subway radius or one geohash cell,
  roughly 39 x 20 km). Split city-wide pulls into district-level searches.
- Listing cards come in batches; a run under a heavy network failure that
  cannot read any batch fails loudly instead of returning an empty success.

# 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 (https://www.zigbang.com/home/oneroom/items/12345678) or bare numeric listing ids. 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.

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

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

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

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

## Actor input object example

```json
{
  "mode": "search",
  "urls": [
    "https://www.zigbang.com/home/oneroom/items/48927059"
  ],
  "query": "강남역",
  "serviceType": "oneroom",
  "subwayRadiusKm": 2,
  "fetchDetails": false,
  "maxItems": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `detail` (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://www.zigbang.com/home/oneroom/items/48927059"
    ],
    "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://www.zigbang.com/home/oneroom/items/48927059"],
    "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://www.zigbang.com/home/oneroom/items/48927059"
  ],
  "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/7IBwMEFeSZ8mIBAYn/openapi.json
