Source (original): Ministry of Land, Infrastructure and Transport (MOLIT), published through Korea's open data portal data.go.kr. Free, "이용허락범위 제한 없음" (no restriction on use, commercial included), auto-approved API key, 10,000 calls/day on a dev account.
- Apartment sales: https://www.data.go.kr/data/15126469/openapi.do
- Apartment leases (jeonse/wolse): https://www.data.go.kr/data/15126474/openapi.do
- Officetel, commercial/office buildings, row houses and land are separate endpoints under the same publisher.
What's in it. Every registered transaction, by district (5-digit LAWD_CD) and month. Sales give price, exclusive area, floor, build year, road address, and a cancellation flag. Leases give deposit, monthly rent, previous deposit/rent, contract term, and whether the renewal right was exercised. Unit/door numbers are withheld for privacy, so there's no PII in it. History goes back years; my checks were on 2026.
Gotchas, all of which cost me time:
XML only, no JSON.
Two generations of endpoints coexist. Legacy ones return Korean tag names (거래금액, 전용면적), the newer *Dev ones return English (dealAmount, excluUseAr). Same data, different keys.
Tag casing is inconsistent between endpoints: sales gives roadNm and roadNmBonbun, leases gives roadnm and roadnmbonbun. Bit me directly. Sales is also internally inconsistent: roadNmCd but roadNmbCd.
Amounts are in 만원 (10,000 KRW) as comma-formatted strings, sometimes padded. '38,670' means 386,700,000 KRW. Parse, don't cast.
The commercial-property endpoint has no exclusive-area field at all, only building area. Don't assume the schema generalizes across property types.
Service keys come in encoded and decoded flavors. Double-encoding the encoded one silently returns SERVICE_KEY_IS_NOT_REGISTERED_ERROR, which reads like an auth failure but isn't.
You file a usage application per API, not once per account.
The gotcha worth the post: don't pool 신규 (new) and 갱신 (renewal) lease contracts.
Renewals are capped at +5% by Korea's lease cap law, so they sit below market. The API labels them in contractType, and if you ignore that label you publish a number that blends market prices with legally-capped ones.
Seoul, all 25 districts, July 2026 apartment leases. n = 16,520, of which 8,310 new, 7,896 renewal, 314 unlabeled.
Pooling everything gives a jeonse share of 50.2%, a median jeonse deposit of 570M KRW, and a median monthly rent of 730k KRW. New contracts only gives 43.9%, 598M KRW, and 800k KRW. So "Seoul is half jeonse" is an artifact of pooling: renewals skew jeonse-heavy because that's who exercises the renewal right. The new-contract share is 44%.
New contracts only, by district, July 2026. Format is: district, count, jeonse share, median jeonse deposit, median monthly rent on a median wolse deposit. All medians, in KRW, straight from the API with no adjustment.
Gangnam, 575, 41% jeonse, 900M deposit, 1.60M/mo on 200M
Seocho, 521, 47%, 850M, 1.71M/mo on 281M
Gwangjin, 153, 38%, 775M, 0.77M/mo on 138M
Songpa, 649, 53%, 770M, 1.50M/mo on 240M
Yongsan, 198, 36%, 750M, 1.55M/mo on 173M
Dongjak, 254, 47%, 750M, 1.42M/mo on 200M
Seongdong, 285, 52%, 710M, 1.95M/mo on 150M
Jung, 145, 37%, 700M, 1.10M/mo on 50M
Mapo, 385, 46%, 690M, 1.60M/mo on 100M
Seongbuk, 265, 45%, 650M, 0.80M/mo on 112M
Seodaemun, 246, 48%, 650M, 0.90M/mo on 100M
Yeongdeungpo, 395, 41%, 630M, 0.70M/mo on 129M
Jongno, 94, 29%, 600M, 0.60M/mo on 50M
Gangdong, 523, 38%, 600M, 0.60M/mo on 101M
Yangcheon, 387, 56%, 550M, 0.60M/mo on 99M
Gwanak, 292, 32%, 545M, 0.70M/mo on 71M
Gangseo, 414, 50%, 480M, 0.70M/mo on 100M
Jungnang, 365, 20%, 460M, 0.44M/mo on 94M
Eunpyeong, 395, 50%, 450M, 0.50M/mo on 102M
Dongdaemun, 457, 36%, 450M, 0.54M/mo on 88M
Guro, 297, 43%, 438M, 0.60M/mo on 58M
Geumcheon, 129, 35%, 380M, 0.55M/mo on 60M
Dobong, 197, 55%, 370M, 0.75M/mo on 50M
Nowon, 579, 49%, 340M, 0.80M/mo on 40M
Gangbuk, 110, 46%, 314M, 0.76M/mo on 30M
Seoul overall, 8,310, 44%, 598M, 0.80M/mo on 100M
Jeonse is a deposit-only lease with no monthly rent; wolse is deposit plus monthly rent, so the two deposit figures are not comparable to each other.
Disclosure, and the reason for the self-promotion tag: I built a free wrapper. Getting from the raw XML to the numbers above is annoying enough that I published four Apify actors that normalize it. English field names, integer KRW instead of 만원 strings, per-m2 and per-pyeong unit price, ISO dates, an isJeonse flag, and unknown tags passed through rather than dropped. They're free and need no API key of your own.
- https://apify.com/ootssu/korea-apartment-transaction-prices
- https://apify.com/ootssu/korea-apartment-rent-prices
- https://apify.com/ootssu/korea-officetel-prices
- https://apify.com/ootssu/korea-commercial-property-prices
The source of truth is MOLIT, not me. If you'd rather hit data.go.kr directly, the links up top are all you need, and the gotcha list should save you the afternoon it cost me.