# Korea 아파트 전월세 실거래가 API (Apartment Jeonse & Rent) (`linus009400/korea-rent-transactions-scraper`) Actor

Official Korean apartment jeonse and monthly-rent contracts from the Ministry of Land register. Real KRW, English labels, a stable complex ID and a geocodable road address on every row. Every renewal filing carries its previous deposit and rent.

- **URL**: https://apify.com/linus009400/korea-rent-transactions-scraper.md
- **Developed by:** [HEESEOK HONG](https://apify.com/linus009400) (community)
- **Categories:** Real estate, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 rental contracts

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

## Korea Apartment Jeonse & Rent Transactions Scraper — 아파트 전월세 실거래가 API

Every apartment rental contract filed with the Korean government, in real won, with English labels — **and the previous contract attached**, so you can see what the tenant actually paid before.

***

Every row carries `complexId` — the register's stable per-complex key — so you can build a time series for one building without matching on names that get re-spelled between filings, and join rentals against sale transactions for the same complex. Every row also carries a full road-name address, ready to geocode.

### What you get that a listing scrape cannot give you

Korean rental filings have carried the previous contract's terms since the 임대차 신고제 came into force. That turns a price list into a rent-increase dataset:

| Field | What it answers |
|---|---|
| `depositChangePct` | How much did this landlord raise the deposit on renewal? Present on **every** renewal filing. |
| `monthlyRentChangePct` | Same for monthly rent. |
| `jeonseConversionRatePct` | When a tenant swapped deposit for monthly rent, what annual rate did they get? This is the number compared against the legal cap of base rate + 2%p. |
| `usedRenewalRight` | Did the tenant exercise the statutory 계약갱신요구권? |
| `contractTermFrom` / `To` | When does this lease expire — i.e. what supply is coming back to market? |

Portal listings show asking prices. This is the filed, binding contract.

#### Field coverage — measured, not promised

Sampled on Gangnam-gu, June 2026 (n=100 filings):

| Field | Present on |
|---|---|
| Deposit, rent, area, floor, `complexId`, road address | **100% of rows** |
| `contractType` (new vs renewal) | **70% of rows** |
| Previous contract — `previousDepositKrw`, `depositChangePct` | **100% of renewal filings** (24 of 24) |
| `usedRenewalRight` affirmative | **46% of renewals** (11 of 24) |

A previous contract is structurally absent from a *new* lease — there is nothing to compare against. Every renewal in the sample carried its previous deposit and rent, so the increase and conversion figures are complete where they are meaningful rather than sparse everywhere.

Missing values are always `null`, never `0`, so a blank never silently drags an average down.

#### What one run tells you

That single 100-row sample already yields:

- **34%** of classified filings were renewals (24 of 70)
- **46%** of those renewals used the statutory 계약갱신요구권 (11 of 24)
- a per-contract deposit increase for every one of those 24

No survey, no panel, no estimation — these are filed contracts.

***

### Sample output

```json
{
  "propertyType": "APARTMENT",
  "leaseType": "MONTHLY_RENT",
  "sidoEn": "Seoul",
  "sggEn": "Gangnam-gu",
  "umdKr": "삼성동",
  "buildingName": "삼성동롯데아파트",
  "areaSqm": 59.4,
  "areaPyeong": 17.97,
  "floor": 2,
  "buildYear": 2000,

  "depositKrw": 300000000,
  "monthlyRentKrw": 2000000,
  "depositKrwPerPyeong": 16694915,

  "previousDepositKrw": 800000000,
  "previousMonthlyRentKrw": 0,
  "depositChangePct": -62.5,
  "jeonseConversionRatePct": 4.8,

  "contractType": "RENEWAL",
  "usedRenewalRight": null,
  "contractTermFrom": "2026-06",
  "contractTermTo": "2028-06",
  "reportedDate": "2026-06-04"
}
```

A pure jeonse contract has `monthlyRentKrw: 0` and `leaseType: "JEONSE"`.

***

### Who uses this

- **Proptech and listing platforms** — benchmark asking prices against filed contracts
- **Banks and insurers** — jeonse deposit exposure, LTV against actual deposit levels
- **Funds and REITs** — rental yield by district, built from contracts rather than surveys
- **Policy and press** — renewal-right take-up, conversion rates against the legal cap
- **Relocation and corporate housing** — what a given building actually rents for

***

### Input

| Field | Default | Notes |
|---|---|---|
| `regions` | `["Gangnam-gu"]` | English, Korean, or 5-digit 시군구 code. Mix freely. |
| `monthFrom` / `monthTo` | last completed month | `YYYY-MM` |
| `months` | — | Exact months; overrides the range |
| `leaseType` | `ANY` | `JEONSE` / `MONTHLY_RENT` |
| `contractType` | `ANY` | `NEW` / `RENEWAL` |
| `renewalRightOnly` | `false` | Only contracts where the renewal right was exercised |
| `minDepositKrw` / `maxDepositKrw` | — | **In won, not 만원** |
| `minAreaSqm` / `maxAreaSqm` | — | m² |
| `maxResults` | `500` | Hard cap on billed rows |
| `serviceKey` | shared key | Your own data.go.kr key for scheduled runs |

Running with defaults works — no configuration needed to see output.

***

### Two things that will bite you if you use the raw API

**1. Amounts are in 만원, not won.** `<deposit>50,000</deposit>` is ₩500,000,000, not ₩50,000. A foreign analyst reading the raw field is off by 10,000×. This Actor returns real won and keeps the original string in `raw`.

**2. A blank renewal-right field does not mean "not used".** It means "not reported" — common for small landlords and for older filings. Treating blanks as `false` silently understates take-up. This Actor returns `null` for blanks and `true` only for an affirmative filing.

***

### Notes

- Official government Open API only. No login, no scraping behind authentication.
- Rental contracts must be filed within 30 days, so the current month is always incomplete. The default is the last completed month.
- Rentals are dense: Gangnam-gu filed 1,921 apartment rentals in June 2026 against 223 sales. `maxResults` defaults to 500 so a first run cannot surprise you.
- A free data.go.kr key allows 10,000 requests per day and is approved automatically. Your existing key works — this API just needs its own 활용신청 on the portal.

***

### Coming next

Officetel (오피스텔), row house (연립다세대) and detached house (단독다가구) rentals come from three sibling registers and are already implemented here. They ship once their field names are confirmed against live responses rather than documentation — a rental row with a null building name is worse than no row at all.

***

### Related

- **Korea 아파트 실거래가 API** — apartment *sale* transactions from the same register
- **DART Korea 공시·재무제표 API** — Korean corporate filings and financial statements

# Actor input Schema

## `regions` (type: `array`):

English name (`Gangnam-gu`), Korean name (`강남구`, `서울 강남구`), or a 5-digit 시군구 code (`11680`). Mix freely. Cities that are divided into 구 must be given at 구 level — `Seongnam Bundang-gu`, not `성남시`.

## `monthFrom` (type: `string`):

`YYYY-MM`, e.g. `2026-01`. Defaults to the last completed month — rental contracts must be filed within 30 days, so the current month is always incomplete.

## `monthTo` (type: `string`):

`YYYY-MM`. Leave empty to fetch a single month.

## `months` (type: `array`):

Exact months to fetch, e.g. `2025-06`, `2026-06`. Overrides the range above — useful for year-on-year comparisons.

## `leaseType` (type: `string`):

`JEONSE` is the deposit-only Korean lease (no monthly rent). `MONTHLY_RENT` covers 월세 and 반전세, where a deposit and a monthly rent are both paid.

## `contractType` (type: `string`):

`RENEWAL` contracts carry the previous deposit and rent, which is what makes the increase and conversion-rate fields computable.

## `renewalRightOnly` (type: `boolean`):

Keeps only rows where the tenant exercised 계약갱신요구권. Blank filings are excluded, since a blank means 'not reported' rather than 'not used'.

## `minDepositKrw` (type: `integer`):

In actual won, not 만원. Five hundred million won is `500000000`.

## `maxDepositKrw` (type: `integer`):

In actual won, not 만원.

## `minAreaSqm` (type: `integer`):

Exclusive-use area, except for detached houses where the register reports contract area. 84 m² is the standard Korean family unit, roughly 25 pyeong.

## `maxAreaSqm` (type: `integer`):

Upper bound on area in square metres.

## `maxResults` (type: `integer`):

Hard cap on billed rows. Rental filings are far denser than sales — Gangnam-gu alone filed 1,921 apartment rentals in June 2026, against 223 sales. Raise this deliberately: the cap is what stops a wide query from billing more than you meant.

## `serviceKey` (type: `string`):

Leave empty to use the shared key. For scheduled or high-volume runs get your own — free and approved instantly at [data.go.kr](https://www.data.go.kr/data/15126474/openapi.do). Use the **Decoding** key, not the Encoding one. Limit is 10,000 requests per day.

## Actor input object example

```json
{
  "regions": [
    "Gangnam-gu",
    "Songpa-gu",
    "Seongnam Bundang-gu"
  ],
  "leaseType": "ANY",
  "contractType": "ANY",
  "renewalRightOnly": false,
  "maxResults": 500
}
```

# 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 = {
    "regions": [
        "Gangnam-gu",
        "Songpa-gu",
        "Seongnam Bundang-gu"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("linus009400/korea-rent-transactions-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 = { "regions": [
        "Gangnam-gu",
        "Songpa-gu",
        "Seongnam Bundang-gu",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("linus009400/korea-rent-transactions-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 '{
  "regions": [
    "Gangnam-gu",
    "Songpa-gu",
    "Seongnam Bundang-gu"
  ]
}' |
apify call linus009400/korea-rent-transactions-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,linus009400/korea-rent-transactions-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/LaI8oJb8UDt2JRqBk/builds/UjLz9VYZfyNkg9Vje/openapi.json
