# SUUMO Rental Property Scraper (Japan) (`scrapyx/suumo-properties-scraper`) Actor

Scrapes rental apartments from SUUMO — Japan's #1 property portal. Search 15 prefectures by ward, rent, layout, size and walk time to a station. One row per unit: rent, admin fee, deposit, key money, layout, floor, area, plus building address and stations.

- **URL**: https://apify.com/scrapyx/suumo-properties-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Real estate, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 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/actors/running/actors-in-store.md#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

## SUUMO Rental Property Scraper (Japan)

Scrapes rental apartments from **[SUUMO](https://suumo.jp)** — Japan's #1
property portal (Recruit). Tokyo alone advertises close to 2 million rental
listings.

Public data only. No login, no cookies, no browser.

### What you get

#### `ROOM` — one row per rentable unit

SUUMO nests rooms inside buildings: a listing page shows 30 buildings holding
roughly 265 actual units. The **unit** is the row, because that's what has a
price and a URL. Each row carries:

| Field | Example |
| --- | --- |
| `rent` | `13万円` |
| `adminFee` | `7000円` |
| `deposit` (敷金) | `6.5万円` |
| `keyMoney` (礼金) | `13万円` |
| `layout` (間取り) | `1DK` |
| `area` | `29.4m2` |
| `floor` | `3階` |
| `roomUrl` | direct SUUMO link |

…plus its building nested under `building`:

```json
"building": {
  "category": "賃貸マンション",
  "name": "マミービル",
  "address": "東京都新宿区高田馬場２",
  "stations": ["ＪＲ山手線/高田馬場駅 歩5分",
               "東京メトロ東西線/高田馬場駅 歩3分",
               "東京メトロ副都心線/西早稲田駅 歩7分"],
  "ageAndStructure": ["築7年", "5階建"]
}
```

Values are kept **exactly as SUUMO renders them** — `13万円` stays `13万円`.
No unit conversion, because guessing wrong between man-yen and yen would
corrupt the data silently. Parsing is left to you.

#### `SEARCH_SUMMARY` — one row per run

How many ads SUUMO advertises, how many buildings and rooms actually came
back, how deep the run paged, and which filters were sent.

### Input

| Field | What it does |
| --- | --- |
| **Prefecture** | 15 verified prefectures — Tokyo, Osaka, Kyoto, Aichi, Fukuoka, Hokkaido… |
| **City / ward codes** | optional `sc` codes, e.g. `13104` Shinjuku, `13113` Shibuya |
| **Min / max rent** | in 万円 (man-yen): `8` = ¥80,000/month, `8.5` = ¥85,000 |
| **Layout** | multi-select: 1R, 1K, 1DK, 1LDK, 2K … 4LDK+ |
| **Min / max area** | m² |
| **Max walk to station** | minutes |
| **Max rooms** | `0` = unlimited |

#### Example

```json
{
  "prefecture": "tokyo",
  "cityCodes": ["13104"],
  "minRent": "8",
  "maxRent": "15",
  "layouts": ["02", "03"],
  "maxWalkMinutes": 10,
  "maxItems": 500
}
```

To find a ward code, run the search on suumo.jp and read `sc=` out of the URL.

### What this actor protects you from

SUUMO **never returns an error** for a bad input — every mistake comes back as
a healthy HTTP 200. Four of those were found and are now handled:

1. **Integer rent silently returns nothing.** `cb=8` gives an empty page;
   `cb=8.0` gives 116,149 results. Whatever you type is normalised to the
   decimal form SUUMO requires — otherwise "8 to 15 man-yen" quietly looks
   like "no apartments match your budget."
2. **A ward code from the wrong prefecture is silently dropped**, and SUUMO
   returns the *entire* prefecture instead. Because `sc` values are JIS
   municipal codes prefixed with the prefecture number, this is checked
   arithmetically and refused before the run.
3. **Region/prefecture code pairs must agree** or the page comes back empty.
   Only pairs verified to return listings are offered.
4. **Unknown filters are ignored, not rejected.** Every filter here was
   verified to actually change the result count.

And when page 1 legitimately comes back empty, the run says so explicitly
rather than reporting a successful crawl of nothing.

### Notes on reliability

- **No WAF, no challenge.** Plain server-rendered HTML.
- **SUUMO does rate-limit**, answering bursts with HTTP 503. This actor paces
  itself instead of racing: concurrency defaults to **2**, with a polite delay
  between pages and a long backoff on 503. Raising concurrency makes runs
  slower, not faster, once throttling starts.
- **Runs unattended.** No session, no token, nothing to refresh.
- **`advertisedHits` is not the number of rows you can get.** SUUMO counts
  every agency ad (120,284 for Shinjuku) but collapses duplicates into
  buildings. Both numbers are reported so the gap is visible.

### Scope

**Rentals only** (`chintai`). For-sale mansions, houses and land use a
different page structure and are deliberately out of scope rather than
half-supported.

There is also **no detail-fetch toggle** — unlike the other property actors
here, SUUMO's listing page already carries every field the room detail page
repeats, so a detail pass would cost ~9× the requests to re-fetch what you
already have.

See [`CRAWLING_METHOD.md`](CRAWLING_METHOD.md) for the full reverse-engineering
trail, the region-code sweep and all four trap write-ups.

# Actor input Schema

## `prefecture` (type: `string`):

Which prefecture to search. Only pairs verified to return listings are offered — SUUMO pairs a region code with a prefecture code internally and answers a mismatched pair with an EMPTY page rather than an error, so unverified combinations are refused instead of silently returning nothing.

## `cityCodes` (type: `array`):

Optional SUUMO `sc` codes to narrow to specific wards or cities, e.g. 13104 = Shinjuku, 13113 = Shibuya, 13101 = Chiyoda. Leave empty for the whole prefecture. To find a code, run the search on suumo.jp and read `sc=` out of the URL. Codes must belong to the chosen prefecture — SUUMO returns an empty page rather than an error if they do not.

## `minRent` (type: `string`):

In man-yen (units of 10,000 JPY), as SUUMO expresses it: 8 means 80,000 yen, 8.5 means 85,000 yen. Leave empty for no minimum.

## `maxRent` (type: `string`):

In man-yen (units of 10,000 JPY): 15 means 150,000 yen. Leave empty for no maximum.

## `layouts` (type: `array`):

Select one or more floor-plan types. Leave empty for any.

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

Leave empty or 0 for any.

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

Leave empty or 0 for any.

## `maxWalkMinutes` (type: `integer`):

Only return properties within this many minutes' walk of a station. Leave empty or 0 for any.

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

Stop after this many rentable units. Set to 0 for unlimited. Note one page of 30 buildings carries roughly 265 units, so this fills up faster than a per-listing count would suggest.

## `maxConcurrency` (type: `integer`):

Deliberately low by default. SUUMO answers bursts of requests with HTTP 503, so this actor paces itself rather than racing; raising this makes runs slower, not faster, once throttling kicks in.

## `proxyConfiguration` (type: `object`):

SUUMO has no bot challenge, but it does rate-limit by IP — Residential lets a long crawl rotate exits rather than backing off on one address.

## Actor input object example

```json
{
  "prefecture": "tokyo",
  "cityCodes": [],
  "minRent": "",
  "maxRent": "",
  "layouts": [],
  "minArea": 0,
  "maxArea": 0,
  "maxWalkMinutes": 0,
  "maxItems": 100,
  "maxConcurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/suumo-properties-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/suumo-properties-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 '{}' |
apify call scrapyx/suumo-properties-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/suumo-properties-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/agXb7GpBmg2BdAQh9/builds/hv1rXAC8ogkfwGOqT/openapi.json
